roojs-debug.js
[roojs1] / roojs-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         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * 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.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * 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]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     }
847 });/*
848  * Based on:
849  * Ext JS Library 1.1.1
850  * Copyright(c) 2006-2007, Ext JS, LLC.
851  *
852  * Originally Released Under LGPL - original licence link has changed is not relivant.
853  *
854  * Fork - LGPL
855  * <script type="text/javascript">
856  */
857
858 /**
859  * @class Date
860  *
861  * The date parsing and format syntax is a subset of
862  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
863  * supported will provide results equivalent to their PHP versions.
864  *
865  * Following is the list of all currently supported formats:
866  *<pre>
867 Sample date:
868 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
869
870 Format  Output      Description
871 ------  ----------  --------------------------------------------------------------
872   d      10         Day of the month, 2 digits with leading zeros
873   D      Wed        A textual representation of a day, three letters
874   j      10         Day of the month without leading zeros
875   l      Wednesday  A full textual representation of the day of the week
876   S      th         English ordinal day of month suffix, 2 chars (use with j)
877   w      3          Numeric representation of the day of the week
878   z      9          The julian date, or day of the year (0-365)
879   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
880   F      January    A full textual representation of the month
881   m      01         Numeric representation of a month, with leading zeros
882   M      Jan        Month name abbreviation, three letters
883   n      1          Numeric representation of a month, without leading zeros
884   t      31         Number of days in the given month
885   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
886   Y      2007       A full numeric representation of a year, 4 digits
887   y      07         A two digit representation of a year
888   a      pm         Lowercase Ante meridiem and Post meridiem
889   A      PM         Uppercase Ante meridiem and Post meridiem
890   g      3          12-hour format of an hour without leading zeros
891   G      15         24-hour format of an hour without leading zeros
892   h      03         12-hour format of an hour with leading zeros
893   H      15         24-hour format of an hour with leading zeros
894   i      05         Minutes with leading zeros
895   s      01         Seconds, with leading zeros
896   O      -0600      Difference to Greenwich time (GMT) in hours
897   T      CST        Timezone setting of the machine running the code
898   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
899 </pre>
900  *
901  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
902  * <pre><code>
903 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
904 document.write(dt.format('Y-m-d'));                         //2007-01-10
905 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
906 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
907  </code></pre>
908  *
909  * Here are some standard date/time patterns that you might find helpful.  They
910  * are not part of the source of Date.js, but to use them you can simply copy this
911  * block of code into any script that is included after Date.js and they will also become
912  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
913  * <pre><code>
914 Date.patterns = {
915     ISO8601Long:"Y-m-d H:i:s",
916     ISO8601Short:"Y-m-d",
917     ShortDate: "n/j/Y",
918     LongDate: "l, F d, Y",
919     FullDateTime: "l, F d, Y g:i:s A",
920     MonthDay: "F d",
921     ShortTime: "g:i A",
922     LongTime: "g:i:s A",
923     SortableDateTime: "Y-m-d\\TH:i:s",
924     UniversalSortableDateTime: "Y-m-d H:i:sO",
925     YearMonth: "F, Y"
926 };
927 </code></pre>
928  *
929  * Example usage:
930  * <pre><code>
931 var dt = new Date();
932 document.write(dt.format(Date.patterns.ShortDate));
933  </code></pre>
934  */
935
936 /*
937  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
938  * They generate precompiled functions from date formats instead of parsing and
939  * processing the pattern every time you format a date.  These functions are available
940  * on every Date object (any javascript function).
941  *
942  * The original article and download are here:
943  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
944  *
945  */
946  
947  
948  // was in core
949 /**
950  Returns the number of milliseconds between this date and date
951  @param {Date} date (optional) Defaults to now
952  @return {Number} The diff in milliseconds
953  @member Date getElapsed
954  */
955 Date.prototype.getElapsed = function(date) {
956         return Math.abs((date || new Date()).getTime()-this.getTime());
957 };
958 // was in date file..
959
960
961 // private
962 Date.parseFunctions = {count:0};
963 // private
964 Date.parseRegexes = [];
965 // private
966 Date.formatFunctions = {count:0};
967
968 // private
969 Date.prototype.dateFormat = function(format) {
970     if (Date.formatFunctions[format] == null) {
971         Date.createNewFormat(format);
972     }
973     var func = Date.formatFunctions[format];
974     return this[func]();
975 };
976
977
978 /**
979  * Formats a date given the supplied format string
980  * @param {String} format The format string
981  * @return {String} The formatted date
982  * @method
983  */
984 Date.prototype.format = Date.prototype.dateFormat;
985
986 // private
987 Date.createNewFormat = function(format) {
988     var funcName = "format" + Date.formatFunctions.count++;
989     Date.formatFunctions[format] = funcName;
990     var code = "Date.prototype." + funcName + " = function(){return ";
991     var special = false;
992     var ch = '';
993     for (var i = 0; i < format.length; ++i) {
994         ch = format.charAt(i);
995         if (!special && ch == "\\") {
996             special = true;
997         }
998         else if (special) {
999             special = false;
1000             code += "'" + String.escape(ch) + "' + ";
1001         }
1002         else {
1003             code += Date.getFormatCode(ch);
1004         }
1005     }
1006     /** eval:var:zzzzzzzzzzzzz */
1007     eval(code.substring(0, code.length - 3) + ";}");
1008 };
1009
1010 // private
1011 Date.getFormatCode = function(character) {
1012     switch (character) {
1013     case "d":
1014         return "String.leftPad(this.getDate(), 2, '0') + ";
1015     case "D":
1016         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1017     case "j":
1018         return "this.getDate() + ";
1019     case "l":
1020         return "Date.dayNames[this.getDay()] + ";
1021     case "S":
1022         return "this.getSuffix() + ";
1023     case "w":
1024         return "this.getDay() + ";
1025     case "z":
1026         return "this.getDayOfYear() + ";
1027     case "W":
1028         return "this.getWeekOfYear() + ";
1029     case "F":
1030         return "Date.monthNames[this.getMonth()] + ";
1031     case "m":
1032         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1033     case "M":
1034         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1035     case "n":
1036         return "(this.getMonth() + 1) + ";
1037     case "t":
1038         return "this.getDaysInMonth() + ";
1039     case "L":
1040         return "(this.isLeapYear() ? 1 : 0) + ";
1041     case "Y":
1042         return "this.getFullYear() + ";
1043     case "y":
1044         return "('' + this.getFullYear()).substring(2, 4) + ";
1045     case "a":
1046         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1047     case "A":
1048         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1049     case "g":
1050         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1051     case "G":
1052         return "this.getHours() + ";
1053     case "h":
1054         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1055     case "H":
1056         return "String.leftPad(this.getHours(), 2, '0') + ";
1057     case "i":
1058         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1059     case "s":
1060         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1061     case "O":
1062         return "this.getGMTOffset() + ";
1063     case "T":
1064         return "this.getTimezone() + ";
1065     case "Z":
1066         return "(this.getTimezoneOffset() * -60) + ";
1067     default:
1068         return "'" + String.escape(character) + "' + ";
1069     }
1070 };
1071
1072 /**
1073  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1074  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1075  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1076  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1077  * string or the parse operation will fail.
1078  * Example Usage:
1079 <pre><code>
1080 //dt = Fri May 25 2007 (current date)
1081 var dt = new Date();
1082
1083 //dt = Thu May 25 2006 (today's month/day in 2006)
1084 dt = Date.parseDate("2006", "Y");
1085
1086 //dt = Sun Jan 15 2006 (all date parts specified)
1087 dt = Date.parseDate("2006-1-15", "Y-m-d");
1088
1089 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1090 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1091 </code></pre>
1092  * @param {String} input The unparsed date as a string
1093  * @param {String} format The format the date is in
1094  * @return {Date} The parsed date
1095  * @static
1096  */
1097 Date.parseDate = function(input, format) {
1098     if (Date.parseFunctions[format] == null) {
1099         Date.createParser(format);
1100     }
1101     var func = Date.parseFunctions[format];
1102     return Date[func](input);
1103 };
1104
1105 // private
1106 Date.createParser = function(format) {
1107     var funcName = "parse" + Date.parseFunctions.count++;
1108     var regexNum = Date.parseRegexes.length;
1109     var currentGroup = 1;
1110     Date.parseFunctions[format] = funcName;
1111
1112     var code = "Date." + funcName + " = function(input){\n"
1113         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1114         + "var d = new Date();\n"
1115         + "y = d.getFullYear();\n"
1116         + "m = d.getMonth();\n"
1117         + "d = d.getDate();\n"
1118         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1119         + "if (results && results.length > 0) {";
1120     var regex = "";
1121
1122     var special = false;
1123     var ch = '';
1124     for (var i = 0; i < format.length; ++i) {
1125         ch = format.charAt(i);
1126         if (!special && ch == "\\") {
1127             special = true;
1128         }
1129         else if (special) {
1130             special = false;
1131             regex += String.escape(ch);
1132         }
1133         else {
1134             var obj = Date.formatCodeToRegex(ch, currentGroup);
1135             currentGroup += obj.g;
1136             regex += obj.s;
1137             if (obj.g && obj.c) {
1138                 code += obj.c;
1139             }
1140         }
1141     }
1142
1143     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1144         + "{v = new Date(y, m, d, h, i, s);}\n"
1145         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1146         + "{v = new Date(y, m, d, h, i);}\n"
1147         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1148         + "{v = new Date(y, m, d, h);}\n"
1149         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1150         + "{v = new Date(y, m, d);}\n"
1151         + "else if (y >= 0 && m >= 0)\n"
1152         + "{v = new Date(y, m);}\n"
1153         + "else if (y >= 0)\n"
1154         + "{v = new Date(y);}\n"
1155         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1156         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1157         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1158         + ";}";
1159
1160     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1161      /** eval:var:zzzzzzzzzzzzz */
1162     eval(code);
1163 };
1164
1165 // private
1166 Date.formatCodeToRegex = function(character, currentGroup) {
1167     switch (character) {
1168     case "D":
1169         return {g:0,
1170         c:null,
1171         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1172     case "j":
1173         return {g:1,
1174             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1175             s:"(\\d{1,2})"}; // day of month without leading zeroes
1176     case "d":
1177         return {g:1,
1178             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1179             s:"(\\d{2})"}; // day of month with leading zeroes
1180     case "l":
1181         return {g:0,
1182             c:null,
1183             s:"(?:" + Date.dayNames.join("|") + ")"};
1184     case "S":
1185         return {g:0,
1186             c:null,
1187             s:"(?:st|nd|rd|th)"};
1188     case "w":
1189         return {g:0,
1190             c:null,
1191             s:"\\d"};
1192     case "z":
1193         return {g:0,
1194             c:null,
1195             s:"(?:\\d{1,3})"};
1196     case "W":
1197         return {g:0,
1198             c:null,
1199             s:"(?:\\d{2})"};
1200     case "F":
1201         return {g:1,
1202             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1203             s:"(" + Date.monthNames.join("|") + ")"};
1204     case "M":
1205         return {g:1,
1206             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1207             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1208     case "n":
1209         return {g:1,
1210             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1211             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1212     case "m":
1213         return {g:1,
1214             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1215             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1216     case "t":
1217         return {g:0,
1218             c:null,
1219             s:"\\d{1,2}"};
1220     case "L":
1221         return {g:0,
1222             c:null,
1223             s:"(?:1|0)"};
1224     case "Y":
1225         return {g:1,
1226             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1227             s:"(\\d{4})"};
1228     case "y":
1229         return {g:1,
1230             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1231                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1232             s:"(\\d{1,2})"};
1233     case "a":
1234         return {g:1,
1235             c:"if (results[" + currentGroup + "] == 'am') {\n"
1236                 + "if (h == 12) { h = 0; }\n"
1237                 + "} else { if (h < 12) { h += 12; }}",
1238             s:"(am|pm)"};
1239     case "A":
1240         return {g:1,
1241             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1242                 + "if (h == 12) { h = 0; }\n"
1243                 + "} else { if (h < 12) { h += 12; }}",
1244             s:"(AM|PM)"};
1245     case "g":
1246     case "G":
1247         return {g:1,
1248             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1249             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1250     case "h":
1251     case "H":
1252         return {g:1,
1253             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1254             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1255     case "i":
1256         return {g:1,
1257             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1258             s:"(\\d{2})"};
1259     case "s":
1260         return {g:1,
1261             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1262             s:"(\\d{2})"};
1263     case "O":
1264         return {g:1,
1265             c:[
1266                 "o = results[", currentGroup, "];\n",
1267                 "var sn = o.substring(0,1);\n", // get + / - sign
1268                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1269                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1270                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1271                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1272             ].join(""),
1273             s:"([+\-]\\d{4})"};
1274     case "T":
1275         return {g:0,
1276             c:null,
1277             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1278     case "Z":
1279         return {g:1,
1280             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1281                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1282             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1283     default:
1284         return {g:0,
1285             c:null,
1286             s:String.escape(character)};
1287     }
1288 };
1289
1290 /**
1291  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1292  * @return {String} The abbreviated timezone name (e.g. 'CST')
1293  */
1294 Date.prototype.getTimezone = function() {
1295     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1296 };
1297
1298 /**
1299  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1300  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1301  */
1302 Date.prototype.getGMTOffset = function() {
1303     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1304         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1305         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1306 };
1307
1308 /**
1309  * Get the numeric day number of the year, adjusted for leap year.
1310  * @return {Number} 0 through 364 (365 in leap years)
1311  */
1312 Date.prototype.getDayOfYear = function() {
1313     var num = 0;
1314     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1315     for (var i = 0; i < this.getMonth(); ++i) {
1316         num += Date.daysInMonth[i];
1317     }
1318     return num + this.getDate() - 1;
1319 };
1320
1321 /**
1322  * Get the string representation of the numeric week number of the year
1323  * (equivalent to the format specifier 'W').
1324  * @return {String} '00' through '52'
1325  */
1326 Date.prototype.getWeekOfYear = function() {
1327     // Skip to Thursday of this week
1328     var now = this.getDayOfYear() + (4 - this.getDay());
1329     // Find the first Thursday of the year
1330     var jan1 = new Date(this.getFullYear(), 0, 1);
1331     var then = (7 - jan1.getDay() + 4);
1332     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1333 };
1334
1335 /**
1336  * Whether or not the current date is in a leap year.
1337  * @return {Boolean} True if the current date is in a leap year, else false
1338  */
1339 Date.prototype.isLeapYear = function() {
1340     var year = this.getFullYear();
1341     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1342 };
1343
1344 /**
1345  * Get the first day of the current month, adjusted for leap year.  The returned value
1346  * is the numeric day index within the week (0-6) which can be used in conjunction with
1347  * the {@link #monthNames} array to retrieve the textual day name.
1348  * Example:
1349  *<pre><code>
1350 var dt = new Date('1/10/2007');
1351 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1352 </code></pre>
1353  * @return {Number} The day number (0-6)
1354  */
1355 Date.prototype.getFirstDayOfMonth = function() {
1356     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1357     return (day < 0) ? (day + 7) : day;
1358 };
1359
1360 /**
1361  * Get the last day of the current month, adjusted for leap year.  The returned value
1362  * is the numeric day index within the week (0-6) which can be used in conjunction with
1363  * the {@link #monthNames} array to retrieve the textual day name.
1364  * Example:
1365  *<pre><code>
1366 var dt = new Date('1/10/2007');
1367 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1368 </code></pre>
1369  * @return {Number} The day number (0-6)
1370  */
1371 Date.prototype.getLastDayOfMonth = function() {
1372     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1373     return (day < 0) ? (day + 7) : day;
1374 };
1375
1376
1377 /**
1378  * Get the first date of this date's month
1379  * @return {Date}
1380  */
1381 Date.prototype.getFirstDateOfMonth = function() {
1382     return new Date(this.getFullYear(), this.getMonth(), 1);
1383 };
1384
1385 /**
1386  * Get the last date of this date's month
1387  * @return {Date}
1388  */
1389 Date.prototype.getLastDateOfMonth = function() {
1390     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1391 };
1392 /**
1393  * Get the number of days in the current month, adjusted for leap year.
1394  * @return {Number} The number of days in the month
1395  */
1396 Date.prototype.getDaysInMonth = function() {
1397     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1398     return Date.daysInMonth[this.getMonth()];
1399 };
1400
1401 /**
1402  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1403  * @return {String} 'st, 'nd', 'rd' or 'th'
1404  */
1405 Date.prototype.getSuffix = function() {
1406     switch (this.getDate()) {
1407         case 1:
1408         case 21:
1409         case 31:
1410             return "st";
1411         case 2:
1412         case 22:
1413             return "nd";
1414         case 3:
1415         case 23:
1416             return "rd";
1417         default:
1418             return "th";
1419     }
1420 };
1421
1422 // private
1423 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1424
1425 /**
1426  * An array of textual month names.
1427  * Override these values for international dates, for example...
1428  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1429  * @type Array
1430  * @static
1431  */
1432 Date.monthNames =
1433    ["January",
1434     "February",
1435     "March",
1436     "April",
1437     "May",
1438     "June",
1439     "July",
1440     "August",
1441     "September",
1442     "October",
1443     "November",
1444     "December"];
1445
1446 /**
1447  * An array of textual day names.
1448  * Override these values for international dates, for example...
1449  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1450  * @type Array
1451  * @static
1452  */
1453 Date.dayNames =
1454    ["Sunday",
1455     "Monday",
1456     "Tuesday",
1457     "Wednesday",
1458     "Thursday",
1459     "Friday",
1460     "Saturday"];
1461
1462 // private
1463 Date.y2kYear = 50;
1464 // private
1465 Date.monthNumbers = {
1466     Jan:0,
1467     Feb:1,
1468     Mar:2,
1469     Apr:3,
1470     May:4,
1471     Jun:5,
1472     Jul:6,
1473     Aug:7,
1474     Sep:8,
1475     Oct:9,
1476     Nov:10,
1477     Dec:11};
1478
1479 /**
1480  * Creates and returns a new Date instance with the exact same date value as the called instance.
1481  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1482  * variable will also be changed.  When the intention is to create a new variable that will not
1483  * modify the original instance, you should create a clone.
1484  *
1485  * Example of correctly cloning a date:
1486  * <pre><code>
1487 //wrong way:
1488 var orig = new Date('10/1/2006');
1489 var copy = orig;
1490 copy.setDate(5);
1491 document.write(orig);  //returns 'Thu Oct 05 2006'!
1492
1493 //correct way:
1494 var orig = new Date('10/1/2006');
1495 var copy = orig.clone();
1496 copy.setDate(5);
1497 document.write(orig);  //returns 'Thu Oct 01 2006'
1498 </code></pre>
1499  * @return {Date} The new Date instance
1500  */
1501 Date.prototype.clone = function() {
1502         return new Date(this.getTime());
1503 };
1504
1505 /**
1506  * Clears any time information from this date
1507  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1508  @return {Date} this or the clone
1509  */
1510 Date.prototype.clearTime = function(clone){
1511     if(clone){
1512         return this.clone().clearTime();
1513     }
1514     this.setHours(0);
1515     this.setMinutes(0);
1516     this.setSeconds(0);
1517     this.setMilliseconds(0);
1518     return this;
1519 };
1520
1521 // private
1522 // safari setMonth is broken
1523 if(Roo.isSafari){
1524     Date.brokenSetMonth = Date.prototype.setMonth;
1525         Date.prototype.setMonth = function(num){
1526                 if(num <= -1){
1527                         var n = Math.ceil(-num);
1528                         var back_year = Math.ceil(n/12);
1529                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1530                         this.setFullYear(this.getFullYear() - back_year);
1531                         return Date.brokenSetMonth.call(this, month);
1532                 } else {
1533                         return Date.brokenSetMonth.apply(this, arguments);
1534                 }
1535         };
1536 }
1537
1538 /** Date interval constant 
1539 * @static 
1540 * @type String */
1541 Date.MILLI = "ms";
1542 /** Date interval constant 
1543 * @static 
1544 * @type String */
1545 Date.SECOND = "s";
1546 /** Date interval constant 
1547 * @static 
1548 * @type String */
1549 Date.MINUTE = "mi";
1550 /** Date interval constant 
1551 * @static 
1552 * @type String */
1553 Date.HOUR = "h";
1554 /** Date interval constant 
1555 * @static 
1556 * @type String */
1557 Date.DAY = "d";
1558 /** Date interval constant 
1559 * @static 
1560 * @type String */
1561 Date.MONTH = "mo";
1562 /** Date interval constant 
1563 * @static 
1564 * @type String */
1565 Date.YEAR = "y";
1566
1567 /**
1568  * Provides a convenient method of performing basic date arithmetic.  This method
1569  * does not modify the Date instance being called - it creates and returns
1570  * a new Date instance containing the resulting date value.
1571  *
1572  * Examples:
1573  * <pre><code>
1574 //Basic usage:
1575 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1576 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1577
1578 //Negative values will subtract correctly:
1579 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1580 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1581
1582 //You can even chain several calls together in one line!
1583 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1584 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1585  </code></pre>
1586  *
1587  * @param {String} interval   A valid date interval enum value
1588  * @param {Number} value      The amount to add to the current date
1589  * @return {Date} The new Date instance
1590  */
1591 Date.prototype.add = function(interval, value){
1592   var d = this.clone();
1593   if (!interval || value === 0) return d;
1594   switch(interval.toLowerCase()){
1595     case Date.MILLI:
1596       d.setMilliseconds(this.getMilliseconds() + value);
1597       break;
1598     case Date.SECOND:
1599       d.setSeconds(this.getSeconds() + value);
1600       break;
1601     case Date.MINUTE:
1602       d.setMinutes(this.getMinutes() + value);
1603       break;
1604     case Date.HOUR:
1605       d.setHours(this.getHours() + value);
1606       break;
1607     case Date.DAY:
1608       d.setDate(this.getDate() + value);
1609       break;
1610     case Date.MONTH:
1611       var day = this.getDate();
1612       if(day > 28){
1613           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1614       }
1615       d.setDate(day);
1616       d.setMonth(this.getMonth() + value);
1617       break;
1618     case Date.YEAR:
1619       d.setFullYear(this.getFullYear() + value);
1620       break;
1621   }
1622   return d;
1623 };/*
1624  * Based on:
1625  * Ext JS Library 1.1.1
1626  * Copyright(c) 2006-2007, Ext JS, LLC.
1627  *
1628  * Originally Released Under LGPL - original licence link has changed is not relivant.
1629  *
1630  * Fork - LGPL
1631  * <script type="text/javascript">
1632  */
1633
1634 Roo.lib.Dom = {
1635     getViewWidth : function(full) {
1636         return full ? this.getDocumentWidth() : this.getViewportWidth();
1637     },
1638
1639     getViewHeight : function(full) {
1640         return full ? this.getDocumentHeight() : this.getViewportHeight();
1641     },
1642
1643     getDocumentHeight: function() {
1644         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1645         return Math.max(scrollHeight, this.getViewportHeight());
1646     },
1647
1648     getDocumentWidth: function() {
1649         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1650         return Math.max(scrollWidth, this.getViewportWidth());
1651     },
1652
1653     getViewportHeight: function() {
1654         var height = self.innerHeight;
1655         var mode = document.compatMode;
1656
1657         if ((mode || Roo.isIE) && !Roo.isOpera) {
1658             height = (mode == "CSS1Compat") ?
1659                      document.documentElement.clientHeight :
1660                      document.body.clientHeight;
1661         }
1662
1663         return height;
1664     },
1665
1666     getViewportWidth: function() {
1667         var width = self.innerWidth;
1668         var mode = document.compatMode;
1669
1670         if (mode || Roo.isIE) {
1671             width = (mode == "CSS1Compat") ?
1672                     document.documentElement.clientWidth :
1673                     document.body.clientWidth;
1674         }
1675         return width;
1676     },
1677
1678     isAncestor : function(p, c) {
1679         p = Roo.getDom(p);
1680         c = Roo.getDom(c);
1681         if (!p || !c) {
1682             return false;
1683         }
1684
1685         if (p.contains && !Roo.isSafari) {
1686             return p.contains(c);
1687         } else if (p.compareDocumentPosition) {
1688             return !!(p.compareDocumentPosition(c) & 16);
1689         } else {
1690             var parent = c.parentNode;
1691             while (parent) {
1692                 if (parent == p) {
1693                     return true;
1694                 }
1695                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1696                     return false;
1697                 }
1698                 parent = parent.parentNode;
1699             }
1700             return false;
1701         }
1702     },
1703
1704     getRegion : function(el) {
1705         return Roo.lib.Region.getRegion(el);
1706     },
1707
1708     getY : function(el) {
1709         return this.getXY(el)[1];
1710     },
1711
1712     getX : function(el) {
1713         return this.getXY(el)[0];
1714     },
1715
1716     getXY : function(el) {
1717         var p, pe, b, scroll, bd = document.body;
1718         el = Roo.getDom(el);
1719         var fly = Roo.lib.AnimBase.fly;
1720         if (el.getBoundingClientRect) {
1721             b = el.getBoundingClientRect();
1722             scroll = fly(document).getScroll();
1723             return [b.left + scroll.left, b.top + scroll.top];
1724         }
1725         var x = 0, y = 0;
1726
1727         p = el;
1728
1729         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1730
1731         while (p) {
1732
1733             x += p.offsetLeft;
1734             y += p.offsetTop;
1735
1736             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1737                 hasAbsolute = true;
1738             }
1739
1740             if (Roo.isGecko) {
1741                 pe = fly(p);
1742
1743                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1744                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1745
1746
1747                 x += bl;
1748                 y += bt;
1749
1750
1751                 if (p != el && pe.getStyle('overflow') != 'visible') {
1752                     x += bl;
1753                     y += bt;
1754                 }
1755             }
1756             p = p.offsetParent;
1757         }
1758
1759         if (Roo.isSafari && hasAbsolute) {
1760             x -= bd.offsetLeft;
1761             y -= bd.offsetTop;
1762         }
1763
1764         if (Roo.isGecko && !hasAbsolute) {
1765             var dbd = fly(bd);
1766             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1767             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1768         }
1769
1770         p = el.parentNode;
1771         while (p && p != bd) {
1772             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1773                 x -= p.scrollLeft;
1774                 y -= p.scrollTop;
1775             }
1776             p = p.parentNode;
1777         }
1778         return [x, y];
1779     },
1780  
1781   
1782
1783
1784     setXY : function(el, xy) {
1785         el = Roo.fly(el, '_setXY');
1786         el.position();
1787         var pts = el.translatePoints(xy);
1788         if (xy[0] !== false) {
1789             el.dom.style.left = pts.left + "px";
1790         }
1791         if (xy[1] !== false) {
1792             el.dom.style.top = pts.top + "px";
1793         }
1794     },
1795
1796     setX : function(el, x) {
1797         this.setXY(el, [x, false]);
1798     },
1799
1800     setY : function(el, y) {
1801         this.setXY(el, [false, y]);
1802     }
1803 };
1804 /*
1805  * Portions of this file are based on pieces of Yahoo User Interface Library
1806  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1807  * YUI licensed under the BSD License:
1808  * http://developer.yahoo.net/yui/license.txt
1809  * <script type="text/javascript">
1810  *
1811  */
1812
1813 Roo.lib.Event = function() {
1814     var loadComplete = false;
1815     var listeners = [];
1816     var unloadListeners = [];
1817     var retryCount = 0;
1818     var onAvailStack = [];
1819     var counter = 0;
1820     var lastError = null;
1821
1822     return {
1823         POLL_RETRYS: 200,
1824         POLL_INTERVAL: 20,
1825         EL: 0,
1826         TYPE: 1,
1827         FN: 2,
1828         WFN: 3,
1829         OBJ: 3,
1830         ADJ_SCOPE: 4,
1831         _interval: null,
1832
1833         startInterval: function() {
1834             if (!this._interval) {
1835                 var self = this;
1836                 var callback = function() {
1837                     self._tryPreloadAttach();
1838                 };
1839                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1840
1841             }
1842         },
1843
1844         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1845             onAvailStack.push({ id:         p_id,
1846                 fn:         p_fn,
1847                 obj:        p_obj,
1848                 override:   p_override,
1849                 checkReady: false    });
1850
1851             retryCount = this.POLL_RETRYS;
1852             this.startInterval();
1853         },
1854
1855
1856         addListener: function(el, eventName, fn) {
1857             el = Roo.getDom(el);
1858             if (!el || !fn) {
1859                 return false;
1860             }
1861
1862             if ("unload" == eventName) {
1863                 unloadListeners[unloadListeners.length] =
1864                 [el, eventName, fn];
1865                 return true;
1866             }
1867
1868             var wrappedFn = function(e) {
1869                 return fn(Roo.lib.Event.getEvent(e));
1870             };
1871
1872             var li = [el, eventName, fn, wrappedFn];
1873
1874             var index = listeners.length;
1875             listeners[index] = li;
1876
1877             this.doAdd(el, eventName, wrappedFn, false);
1878             return true;
1879
1880         },
1881
1882
1883         removeListener: function(el, eventName, fn) {
1884             var i, len;
1885
1886             el = Roo.getDom(el);
1887
1888             if(!fn) {
1889                 return this.purgeElement(el, false, eventName);
1890             }
1891
1892
1893             if ("unload" == eventName) {
1894
1895                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1896                     var li = unloadListeners[i];
1897                     if (li &&
1898                         li[0] == el &&
1899                         li[1] == eventName &&
1900                         li[2] == fn) {
1901                         unloadListeners.splice(i, 1);
1902                         return true;
1903                     }
1904                 }
1905
1906                 return false;
1907             }
1908
1909             var cacheItem = null;
1910
1911
1912             var index = arguments[3];
1913
1914             if ("undefined" == typeof index) {
1915                 index = this._getCacheIndex(el, eventName, fn);
1916             }
1917
1918             if (index >= 0) {
1919                 cacheItem = listeners[index];
1920             }
1921
1922             if (!el || !cacheItem) {
1923                 return false;
1924             }
1925
1926             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1927
1928             delete listeners[index][this.WFN];
1929             delete listeners[index][this.FN];
1930             listeners.splice(index, 1);
1931
1932             return true;
1933
1934         },
1935
1936
1937         getTarget: function(ev, resolveTextNode) {
1938             ev = ev.browserEvent || ev;
1939             var t = ev.target || ev.srcElement;
1940             return this.resolveTextNode(t);
1941         },
1942
1943
1944         resolveTextNode: function(node) {
1945             if (Roo.isSafari && node && 3 == node.nodeType) {
1946                 return node.parentNode;
1947             } else {
1948                 return node;
1949             }
1950         },
1951
1952
1953         getPageX: function(ev) {
1954             ev = ev.browserEvent || ev;
1955             var x = ev.pageX;
1956             if (!x && 0 !== x) {
1957                 x = ev.clientX || 0;
1958
1959                 if (Roo.isIE) {
1960                     x += this.getScroll()[1];
1961                 }
1962             }
1963
1964             return x;
1965         },
1966
1967
1968         getPageY: function(ev) {
1969             ev = ev.browserEvent || ev;
1970             var y = ev.pageY;
1971             if (!y && 0 !== y) {
1972                 y = ev.clientY || 0;
1973
1974                 if (Roo.isIE) {
1975                     y += this.getScroll()[0];
1976                 }
1977             }
1978
1979
1980             return y;
1981         },
1982
1983
1984         getXY: function(ev) {
1985             ev = ev.browserEvent || ev;
1986             return [this.getPageX(ev), this.getPageY(ev)];
1987         },
1988
1989
1990         getRelatedTarget: function(ev) {
1991             ev = ev.browserEvent || ev;
1992             var t = ev.relatedTarget;
1993             if (!t) {
1994                 if (ev.type == "mouseout") {
1995                     t = ev.toElement;
1996                 } else if (ev.type == "mouseover") {
1997                     t = ev.fromElement;
1998                 }
1999             }
2000
2001             return this.resolveTextNode(t);
2002         },
2003
2004
2005         getTime: function(ev) {
2006             ev = ev.browserEvent || ev;
2007             if (!ev.time) {
2008                 var t = new Date().getTime();
2009                 try {
2010                     ev.time = t;
2011                 } catch(ex) {
2012                     this.lastError = ex;
2013                     return t;
2014                 }
2015             }
2016
2017             return ev.time;
2018         },
2019
2020
2021         stopEvent: function(ev) {
2022             this.stopPropagation(ev);
2023             this.preventDefault(ev);
2024         },
2025
2026
2027         stopPropagation: function(ev) {
2028             ev = ev.browserEvent || ev;
2029             if (ev.stopPropagation) {
2030                 ev.stopPropagation();
2031             } else {
2032                 ev.cancelBubble = true;
2033             }
2034         },
2035
2036
2037         preventDefault: function(ev) {
2038             ev = ev.browserEvent || ev;
2039             if(ev.preventDefault) {
2040                 ev.preventDefault();
2041             } else {
2042                 ev.returnValue = false;
2043             }
2044         },
2045
2046
2047         getEvent: function(e) {
2048             var ev = e || window.event;
2049             if (!ev) {
2050                 var c = this.getEvent.caller;
2051                 while (c) {
2052                     ev = c.arguments[0];
2053                     if (ev && Event == ev.constructor) {
2054                         break;
2055                     }
2056                     c = c.caller;
2057                 }
2058             }
2059             return ev;
2060         },
2061
2062
2063         getCharCode: function(ev) {
2064             ev = ev.browserEvent || ev;
2065             return ev.charCode || ev.keyCode || 0;
2066         },
2067
2068
2069         _getCacheIndex: function(el, eventName, fn) {
2070             for (var i = 0,len = listeners.length; i < len; ++i) {
2071                 var li = listeners[i];
2072                 if (li &&
2073                     li[this.FN] == fn &&
2074                     li[this.EL] == el &&
2075                     li[this.TYPE] == eventName) {
2076                     return i;
2077                 }
2078             }
2079
2080             return -1;
2081         },
2082
2083
2084         elCache: {},
2085
2086
2087         getEl: function(id) {
2088             return document.getElementById(id);
2089         },
2090
2091
2092         clearCache: function() {
2093         },
2094
2095
2096         _load: function(e) {
2097             loadComplete = true;
2098             var EU = Roo.lib.Event;
2099
2100
2101             if (Roo.isIE) {
2102                 EU.doRemove(window, "load", EU._load);
2103             }
2104         },
2105
2106
2107         _tryPreloadAttach: function() {
2108
2109             if (this.locked) {
2110                 return false;
2111             }
2112
2113             this.locked = true;
2114
2115
2116             var tryAgain = !loadComplete;
2117             if (!tryAgain) {
2118                 tryAgain = (retryCount > 0);
2119             }
2120
2121
2122             var notAvail = [];
2123             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2124                 var item = onAvailStack[i];
2125                 if (item) {
2126                     var el = this.getEl(item.id);
2127
2128                     if (el) {
2129                         if (!item.checkReady ||
2130                             loadComplete ||
2131                             el.nextSibling ||
2132                             (document && document.body)) {
2133
2134                             var scope = el;
2135                             if (item.override) {
2136                                 if (item.override === true) {
2137                                     scope = item.obj;
2138                                 } else {
2139                                     scope = item.override;
2140                                 }
2141                             }
2142                             item.fn.call(scope, item.obj);
2143                             onAvailStack[i] = null;
2144                         }
2145                     } else {
2146                         notAvail.push(item);
2147                     }
2148                 }
2149             }
2150
2151             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2152
2153             if (tryAgain) {
2154
2155                 this.startInterval();
2156             } else {
2157                 clearInterval(this._interval);
2158                 this._interval = null;
2159             }
2160
2161             this.locked = false;
2162
2163             return true;
2164
2165         },
2166
2167
2168         purgeElement: function(el, recurse, eventName) {
2169             var elListeners = this.getListeners(el, eventName);
2170             if (elListeners) {
2171                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2172                     var l = elListeners[i];
2173                     this.removeListener(el, l.type, l.fn);
2174                 }
2175             }
2176
2177             if (recurse && el && el.childNodes) {
2178                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2179                     this.purgeElement(el.childNodes[i], recurse, eventName);
2180                 }
2181             }
2182         },
2183
2184
2185         getListeners: function(el, eventName) {
2186             var results = [], searchLists;
2187             if (!eventName) {
2188                 searchLists = [listeners, unloadListeners];
2189             } else if (eventName == "unload") {
2190                 searchLists = [unloadListeners];
2191             } else {
2192                 searchLists = [listeners];
2193             }
2194
2195             for (var j = 0; j < searchLists.length; ++j) {
2196                 var searchList = searchLists[j];
2197                 if (searchList && searchList.length > 0) {
2198                     for (var i = 0,len = searchList.length; i < len; ++i) {
2199                         var l = searchList[i];
2200                         if (l && l[this.EL] === el &&
2201                             (!eventName || eventName === l[this.TYPE])) {
2202                             results.push({
2203                                 type:   l[this.TYPE],
2204                                 fn:     l[this.FN],
2205                                 obj:    l[this.OBJ],
2206                                 adjust: l[this.ADJ_SCOPE],
2207                                 index:  i
2208                             });
2209                         }
2210                     }
2211                 }
2212             }
2213
2214             return (results.length) ? results : null;
2215         },
2216
2217
2218         _unload: function(e) {
2219
2220             var EU = Roo.lib.Event, i, j, l, len, index;
2221
2222             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2223                 l = unloadListeners[i];
2224                 if (l) {
2225                     var scope = window;
2226                     if (l[EU.ADJ_SCOPE]) {
2227                         if (l[EU.ADJ_SCOPE] === true) {
2228                             scope = l[EU.OBJ];
2229                         } else {
2230                             scope = l[EU.ADJ_SCOPE];
2231                         }
2232                     }
2233                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2234                     unloadListeners[i] = null;
2235                     l = null;
2236                     scope = null;
2237                 }
2238             }
2239
2240             unloadListeners = null;
2241
2242             if (listeners && listeners.length > 0) {
2243                 j = listeners.length;
2244                 while (j) {
2245                     index = j - 1;
2246                     l = listeners[index];
2247                     if (l) {
2248                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2249                                 l[EU.FN], index);
2250                     }
2251                     j = j - 1;
2252                 }
2253                 l = null;
2254
2255                 EU.clearCache();
2256             }
2257
2258             EU.doRemove(window, "unload", EU._unload);
2259
2260         },
2261
2262
2263         getScroll: function() {
2264             var dd = document.documentElement, db = document.body;
2265             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2266                 return [dd.scrollTop, dd.scrollLeft];
2267             } else if (db) {
2268                 return [db.scrollTop, db.scrollLeft];
2269             } else {
2270                 return [0, 0];
2271             }
2272         },
2273
2274
2275         doAdd: function () {
2276             if (window.addEventListener) {
2277                 return function(el, eventName, fn, capture) {
2278                     el.addEventListener(eventName, fn, (capture));
2279                 };
2280             } else if (window.attachEvent) {
2281                 return function(el, eventName, fn, capture) {
2282                     el.attachEvent("on" + eventName, fn);
2283                 };
2284             } else {
2285                 return function() {
2286                 };
2287             }
2288         }(),
2289
2290
2291         doRemove: function() {
2292             if (window.removeEventListener) {
2293                 return function (el, eventName, fn, capture) {
2294                     el.removeEventListener(eventName, fn, (capture));
2295                 };
2296             } else if (window.detachEvent) {
2297                 return function (el, eventName, fn) {
2298                     el.detachEvent("on" + eventName, fn);
2299                 };
2300             } else {
2301                 return function() {
2302                 };
2303             }
2304         }()
2305     };
2306     
2307 }();
2308 (function() {     
2309    
2310     var E = Roo.lib.Event;
2311     E.on = E.addListener;
2312     E.un = E.removeListener;
2313
2314     if (document && document.body) {
2315         E._load();
2316     } else {
2317         E.doAdd(window, "load", E._load);
2318     }
2319     E.doAdd(window, "unload", E._unload);
2320     E._tryPreloadAttach();
2321 })();
2322
2323 /*
2324  * Portions of this file are based on pieces of Yahoo User Interface Library
2325  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2326  * YUI licensed under the BSD License:
2327  * http://developer.yahoo.net/yui/license.txt
2328  * <script type="text/javascript">
2329  *
2330  */
2331
2332 (function() {
2333     
2334     Roo.lib.Ajax = {
2335         request : function(method, uri, cb, data, options) {
2336             if(options){
2337                 var hs = options.headers;
2338                 if(hs){
2339                     for(var h in hs){
2340                         if(hs.hasOwnProperty(h)){
2341                             this.initHeader(h, hs[h], false);
2342                         }
2343                     }
2344                 }
2345                 if(options.xmlData){
2346                     this.initHeader('Content-Type', 'text/xml', false);
2347                     method = 'POST';
2348                     data = options.xmlData;
2349                 }
2350             }
2351
2352             return this.asyncRequest(method, uri, cb, data);
2353         },
2354
2355         serializeForm : function(form) {
2356             if(typeof form == 'string') {
2357                 form = (document.getElementById(form) || document.forms[form]);
2358             }
2359
2360             var el, name, val, disabled, data = '', hasSubmit = false;
2361             for (var i = 0; i < form.elements.length; i++) {
2362                 el = form.elements[i];
2363                 disabled = form.elements[i].disabled;
2364                 name = form.elements[i].name;
2365                 val = form.elements[i].value;
2366
2367                 if (!disabled && name){
2368                     switch (el.type)
2369                             {
2370                         case 'select-one':
2371                         case 'select-multiple':
2372                             for (var j = 0; j < el.options.length; j++) {
2373                                 if (el.options[j].selected) {
2374                                     if (Roo.isIE) {
2375                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2376                                     }
2377                                     else {
2378                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2379                                     }
2380                                 }
2381                             }
2382                             break;
2383                         case 'radio':
2384                         case 'checkbox':
2385                             if (el.checked) {
2386                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2387                             }
2388                             break;
2389                         case 'file':
2390
2391                         case undefined:
2392
2393                         case 'reset':
2394
2395                         case 'button':
2396
2397                             break;
2398                         case 'submit':
2399                             if(hasSubmit == false) {
2400                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2401                                 hasSubmit = true;
2402                             }
2403                             break;
2404                         default:
2405                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2406                             break;
2407                     }
2408                 }
2409             }
2410             data = data.substr(0, data.length - 1);
2411             return data;
2412         },
2413
2414         headers:{},
2415
2416         hasHeaders:false,
2417
2418         useDefaultHeader:true,
2419
2420         defaultPostHeader:'application/x-www-form-urlencoded',
2421
2422         useDefaultXhrHeader:true,
2423
2424         defaultXhrHeader:'XMLHttpRequest',
2425
2426         hasDefaultHeaders:true,
2427
2428         defaultHeaders:{},
2429
2430         poll:{},
2431
2432         timeout:{},
2433
2434         pollInterval:50,
2435
2436         transactionId:0,
2437
2438         setProgId:function(id)
2439         {
2440             this.activeX.unshift(id);
2441         },
2442
2443         setDefaultPostHeader:function(b)
2444         {
2445             this.useDefaultHeader = b;
2446         },
2447
2448         setDefaultXhrHeader:function(b)
2449         {
2450             this.useDefaultXhrHeader = b;
2451         },
2452
2453         setPollingInterval:function(i)
2454         {
2455             if (typeof i == 'number' && isFinite(i)) {
2456                 this.pollInterval = i;
2457             }
2458         },
2459
2460         createXhrObject:function(transactionId)
2461         {
2462             var obj,http;
2463             try
2464             {
2465
2466                 http = new XMLHttpRequest();
2467
2468                 obj = { conn:http, tId:transactionId };
2469             }
2470             catch(e)
2471             {
2472                 for (var i = 0; i < this.activeX.length; ++i) {
2473                     try
2474                     {
2475
2476                         http = new ActiveXObject(this.activeX[i]);
2477
2478                         obj = { conn:http, tId:transactionId };
2479                         break;
2480                     }
2481                     catch(e) {
2482                     }
2483                 }
2484             }
2485             finally
2486             {
2487                 return obj;
2488             }
2489         },
2490
2491         getConnectionObject:function()
2492         {
2493             var o;
2494             var tId = this.transactionId;
2495
2496             try
2497             {
2498                 o = this.createXhrObject(tId);
2499                 if (o) {
2500                     this.transactionId++;
2501                 }
2502             }
2503             catch(e) {
2504             }
2505             finally
2506             {
2507                 return o;
2508             }
2509         },
2510
2511         asyncRequest:function(method, uri, callback, postData)
2512         {
2513             var o = this.getConnectionObject();
2514
2515             if (!o) {
2516                 return null;
2517             }
2518             else {
2519                 o.conn.open(method, uri, true);
2520
2521                 if (this.useDefaultXhrHeader) {
2522                     if (!this.defaultHeaders['X-Requested-With']) {
2523                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2524                     }
2525                 }
2526
2527                 if(postData && this.useDefaultHeader){
2528                     this.initHeader('Content-Type', this.defaultPostHeader);
2529                 }
2530
2531                  if (this.hasDefaultHeaders || this.hasHeaders) {
2532                     this.setHeader(o);
2533                 }
2534
2535                 this.handleReadyState(o, callback);
2536                 o.conn.send(postData || null);
2537
2538                 return o;
2539             }
2540         },
2541
2542         handleReadyState:function(o, callback)
2543         {
2544             var oConn = this;
2545
2546             if (callback && callback.timeout) {
2547                 this.timeout[o.tId] = window.setTimeout(function() {
2548                     oConn.abort(o, callback, true);
2549                 }, callback.timeout);
2550             }
2551
2552             this.poll[o.tId] = window.setInterval(
2553                     function() {
2554                         if (o.conn && o.conn.readyState == 4) {
2555                             window.clearInterval(oConn.poll[o.tId]);
2556                             delete oConn.poll[o.tId];
2557
2558                             if(callback && callback.timeout) {
2559                                 window.clearTimeout(oConn.timeout[o.tId]);
2560                                 delete oConn.timeout[o.tId];
2561                             }
2562
2563                             oConn.handleTransactionResponse(o, callback);
2564                         }
2565                     }
2566                     , this.pollInterval);
2567         },
2568
2569         handleTransactionResponse:function(o, callback, isAbort)
2570         {
2571
2572             if (!callback) {
2573                 this.releaseObject(o);
2574                 return;
2575             }
2576
2577             var httpStatus, responseObject;
2578
2579             try
2580             {
2581                 if (o.conn.status !== undefined && o.conn.status != 0) {
2582                     httpStatus = o.conn.status;
2583                 }
2584                 else {
2585                     httpStatus = 13030;
2586                 }
2587             }
2588             catch(e) {
2589
2590
2591                 httpStatus = 13030;
2592             }
2593
2594             if (httpStatus >= 200 && httpStatus < 300) {
2595                 responseObject = this.createResponseObject(o, callback.argument);
2596                 if (callback.success) {
2597                     if (!callback.scope) {
2598                         callback.success(responseObject);
2599                     }
2600                     else {
2601
2602
2603                         callback.success.apply(callback.scope, [responseObject]);
2604                     }
2605                 }
2606             }
2607             else {
2608                 switch (httpStatus) {
2609
2610                     case 12002:
2611                     case 12029:
2612                     case 12030:
2613                     case 12031:
2614                     case 12152:
2615                     case 13030:
2616                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2617                         if (callback.failure) {
2618                             if (!callback.scope) {
2619                                 callback.failure(responseObject);
2620                             }
2621                             else {
2622                                 callback.failure.apply(callback.scope, [responseObject]);
2623                             }
2624                         }
2625                         break;
2626                     default:
2627                         responseObject = this.createResponseObject(o, callback.argument);
2628                         if (callback.failure) {
2629                             if (!callback.scope) {
2630                                 callback.failure(responseObject);
2631                             }
2632                             else {
2633                                 callback.failure.apply(callback.scope, [responseObject]);
2634                             }
2635                         }
2636                 }
2637             }
2638
2639             this.releaseObject(o);
2640             responseObject = null;
2641         },
2642
2643         createResponseObject:function(o, callbackArg)
2644         {
2645             var obj = {};
2646             var headerObj = {};
2647
2648             try
2649             {
2650                 var headerStr = o.conn.getAllResponseHeaders();
2651                 var header = headerStr.split('\n');
2652                 for (var i = 0; i < header.length; i++) {
2653                     var delimitPos = header[i].indexOf(':');
2654                     if (delimitPos != -1) {
2655                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2656                     }
2657                 }
2658             }
2659             catch(e) {
2660             }
2661
2662             obj.tId = o.tId;
2663             obj.status = o.conn.status;
2664             obj.statusText = o.conn.statusText;
2665             obj.getResponseHeader = headerObj;
2666             obj.getAllResponseHeaders = headerStr;
2667             obj.responseText = o.conn.responseText;
2668             obj.responseXML = o.conn.responseXML;
2669
2670             if (typeof callbackArg !== undefined) {
2671                 obj.argument = callbackArg;
2672             }
2673
2674             return obj;
2675         },
2676
2677         createExceptionObject:function(tId, callbackArg, isAbort)
2678         {
2679             var COMM_CODE = 0;
2680             var COMM_ERROR = 'communication failure';
2681             var ABORT_CODE = -1;
2682             var ABORT_ERROR = 'transaction aborted';
2683
2684             var obj = {};
2685
2686             obj.tId = tId;
2687             if (isAbort) {
2688                 obj.status = ABORT_CODE;
2689                 obj.statusText = ABORT_ERROR;
2690             }
2691             else {
2692                 obj.status = COMM_CODE;
2693                 obj.statusText = COMM_ERROR;
2694             }
2695
2696             if (callbackArg) {
2697                 obj.argument = callbackArg;
2698             }
2699
2700             return obj;
2701         },
2702
2703         initHeader:function(label, value, isDefault)
2704         {
2705             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2706
2707             if (headerObj[label] === undefined) {
2708                 headerObj[label] = value;
2709             }
2710             else {
2711
2712
2713                 headerObj[label] = value + "," + headerObj[label];
2714             }
2715
2716             if (isDefault) {
2717                 this.hasDefaultHeaders = true;
2718             }
2719             else {
2720                 this.hasHeaders = true;
2721             }
2722         },
2723
2724
2725         setHeader:function(o)
2726         {
2727             if (this.hasDefaultHeaders) {
2728                 for (var prop in this.defaultHeaders) {
2729                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2730                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2731                     }
2732                 }
2733             }
2734
2735             if (this.hasHeaders) {
2736                 for (var prop in this.headers) {
2737                     if (this.headers.hasOwnProperty(prop)) {
2738                         o.conn.setRequestHeader(prop, this.headers[prop]);
2739                     }
2740                 }
2741                 this.headers = {};
2742                 this.hasHeaders = false;
2743             }
2744         },
2745
2746         resetDefaultHeaders:function() {
2747             delete this.defaultHeaders;
2748             this.defaultHeaders = {};
2749             this.hasDefaultHeaders = false;
2750         },
2751
2752         abort:function(o, callback, isTimeout)
2753         {
2754             if(this.isCallInProgress(o)) {
2755                 o.conn.abort();
2756                 window.clearInterval(this.poll[o.tId]);
2757                 delete this.poll[o.tId];
2758                 if (isTimeout) {
2759                     delete this.timeout[o.tId];
2760                 }
2761
2762                 this.handleTransactionResponse(o, callback, true);
2763
2764                 return true;
2765             }
2766             else {
2767                 return false;
2768             }
2769         },
2770
2771
2772         isCallInProgress:function(o)
2773         {
2774             if (o && o.conn) {
2775                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2776             }
2777             else {
2778
2779                 return false;
2780             }
2781         },
2782
2783
2784         releaseObject:function(o)
2785         {
2786
2787             o.conn = null;
2788
2789             o = null;
2790         },
2791
2792         activeX:[
2793         'MSXML2.XMLHTTP.3.0',
2794         'MSXML2.XMLHTTP',
2795         'Microsoft.XMLHTTP'
2796         ]
2797
2798
2799     };
2800 })();/*
2801  * Portions of this file are based on pieces of Yahoo User Interface Library
2802  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2803  * YUI licensed under the BSD License:
2804  * http://developer.yahoo.net/yui/license.txt
2805  * <script type="text/javascript">
2806  *
2807  */
2808
2809 Roo.lib.Region = function(t, r, b, l) {
2810     this.top = t;
2811     this[1] = t;
2812     this.right = r;
2813     this.bottom = b;
2814     this.left = l;
2815     this[0] = l;
2816 };
2817
2818
2819 Roo.lib.Region.prototype = {
2820     contains : function(region) {
2821         return ( region.left >= this.left &&
2822                  region.right <= this.right &&
2823                  region.top >= this.top &&
2824                  region.bottom <= this.bottom    );
2825
2826     },
2827
2828     getArea : function() {
2829         return ( (this.bottom - this.top) * (this.right - this.left) );
2830     },
2831
2832     intersect : function(region) {
2833         var t = Math.max(this.top, region.top);
2834         var r = Math.min(this.right, region.right);
2835         var b = Math.min(this.bottom, region.bottom);
2836         var l = Math.max(this.left, region.left);
2837
2838         if (b >= t && r >= l) {
2839             return new Roo.lib.Region(t, r, b, l);
2840         } else {
2841             return null;
2842         }
2843     },
2844     union : function(region) {
2845         var t = Math.min(this.top, region.top);
2846         var r = Math.max(this.right, region.right);
2847         var b = Math.max(this.bottom, region.bottom);
2848         var l = Math.min(this.left, region.left);
2849
2850         return new Roo.lib.Region(t, r, b, l);
2851     },
2852
2853     adjust : function(t, l, b, r) {
2854         this.top += t;
2855         this.left += l;
2856         this.right += r;
2857         this.bottom += b;
2858         return this;
2859     }
2860 };
2861
2862 Roo.lib.Region.getRegion = function(el) {
2863     var p = Roo.lib.Dom.getXY(el);
2864
2865     var t = p[1];
2866     var r = p[0] + el.offsetWidth;
2867     var b = p[1] + el.offsetHeight;
2868     var l = p[0];
2869
2870     return new Roo.lib.Region(t, r, b, l);
2871 };
2872 /*
2873  * Portions of this file are based on pieces of Yahoo User Interface Library
2874  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2875  * YUI licensed under the BSD License:
2876  * http://developer.yahoo.net/yui/license.txt
2877  * <script type="text/javascript">
2878  *
2879  */
2880 //@@dep Roo.lib.Region
2881
2882
2883 Roo.lib.Point = function(x, y) {
2884     if (x instanceof Array) {
2885         y = x[1];
2886         x = x[0];
2887     }
2888     this.x = this.right = this.left = this[0] = x;
2889     this.y = this.top = this.bottom = this[1] = y;
2890 };
2891
2892 Roo.lib.Point.prototype = new Roo.lib.Region();
2893 /*
2894  * Portions of this file are based on pieces of Yahoo User Interface Library
2895  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2896  * YUI licensed under the BSD License:
2897  * http://developer.yahoo.net/yui/license.txt
2898  * <script type="text/javascript">
2899  *
2900  */
2901  
2902 (function() {   
2903
2904     Roo.lib.Anim = {
2905         scroll : function(el, args, duration, easing, cb, scope) {
2906             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2907         },
2908
2909         motion : function(el, args, duration, easing, cb, scope) {
2910             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2911         },
2912
2913         color : function(el, args, duration, easing, cb, scope) {
2914             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2915         },
2916
2917         run : function(el, args, duration, easing, cb, scope, type) {
2918             type = type || Roo.lib.AnimBase;
2919             if (typeof easing == "string") {
2920                 easing = Roo.lib.Easing[easing];
2921             }
2922             var anim = new type(el, args, duration, easing);
2923             anim.animateX(function() {
2924                 Roo.callback(cb, scope);
2925             });
2926             return anim;
2927         }
2928     };
2929 })();/*
2930  * Portions of this file are based on pieces of Yahoo User Interface Library
2931  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2932  * YUI licensed under the BSD License:
2933  * http://developer.yahoo.net/yui/license.txt
2934  * <script type="text/javascript">
2935  *
2936  */
2937
2938 (function() {    
2939     var libFlyweight;
2940     
2941     function fly(el) {
2942         if (!libFlyweight) {
2943             libFlyweight = new Roo.Element.Flyweight();
2944         }
2945         libFlyweight.dom = el;
2946         return libFlyweight;
2947     }
2948
2949     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2950     
2951    
2952     
2953     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2954         if (el) {
2955             this.init(el, attributes, duration, method);
2956         }
2957     };
2958
2959     Roo.lib.AnimBase.fly = fly;
2960     
2961     
2962     
2963     Roo.lib.AnimBase.prototype = {
2964
2965         toString: function() {
2966             var el = this.getEl();
2967             var id = el.id || el.tagName;
2968             return ("Anim " + id);
2969         },
2970
2971         patterns: {
2972             noNegatives:        /width|height|opacity|padding/i,
2973             offsetAttribute:  /^((width|height)|(top|left))$/,
2974             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2975             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2976         },
2977
2978
2979         doMethod: function(attr, start, end) {
2980             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2981         },
2982
2983
2984         setAttribute: function(attr, val, unit) {
2985             if (this.patterns.noNegatives.test(attr)) {
2986                 val = (val > 0) ? val : 0;
2987             }
2988
2989             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2990         },
2991
2992
2993         getAttribute: function(attr) {
2994             var el = this.getEl();
2995             var val = fly(el).getStyle(attr);
2996
2997             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2998                 return parseFloat(val);
2999             }
3000
3001             var a = this.patterns.offsetAttribute.exec(attr) || [];
3002             var pos = !!( a[3] );
3003             var box = !!( a[2] );
3004
3005
3006             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3007                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3008             } else {
3009                 val = 0;
3010             }
3011
3012             return val;
3013         },
3014
3015
3016         getDefaultUnit: function(attr) {
3017             if (this.patterns.defaultUnit.test(attr)) {
3018                 return 'px';
3019             }
3020
3021             return '';
3022         },
3023
3024         animateX : function(callback, scope) {
3025             var f = function() {
3026                 this.onComplete.removeListener(f);
3027                 if (typeof callback == "function") {
3028                     callback.call(scope || this, this);
3029                 }
3030             };
3031             this.onComplete.addListener(f, this);
3032             this.animate();
3033         },
3034
3035
3036         setRuntimeAttribute: function(attr) {
3037             var start;
3038             var end;
3039             var attributes = this.attributes;
3040
3041             this.runtimeAttributes[attr] = {};
3042
3043             var isset = function(prop) {
3044                 return (typeof prop !== 'undefined');
3045             };
3046
3047             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3048                 return false;
3049             }
3050
3051             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3052
3053
3054             if (isset(attributes[attr]['to'])) {
3055                 end = attributes[attr]['to'];
3056             } else if (isset(attributes[attr]['by'])) {
3057                 if (start.constructor == Array) {
3058                     end = [];
3059                     for (var i = 0, len = start.length; i < len; ++i) {
3060                         end[i] = start[i] + attributes[attr]['by'][i];
3061                     }
3062                 } else {
3063                     end = start + attributes[attr]['by'];
3064                 }
3065             }
3066
3067             this.runtimeAttributes[attr].start = start;
3068             this.runtimeAttributes[attr].end = end;
3069
3070
3071             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3072         },
3073
3074
3075         init: function(el, attributes, duration, method) {
3076
3077             var isAnimated = false;
3078
3079
3080             var startTime = null;
3081
3082
3083             var actualFrames = 0;
3084
3085
3086             el = Roo.getDom(el);
3087
3088
3089             this.attributes = attributes || {};
3090
3091
3092             this.duration = duration || 1;
3093
3094
3095             this.method = method || Roo.lib.Easing.easeNone;
3096
3097
3098             this.useSeconds = true;
3099
3100
3101             this.currentFrame = 0;
3102
3103
3104             this.totalFrames = Roo.lib.AnimMgr.fps;
3105
3106
3107             this.getEl = function() {
3108                 return el;
3109             };
3110
3111
3112             this.isAnimated = function() {
3113                 return isAnimated;
3114             };
3115
3116
3117             this.getStartTime = function() {
3118                 return startTime;
3119             };
3120
3121             this.runtimeAttributes = {};
3122
3123
3124             this.animate = function() {
3125                 if (this.isAnimated()) {
3126                     return false;
3127                 }
3128
3129                 this.currentFrame = 0;
3130
3131                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3132
3133                 Roo.lib.AnimMgr.registerElement(this);
3134             };
3135
3136
3137             this.stop = function(finish) {
3138                 if (finish) {
3139                     this.currentFrame = this.totalFrames;
3140                     this._onTween.fire();
3141                 }
3142                 Roo.lib.AnimMgr.stop(this);
3143             };
3144
3145             var onStart = function() {
3146                 this.onStart.fire();
3147
3148                 this.runtimeAttributes = {};
3149                 for (var attr in this.attributes) {
3150                     this.setRuntimeAttribute(attr);
3151                 }
3152
3153                 isAnimated = true;
3154                 actualFrames = 0;
3155                 startTime = new Date();
3156             };
3157
3158
3159             var onTween = function() {
3160                 var data = {
3161                     duration: new Date() - this.getStartTime(),
3162                     currentFrame: this.currentFrame
3163                 };
3164
3165                 data.toString = function() {
3166                     return (
3167                             'duration: ' + data.duration +
3168                             ', currentFrame: ' + data.currentFrame
3169                             );
3170                 };
3171
3172                 this.onTween.fire(data);
3173
3174                 var runtimeAttributes = this.runtimeAttributes;
3175
3176                 for (var attr in runtimeAttributes) {
3177                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3178                 }
3179
3180                 actualFrames += 1;
3181             };
3182
3183             var onComplete = function() {
3184                 var actual_duration = (new Date() - startTime) / 1000 ;
3185
3186                 var data = {
3187                     duration: actual_duration,
3188                     frames: actualFrames,
3189                     fps: actualFrames / actual_duration
3190                 };
3191
3192                 data.toString = function() {
3193                     return (
3194                             'duration: ' + data.duration +
3195                             ', frames: ' + data.frames +
3196                             ', fps: ' + data.fps
3197                             );
3198                 };
3199
3200                 isAnimated = false;
3201                 actualFrames = 0;
3202                 this.onComplete.fire(data);
3203             };
3204
3205
3206             this._onStart = new Roo.util.Event(this);
3207             this.onStart = new Roo.util.Event(this);
3208             this.onTween = new Roo.util.Event(this);
3209             this._onTween = new Roo.util.Event(this);
3210             this.onComplete = new Roo.util.Event(this);
3211             this._onComplete = new Roo.util.Event(this);
3212             this._onStart.addListener(onStart);
3213             this._onTween.addListener(onTween);
3214             this._onComplete.addListener(onComplete);
3215         }
3216     };
3217 })();
3218 /*
3219  * Portions of this file are based on pieces of Yahoo User Interface Library
3220  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3221  * YUI licensed under the BSD License:
3222  * http://developer.yahoo.net/yui/license.txt
3223  * <script type="text/javascript">
3224  *
3225  */
3226
3227 Roo.lib.AnimMgr = new function() {
3228
3229         var thread = null;
3230
3231
3232         var queue = [];
3233
3234
3235         var tweenCount = 0;
3236
3237
3238         this.fps = 1000;
3239
3240
3241         this.delay = 1;
3242
3243
3244         this.registerElement = function(tween) {
3245             queue[queue.length] = tween;
3246             tweenCount += 1;
3247             tween._onStart.fire();
3248             this.start();
3249         };
3250
3251
3252         this.unRegister = function(tween, index) {
3253             tween._onComplete.fire();
3254             index = index || getIndex(tween);
3255             if (index != -1) {
3256                 queue.splice(index, 1);
3257             }
3258
3259             tweenCount -= 1;
3260             if (tweenCount <= 0) {
3261                 this.stop();
3262             }
3263         };
3264
3265
3266         this.start = function() {
3267             if (thread === null) {
3268                 thread = setInterval(this.run, this.delay);
3269             }
3270         };
3271
3272
3273         this.stop = function(tween) {
3274             if (!tween) {
3275                 clearInterval(thread);
3276
3277                 for (var i = 0, len = queue.length; i < len; ++i) {
3278                     if (queue[0].isAnimated()) {
3279                         this.unRegister(queue[0], 0);
3280                     }
3281                 }
3282
3283                 queue = [];
3284                 thread = null;
3285                 tweenCount = 0;
3286             }
3287             else {
3288                 this.unRegister(tween);
3289             }
3290         };
3291
3292
3293         this.run = function() {
3294             for (var i = 0, len = queue.length; i < len; ++i) {
3295                 var tween = queue[i];
3296                 if (!tween || !tween.isAnimated()) {
3297                     continue;
3298                 }
3299
3300                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3301                 {
3302                     tween.currentFrame += 1;
3303
3304                     if (tween.useSeconds) {
3305                         correctFrame(tween);
3306                     }
3307                     tween._onTween.fire();
3308                 }
3309                 else {
3310                     Roo.lib.AnimMgr.stop(tween, i);
3311                 }
3312             }
3313         };
3314
3315         var getIndex = function(anim) {
3316             for (var i = 0, len = queue.length; i < len; ++i) {
3317                 if (queue[i] == anim) {
3318                     return i;
3319                 }
3320             }
3321             return -1;
3322         };
3323
3324
3325         var correctFrame = function(tween) {
3326             var frames = tween.totalFrames;
3327             var frame = tween.currentFrame;
3328             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3329             var elapsed = (new Date() - tween.getStartTime());
3330             var tweak = 0;
3331
3332             if (elapsed < tween.duration * 1000) {
3333                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3334             } else {
3335                 tweak = frames - (frame + 1);
3336             }
3337             if (tweak > 0 && isFinite(tweak)) {
3338                 if (tween.currentFrame + tweak >= frames) {
3339                     tweak = frames - (frame + 1);
3340                 }
3341
3342                 tween.currentFrame += tweak;
3343             }
3344         };
3345     };/*
3346  * Portions of this file are based on pieces of Yahoo User Interface Library
3347  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3348  * YUI licensed under the BSD License:
3349  * http://developer.yahoo.net/yui/license.txt
3350  * <script type="text/javascript">
3351  *
3352  */
3353 Roo.lib.Bezier = new function() {
3354
3355         this.getPosition = function(points, t) {
3356             var n = points.length;
3357             var tmp = [];
3358
3359             for (var i = 0; i < n; ++i) {
3360                 tmp[i] = [points[i][0], points[i][1]];
3361             }
3362
3363             for (var j = 1; j < n; ++j) {
3364                 for (i = 0; i < n - j; ++i) {
3365                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3366                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3367                 }
3368             }
3369
3370             return [ tmp[0][0], tmp[0][1] ];
3371
3372         };
3373     };/*
3374  * Portions of this file are based on pieces of Yahoo User Interface Library
3375  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3376  * YUI licensed under the BSD License:
3377  * http://developer.yahoo.net/yui/license.txt
3378  * <script type="text/javascript">
3379  *
3380  */
3381 (function() {
3382
3383     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3384         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3385     };
3386
3387     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3388
3389     var fly = Roo.lib.AnimBase.fly;
3390     var Y = Roo.lib;
3391     var superclass = Y.ColorAnim.superclass;
3392     var proto = Y.ColorAnim.prototype;
3393
3394     proto.toString = function() {
3395         var el = this.getEl();
3396         var id = el.id || el.tagName;
3397         return ("ColorAnim " + id);
3398     };
3399
3400     proto.patterns.color = /color$/i;
3401     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3402     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3403     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3404     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3405
3406
3407     proto.parseColor = function(s) {
3408         if (s.length == 3) {
3409             return s;
3410         }
3411
3412         var c = this.patterns.hex.exec(s);
3413         if (c && c.length == 4) {
3414             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3415         }
3416
3417         c = this.patterns.rgb.exec(s);
3418         if (c && c.length == 4) {
3419             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3420         }
3421
3422         c = this.patterns.hex3.exec(s);
3423         if (c && c.length == 4) {
3424             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3425         }
3426
3427         return null;
3428     };
3429     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3430     proto.getAttribute = function(attr) {
3431         var el = this.getEl();
3432         if (this.patterns.color.test(attr)) {
3433             var val = fly(el).getStyle(attr);
3434
3435             if (this.patterns.transparent.test(val)) {
3436                 var parent = el.parentNode;
3437                 val = fly(parent).getStyle(attr);
3438
3439                 while (parent && this.patterns.transparent.test(val)) {
3440                     parent = parent.parentNode;
3441                     val = fly(parent).getStyle(attr);
3442                     if (parent.tagName.toUpperCase() == 'HTML') {
3443                         val = '#fff';
3444                     }
3445                 }
3446             }
3447         } else {
3448             val = superclass.getAttribute.call(this, attr);
3449         }
3450
3451         return val;
3452     };
3453     proto.getAttribute = function(attr) {
3454         var el = this.getEl();
3455         if (this.patterns.color.test(attr)) {
3456             var val = fly(el).getStyle(attr);
3457
3458             if (this.patterns.transparent.test(val)) {
3459                 var parent = el.parentNode;
3460                 val = fly(parent).getStyle(attr);
3461
3462                 while (parent && this.patterns.transparent.test(val)) {
3463                     parent = parent.parentNode;
3464                     val = fly(parent).getStyle(attr);
3465                     if (parent.tagName.toUpperCase() == 'HTML') {
3466                         val = '#fff';
3467                     }
3468                 }
3469             }
3470         } else {
3471             val = superclass.getAttribute.call(this, attr);
3472         }
3473
3474         return val;
3475     };
3476
3477     proto.doMethod = function(attr, start, end) {
3478         var val;
3479
3480         if (this.patterns.color.test(attr)) {
3481             val = [];
3482             for (var i = 0, len = start.length; i < len; ++i) {
3483                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3484             }
3485
3486             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3487         }
3488         else {
3489             val = superclass.doMethod.call(this, attr, start, end);
3490         }
3491
3492         return val;
3493     };
3494
3495     proto.setRuntimeAttribute = function(attr) {
3496         superclass.setRuntimeAttribute.call(this, attr);
3497
3498         if (this.patterns.color.test(attr)) {
3499             var attributes = this.attributes;
3500             var start = this.parseColor(this.runtimeAttributes[attr].start);
3501             var end = this.parseColor(this.runtimeAttributes[attr].end);
3502
3503             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3504                 end = this.parseColor(attributes[attr].by);
3505
3506                 for (var i = 0, len = start.length; i < len; ++i) {
3507                     end[i] = start[i] + end[i];
3508                 }
3509             }
3510
3511             this.runtimeAttributes[attr].start = start;
3512             this.runtimeAttributes[attr].end = end;
3513         }
3514     };
3515 })();
3516
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525 Roo.lib.Easing = {
3526
3527
3528     easeNone: function (t, b, c, d) {
3529         return c * t / d + b;
3530     },
3531
3532
3533     easeIn: function (t, b, c, d) {
3534         return c * (t /= d) * t + b;
3535     },
3536
3537
3538     easeOut: function (t, b, c, d) {
3539         return -c * (t /= d) * (t - 2) + b;
3540     },
3541
3542
3543     easeBoth: function (t, b, c, d) {
3544         if ((t /= d / 2) < 1) {
3545             return c / 2 * t * t + b;
3546         }
3547
3548         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3549     },
3550
3551
3552     easeInStrong: function (t, b, c, d) {
3553         return c * (t /= d) * t * t * t + b;
3554     },
3555
3556
3557     easeOutStrong: function (t, b, c, d) {
3558         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3559     },
3560
3561
3562     easeBothStrong: function (t, b, c, d) {
3563         if ((t /= d / 2) < 1) {
3564             return c / 2 * t * t * t * t + b;
3565         }
3566
3567         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3568     },
3569
3570
3571
3572     elasticIn: function (t, b, c, d, a, p) {
3573         if (t == 0) {
3574             return b;
3575         }
3576         if ((t /= d) == 1) {
3577             return b + c;
3578         }
3579         if (!p) {
3580             p = d * .3;
3581         }
3582
3583         if (!a || a < Math.abs(c)) {
3584             a = c;
3585             var s = p / 4;
3586         }
3587         else {
3588             var s = p / (2 * Math.PI) * Math.asin(c / a);
3589         }
3590
3591         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3592     },
3593
3594
3595     elasticOut: function (t, b, c, d, a, p) {
3596         if (t == 0) {
3597             return b;
3598         }
3599         if ((t /= d) == 1) {
3600             return b + c;
3601         }
3602         if (!p) {
3603             p = d * .3;
3604         }
3605
3606         if (!a || a < Math.abs(c)) {
3607             a = c;
3608             var s = p / 4;
3609         }
3610         else {
3611             var s = p / (2 * Math.PI) * Math.asin(c / a);
3612         }
3613
3614         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3615     },
3616
3617
3618     elasticBoth: function (t, b, c, d, a, p) {
3619         if (t == 0) {
3620             return b;
3621         }
3622
3623         if ((t /= d / 2) == 2) {
3624             return b + c;
3625         }
3626
3627         if (!p) {
3628             p = d * (.3 * 1.5);
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         if (t < 1) {
3640             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3641                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3642         }
3643         return a * Math.pow(2, -10 * (t -= 1)) *
3644                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3645     },
3646
3647
3648
3649     backIn: function (t, b, c, d, s) {
3650         if (typeof s == 'undefined') {
3651             s = 1.70158;
3652         }
3653         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3654     },
3655
3656
3657     backOut: function (t, b, c, d, s) {
3658         if (typeof s == 'undefined') {
3659             s = 1.70158;
3660         }
3661         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3662     },
3663
3664
3665     backBoth: function (t, b, c, d, s) {
3666         if (typeof s == 'undefined') {
3667             s = 1.70158;
3668         }
3669
3670         if ((t /= d / 2 ) < 1) {
3671             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3672         }
3673         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3674     },
3675
3676
3677     bounceIn: function (t, b, c, d) {
3678         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3679     },
3680
3681
3682     bounceOut: function (t, b, c, d) {
3683         if ((t /= d) < (1 / 2.75)) {
3684             return c * (7.5625 * t * t) + b;
3685         } else if (t < (2 / 2.75)) {
3686             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3687         } else if (t < (2.5 / 2.75)) {
3688             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3689         }
3690         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3691     },
3692
3693
3694     bounceBoth: function (t, b, c, d) {
3695         if (t < d / 2) {
3696             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3697         }
3698         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3699     }
3700 };/*
3701  * Portions of this file are based on pieces of Yahoo User Interface Library
3702  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3703  * YUI licensed under the BSD License:
3704  * http://developer.yahoo.net/yui/license.txt
3705  * <script type="text/javascript">
3706  *
3707  */
3708     (function() {
3709         Roo.lib.Motion = function(el, attributes, duration, method) {
3710             if (el) {
3711                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3712             }
3713         };
3714
3715         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3716
3717
3718         var Y = Roo.lib;
3719         var superclass = Y.Motion.superclass;
3720         var proto = Y.Motion.prototype;
3721
3722         proto.toString = function() {
3723             var el = this.getEl();
3724             var id = el.id || el.tagName;
3725             return ("Motion " + id);
3726         };
3727
3728         proto.patterns.points = /^points$/i;
3729
3730         proto.setAttribute = function(attr, val, unit) {
3731             if (this.patterns.points.test(attr)) {
3732                 unit = unit || 'px';
3733                 superclass.setAttribute.call(this, 'left', val[0], unit);
3734                 superclass.setAttribute.call(this, 'top', val[1], unit);
3735             } else {
3736                 superclass.setAttribute.call(this, attr, val, unit);
3737             }
3738         };
3739
3740         proto.getAttribute = function(attr) {
3741             if (this.patterns.points.test(attr)) {
3742                 var val = [
3743                         superclass.getAttribute.call(this, 'left'),
3744                         superclass.getAttribute.call(this, 'top')
3745                         ];
3746             } else {
3747                 val = superclass.getAttribute.call(this, attr);
3748             }
3749
3750             return val;
3751         };
3752
3753         proto.doMethod = function(attr, start, end) {
3754             var val = null;
3755
3756             if (this.patterns.points.test(attr)) {
3757                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3758                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3759             } else {
3760                 val = superclass.doMethod.call(this, attr, start, end);
3761             }
3762             return val;
3763         };
3764
3765         proto.setRuntimeAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var el = this.getEl();
3768                 var attributes = this.attributes;
3769                 var start;
3770                 var control = attributes['points']['control'] || [];
3771                 var end;
3772                 var i, len;
3773
3774                 if (control.length > 0 && !(control[0] instanceof Array)) {
3775                     control = [control];
3776                 } else {
3777                     var tmp = [];
3778                     for (i = 0,len = control.length; i < len; ++i) {
3779                         tmp[i] = control[i];
3780                     }
3781                     control = tmp;
3782                 }
3783
3784                 Roo.fly(el).position();
3785
3786                 if (isset(attributes['points']['from'])) {
3787                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3788                 }
3789                 else {
3790                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3791                 }
3792
3793                 start = this.getAttribute('points');
3794
3795
3796                 if (isset(attributes['points']['to'])) {
3797                     end = translateValues.call(this, attributes['points']['to'], start);
3798
3799                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3800                     for (i = 0,len = control.length; i < len; ++i) {
3801                         control[i] = translateValues.call(this, control[i], start);
3802                     }
3803
3804
3805                 } else if (isset(attributes['points']['by'])) {
3806                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3807
3808                     for (i = 0,len = control.length; i < len; ++i) {
3809                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3810                     }
3811                 }
3812
3813                 this.runtimeAttributes[attr] = [start];
3814
3815                 if (control.length > 0) {
3816                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3817                 }
3818
3819                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3820             }
3821             else {
3822                 superclass.setRuntimeAttribute.call(this, attr);
3823             }
3824         };
3825
3826         var translateValues = function(val, start) {
3827             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3828             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3829
3830             return val;
3831         };
3832
3833         var isset = function(prop) {
3834             return (typeof prop !== 'undefined');
3835         };
3836     })();
3837 /*
3838  * Portions of this file are based on pieces of Yahoo User Interface Library
3839  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3840  * YUI licensed under the BSD License:
3841  * http://developer.yahoo.net/yui/license.txt
3842  * <script type="text/javascript">
3843  *
3844  */
3845     (function() {
3846         Roo.lib.Scroll = function(el, attributes, duration, method) {
3847             if (el) {
3848                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3849             }
3850         };
3851
3852         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3853
3854
3855         var Y = Roo.lib;
3856         var superclass = Y.Scroll.superclass;
3857         var proto = Y.Scroll.prototype;
3858
3859         proto.toString = function() {
3860             var el = this.getEl();
3861             var id = el.id || el.tagName;
3862             return ("Scroll " + id);
3863         };
3864
3865         proto.doMethod = function(attr, start, end) {
3866             var val = null;
3867
3868             if (attr == 'scroll') {
3869                 val = [
3870                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3871                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3872                         ];
3873
3874             } else {
3875                 val = superclass.doMethod.call(this, attr, start, end);
3876             }
3877             return val;
3878         };
3879
3880         proto.getAttribute = function(attr) {
3881             var val = null;
3882             var el = this.getEl();
3883
3884             if (attr == 'scroll') {
3885                 val = [ el.scrollLeft, el.scrollTop ];
3886             } else {
3887                 val = superclass.getAttribute.call(this, attr);
3888             }
3889
3890             return val;
3891         };
3892
3893         proto.setAttribute = function(attr, val, unit) {
3894             var el = this.getEl();
3895
3896             if (attr == 'scroll') {
3897                 el.scrollLeft = val[0];
3898                 el.scrollTop = val[1];
3899             } else {
3900                 superclass.setAttribute.call(this, attr, val, unit);
3901             }
3902         };
3903     })();
3904 /*
3905  * Based on:
3906  * Ext JS Library 1.1.1
3907  * Copyright(c) 2006-2007, Ext JS, LLC.
3908  *
3909  * Originally Released Under LGPL - original licence link has changed is not relivant.
3910  *
3911  * Fork - LGPL
3912  * <script type="text/javascript">
3913  */
3914  
3915
3916 /**
3917  * @class Roo.DomHelper
3918  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3919  * 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>.
3920  * @singleton
3921  */
3922 Roo.DomHelper = function(){
3923     var tempTableEl = null;
3924     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3925     var tableRe = /^table|tbody|tr|td$/i;
3926     var xmlns = {};
3927     // build as innerHTML where available
3928     /** @ignore */
3929     var createHtml = function(o){
3930         if(typeof o == 'string'){
3931             return o;
3932         }
3933         var b = "";
3934         if(!o.tag){
3935             o.tag = "div";
3936         }
3937         b += "<" + o.tag;
3938         for(var attr in o){
3939             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3940             if(attr == "style"){
3941                 var s = o["style"];
3942                 if(typeof s == "function"){
3943                     s = s.call();
3944                 }
3945                 if(typeof s == "string"){
3946                     b += ' style="' + s + '"';
3947                 }else if(typeof s == "object"){
3948                     b += ' style="';
3949                     for(var key in s){
3950                         if(typeof s[key] != "function"){
3951                             b += key + ":" + s[key] + ";";
3952                         }
3953                     }
3954                     b += '"';
3955                 }
3956             }else{
3957                 if(attr == "cls"){
3958                     b += ' class="' + o["cls"] + '"';
3959                 }else if(attr == "htmlFor"){
3960                     b += ' for="' + o["htmlFor"] + '"';
3961                 }else{
3962                     b += " " + attr + '="' + o[attr] + '"';
3963                 }
3964             }
3965         }
3966         if(emptyTags.test(o.tag)){
3967             b += "/>";
3968         }else{
3969             b += ">";
3970             var cn = o.children || o.cn;
3971             if(cn){
3972                 //http://bugs.kde.org/show_bug.cgi?id=71506
3973                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3974                     for(var i = 0, len = cn.length; i < len; i++) {
3975                         b += createHtml(cn[i], b);
3976                     }
3977                 }else{
3978                     b += createHtml(cn, b);
3979                 }
3980             }
3981             if(o.html){
3982                 b += o.html;
3983             }
3984             b += "</" + o.tag + ">";
3985         }
3986         return b;
3987     };
3988
3989     // build as dom
3990     /** @ignore */
3991     var createDom = function(o, parentNode){
3992          
3993         // defininition craeted..
3994         var ns = false;
3995         if (o.ns && o.ns != 'html') {
3996                
3997             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3998                 xmlns[o.ns] = o.xmlns;
3999                 ns = o.xmlns;
4000             }
4001             if (typeof(xmlns[o.ns]) == 'undefined') {
4002                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4003             }
4004             ns = xmlns[o.ns];
4005         }
4006         
4007         
4008         if (typeof(o) == 'string') {
4009             return parentNode.appendChild(document.createTextNode(o));
4010         }
4011         o.tag = o.tag || div;
4012         if (o.ns && Roo.isIE) {
4013             ns = false;
4014             o.tag = o.ns + ':' + o.tag;
4015             
4016         }
4017         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4018         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4019         for(var attr in o){
4020             
4021             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4022                     attr == "style" || typeof o[attr] == "function") continue;
4023                     
4024             if(attr=="cls" && Roo.isIE){
4025                 el.className = o["cls"];
4026             }else{
4027                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4028                 else el[attr] = o[attr];
4029             }
4030         }
4031         Roo.DomHelper.applyStyles(el, o.style);
4032         var cn = o.children || o.cn;
4033         if(cn){
4034             //http://bugs.kde.org/show_bug.cgi?id=71506
4035              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4036                 for(var i = 0, len = cn.length; i < len; i++) {
4037                     createDom(cn[i], el);
4038                 }
4039             }else{
4040                 createDom(cn, el);
4041             }
4042         }
4043         if(o.html){
4044             el.innerHTML = o.html;
4045         }
4046         if(parentNode){
4047            parentNode.appendChild(el);
4048         }
4049         return el;
4050     };
4051
4052     var ieTable = function(depth, s, h, e){
4053         tempTableEl.innerHTML = [s, h, e].join('');
4054         var i = -1, el = tempTableEl;
4055         while(++i < depth){
4056             el = el.firstChild;
4057         }
4058         return el;
4059     };
4060
4061     // kill repeat to save bytes
4062     var ts = '<table>',
4063         te = '</table>',
4064         tbs = ts+'<tbody>',
4065         tbe = '</tbody>'+te,
4066         trs = tbs + '<tr>',
4067         tre = '</tr>'+tbe;
4068
4069     /**
4070      * @ignore
4071      * Nasty code for IE's broken table implementation
4072      */
4073     var insertIntoTable = function(tag, where, el, html){
4074         if(!tempTableEl){
4075             tempTableEl = document.createElement('div');
4076         }
4077         var node;
4078         var before = null;
4079         if(tag == 'td'){
4080             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4081                 return;
4082             }
4083             if(where == 'beforebegin'){
4084                 before = el;
4085                 el = el.parentNode;
4086             } else{
4087                 before = el.nextSibling;
4088                 el = el.parentNode;
4089             }
4090             node = ieTable(4, trs, html, tre);
4091         }
4092         else if(tag == 'tr'){
4093             if(where == 'beforebegin'){
4094                 before = el;
4095                 el = el.parentNode;
4096                 node = ieTable(3, tbs, html, tbe);
4097             } else if(where == 'afterend'){
4098                 before = el.nextSibling;
4099                 el = el.parentNode;
4100                 node = ieTable(3, tbs, html, tbe);
4101             } else{ // INTO a TR
4102                 if(where == 'afterbegin'){
4103                     before = el.firstChild;
4104                 }
4105                 node = ieTable(4, trs, html, tre);
4106             }
4107         } else if(tag == 'tbody'){
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111                 node = ieTable(2, ts, html, te);
4112             } else if(where == 'afterend'){
4113                 before = el.nextSibling;
4114                 el = el.parentNode;
4115                 node = ieTable(2, ts, html, te);
4116             } else{
4117                 if(where == 'afterbegin'){
4118                     before = el.firstChild;
4119                 }
4120                 node = ieTable(3, tbs, html, tbe);
4121             }
4122         } else{ // TABLE
4123             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4124                 return;
4125             }
4126             if(where == 'afterbegin'){
4127                 before = el.firstChild;
4128             }
4129             node = ieTable(2, ts, html, te);
4130         }
4131         el.insertBefore(node, before);
4132         return node;
4133     };
4134
4135     return {
4136     /** True to force the use of DOM instead of html fragments @type Boolean */
4137     useDom : false,
4138
4139     /**
4140      * Returns the markup for the passed Element(s) config
4141      * @param {Object} o The Dom object spec (and children)
4142      * @return {String}
4143      */
4144     markup : function(o){
4145         return createHtml(o);
4146     },
4147
4148     /**
4149      * Applies a style specification to an element
4150      * @param {String/HTMLElement} el The element to apply styles to
4151      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4152      * a function which returns such a specification.
4153      */
4154     applyStyles : function(el, styles){
4155         if(styles){
4156            el = Roo.fly(el);
4157            if(typeof styles == "string"){
4158                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4159                var matches;
4160                while ((matches = re.exec(styles)) != null){
4161                    el.setStyle(matches[1], matches[2]);
4162                }
4163            }else if (typeof styles == "object"){
4164                for (var style in styles){
4165                   el.setStyle(style, styles[style]);
4166                }
4167            }else if (typeof styles == "function"){
4168                 Roo.DomHelper.applyStyles(el, styles.call());
4169            }
4170         }
4171     },
4172
4173     /**
4174      * Inserts an HTML fragment into the Dom
4175      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4176      * @param {HTMLElement} el The context element
4177      * @param {String} html The HTML fragmenet
4178      * @return {HTMLElement} The new node
4179      */
4180     insertHtml : function(where, el, html){
4181         where = where.toLowerCase();
4182         if(el.insertAdjacentHTML){
4183             if(tableRe.test(el.tagName)){
4184                 var rs;
4185                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4186                     return rs;
4187                 }
4188             }
4189             switch(where){
4190                 case "beforebegin":
4191                     el.insertAdjacentHTML('BeforeBegin', html);
4192                     return el.previousSibling;
4193                 case "afterbegin":
4194                     el.insertAdjacentHTML('AfterBegin', html);
4195                     return el.firstChild;
4196                 case "beforeend":
4197                     el.insertAdjacentHTML('BeforeEnd', html);
4198                     return el.lastChild;
4199                 case "afterend":
4200                     el.insertAdjacentHTML('AfterEnd', html);
4201                     return el.nextSibling;
4202             }
4203             throw 'Illegal insertion point -> "' + where + '"';
4204         }
4205         var range = el.ownerDocument.createRange();
4206         var frag;
4207         switch(where){
4208              case "beforebegin":
4209                 range.setStartBefore(el);
4210                 frag = range.createContextualFragment(html);
4211                 el.parentNode.insertBefore(frag, el);
4212                 return el.previousSibling;
4213              case "afterbegin":
4214                 if(el.firstChild){
4215                     range.setStartBefore(el.firstChild);
4216                     frag = range.createContextualFragment(html);
4217                     el.insertBefore(frag, el.firstChild);
4218                     return el.firstChild;
4219                 }else{
4220                     el.innerHTML = html;
4221                     return el.firstChild;
4222                 }
4223             case "beforeend":
4224                 if(el.lastChild){
4225                     range.setStartAfter(el.lastChild);
4226                     frag = range.createContextualFragment(html);
4227                     el.appendChild(frag);
4228                     return el.lastChild;
4229                 }else{
4230                     el.innerHTML = html;
4231                     return el.lastChild;
4232                 }
4233             case "afterend":
4234                 range.setStartAfter(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el.nextSibling);
4237                 return el.nextSibling;
4238             }
4239             throw 'Illegal insertion point -> "' + where + '"';
4240     },
4241
4242     /**
4243      * Creates new Dom element(s) and inserts them before el
4244      * @param {String/HTMLElement/Element} el The context element
4245      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4246      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4247      * @return {HTMLElement/Roo.Element} The new node
4248      */
4249     insertBefore : function(el, o, returnElement){
4250         return this.doInsert(el, o, returnElement, "beforeBegin");
4251     },
4252
4253     /**
4254      * Creates new Dom element(s) and inserts them after el
4255      * @param {String/HTMLElement/Element} el The context element
4256      * @param {Object} o The Dom object spec (and children)
4257      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4258      * @return {HTMLElement/Roo.Element} The new node
4259      */
4260     insertAfter : function(el, o, returnElement){
4261         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4262     },
4263
4264     /**
4265      * Creates new Dom element(s) and inserts them as the first child of el
4266      * @param {String/HTMLElement/Element} el The context element
4267      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4268      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4269      * @return {HTMLElement/Roo.Element} The new node
4270      */
4271     insertFirst : function(el, o, returnElement){
4272         return this.doInsert(el, o, returnElement, "afterBegin");
4273     },
4274
4275     // private
4276     doInsert : function(el, o, returnElement, pos, sibling){
4277         el = Roo.getDom(el);
4278         var newNode;
4279         if(this.useDom || o.ns){
4280             newNode = createDom(o, null);
4281             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4282         }else{
4283             var html = createHtml(o);
4284             newNode = this.insertHtml(pos, el, html);
4285         }
4286         return returnElement ? Roo.get(newNode, true) : newNode;
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and appends them to el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     append : function(el, o, returnElement){
4297         el = Roo.getDom(el);
4298         var newNode;
4299         if(this.useDom || o.ns){
4300             newNode = createDom(o, null);
4301             el.appendChild(newNode);
4302         }else{
4303             var html = createHtml(o);
4304             newNode = this.insertHtml("beforeEnd", el, html);
4305         }
4306         return returnElement ? Roo.get(newNode, true) : newNode;
4307     },
4308
4309     /**
4310      * Creates new Dom element(s) and overwrites the contents of el with them
4311      * @param {String/HTMLElement/Element} el The context element
4312      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4313      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4314      * @return {HTMLElement/Roo.Element} The new node
4315      */
4316     overwrite : function(el, o, returnElement){
4317         el = Roo.getDom(el);
4318         if (o.ns) {
4319           
4320             while (el.childNodes.length) {
4321                 el.removeChild(el.firstChild);
4322             }
4323             createDom(o, el);
4324         } else {
4325             el.innerHTML = createHtml(o);   
4326         }
4327         
4328         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4329     },
4330
4331     /**
4332      * Creates a new Roo.DomHelper.Template from the Dom object spec
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {Roo.DomHelper.Template} The new template
4335      */
4336     createTemplate : function(o){
4337         var html = createHtml(o);
4338         return new Roo.Template(html);
4339     }
4340     };
4341 }();
4342 /*
4343  * Based on:
4344  * Ext JS Library 1.1.1
4345  * Copyright(c) 2006-2007, Ext JS, LLC.
4346  *
4347  * Originally Released Under LGPL - original licence link has changed is not relivant.
4348  *
4349  * Fork - LGPL
4350  * <script type="text/javascript">
4351  */
4352  
4353 /**
4354 * @class Roo.Template
4355 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4356 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4357 * Usage:
4358 <pre><code>
4359 var t = new Roo.Template(
4360     '&lt;div name="{id}"&gt;',
4361         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4362     '&lt;/div&gt;'
4363 );
4364 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4365 </code></pre>
4366 * 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>. 
4367 * @constructor
4368 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4369 */
4370 Roo.Template = function(html){
4371     if(html instanceof Array){
4372         html = html.join("");
4373     }else if(arguments.length > 1){
4374         html = Array.prototype.join.call(arguments, "");
4375     }
4376     /**@private*/
4377     this.html = html;
4378     
4379 };
4380 Roo.Template.prototype = {
4381     /**
4382      * Returns an HTML fragment of this template with the specified values applied.
4383      * @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'})
4384      * @return {String} The HTML fragment
4385      */
4386     applyTemplate : function(values){
4387         if(this.compiled){
4388             return this.compiled(values);
4389         }
4390         var useF = this.disableFormats !== true;
4391         var fm = Roo.util.Format, tpl = this;
4392         var fn = function(m, name, format, args){
4393             if(format && useF){
4394                 if(format.substr(0, 5) == "this."){
4395                     return tpl.call(format.substr(5), values[name], values);
4396                 }else{
4397                     if(args){
4398                         // quoted values are required for strings in compiled templates, 
4399                         // but for non compiled we need to strip them
4400                         // quoted reversed for jsmin
4401                         var re = /^\s*['"](.*)["']\s*$/;
4402                         args = args.split(',');
4403                         for(var i = 0, len = args.length; i < len; i++){
4404                             args[i] = args[i].replace(re, "$1");
4405                         }
4406                         args = [values[name]].concat(args);
4407                     }else{
4408                         args = [values[name]];
4409                     }
4410                     return fm[format].apply(fm, args);
4411                 }
4412             }else{
4413                 return values[name] !== undefined ? values[name] : "";
4414             }
4415         };
4416         return this.html.replace(this.re, fn);
4417     },
4418     
4419     /**
4420      * Sets the HTML used as the template and optionally compiles it.
4421      * @param {String} html
4422      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4423      * @return {Roo.Template} this
4424      */
4425     set : function(html, compile){
4426         this.html = html;
4427         this.compiled = null;
4428         if(compile){
4429             this.compile();
4430         }
4431         return this;
4432     },
4433     
4434     /**
4435      * True to disable format functions (defaults to false)
4436      * @type Boolean
4437      */
4438     disableFormats : false,
4439     
4440     /**
4441     * The regular expression used to match template variables 
4442     * @type RegExp
4443     * @property 
4444     */
4445     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4446     
4447     /**
4448      * Compiles the template into an internal function, eliminating the RegEx overhead.
4449      * @return {Roo.Template} this
4450      */
4451     compile : function(){
4452         var fm = Roo.util.Format;
4453         var useF = this.disableFormats !== true;
4454         var sep = Roo.isGecko ? "+" : ",";
4455         var fn = function(m, name, format, args){
4456             if(format && useF){
4457                 args = args ? ',' + args : "";
4458                 if(format.substr(0, 5) != "this."){
4459                     format = "fm." + format + '(';
4460                 }else{
4461                     format = 'this.call("'+ format.substr(5) + '", ';
4462                     args = ", values";
4463                 }
4464             }else{
4465                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4466             }
4467             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4468         };
4469         var body;
4470         // branched to use + in gecko and [].join() in others
4471         if(Roo.isGecko){
4472             body = "this.compiled = function(values){ return '" +
4473                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4474                     "';};";
4475         }else{
4476             body = ["this.compiled = function(values){ return ['"];
4477             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4478             body.push("'].join('');};");
4479             body = body.join('');
4480         }
4481         /**
4482          * eval:var:values
4483          * eval:var:fm
4484          */
4485         eval(body);
4486         return this;
4487     },
4488     
4489     // private function used to call members
4490     call : function(fnName, value, allValues){
4491         return this[fnName](value, allValues);
4492     },
4493     
4494     /**
4495      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4496      * @param {String/HTMLElement/Roo.Element} el The context element
4497      * @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'})
4498      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4499      * @return {HTMLElement/Roo.Element} The new node or Element
4500      */
4501     insertFirst: function(el, values, returnElement){
4502         return this.doInsert('afterBegin', el, values, returnElement);
4503     },
4504
4505     /**
4506      * Applies the supplied values to the template and inserts the new node(s) before el.
4507      * @param {String/HTMLElement/Roo.Element} el The context element
4508      * @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'})
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4510      * @return {HTMLElement/Roo.Element} The new node or Element
4511      */
4512     insertBefore: function(el, values, returnElement){
4513         return this.doInsert('beforeBegin', el, values, returnElement);
4514     },
4515
4516     /**
4517      * Applies the supplied values to the template and inserts the new node(s) after el.
4518      * @param {String/HTMLElement/Roo.Element} el The context element
4519      * @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'})
4520      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4521      * @return {HTMLElement/Roo.Element} The new node or Element
4522      */
4523     insertAfter : function(el, values, returnElement){
4524         return this.doInsert('afterEnd', el, values, returnElement);
4525     },
4526     
4527     /**
4528      * Applies the supplied values to the template and appends the new node(s) to el.
4529      * @param {String/HTMLElement/Roo.Element} el The context element
4530      * @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'})
4531      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4532      * @return {HTMLElement/Roo.Element} The new node or Element
4533      */
4534     append : function(el, values, returnElement){
4535         return this.doInsert('beforeEnd', el, values, returnElement);
4536     },
4537
4538     doInsert : function(where, el, values, returnEl){
4539         el = Roo.getDom(el);
4540         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4541         return returnEl ? Roo.get(newNode, true) : newNode;
4542     },
4543
4544     /**
4545      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4546      * @param {String/HTMLElement/Roo.Element} el The context element
4547      * @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'})
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4549      * @return {HTMLElement/Roo.Element} The new node or Element
4550      */
4551     overwrite : function(el, values, returnElement){
4552         el = Roo.getDom(el);
4553         el.innerHTML = this.applyTemplate(values);
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     }
4556 };
4557 /**
4558  * Alias for {@link #applyTemplate}
4559  * @method
4560  */
4561 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4562
4563 // backwards compat
4564 Roo.DomHelper.Template = Roo.Template;
4565
4566 /**
4567  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4568  * @param {String/HTMLElement} el A DOM element or its id
4569  * @returns {Roo.Template} The created template
4570  * @static
4571  */
4572 Roo.Template.from = function(el){
4573     el = Roo.getDom(el);
4574     return new Roo.Template(el.value || el.innerHTML);
4575 };/*
4576  * Based on:
4577  * Ext JS Library 1.1.1
4578  * Copyright(c) 2006-2007, Ext JS, LLC.
4579  *
4580  * Originally Released Under LGPL - original licence link has changed is not relivant.
4581  *
4582  * Fork - LGPL
4583  * <script type="text/javascript">
4584  */
4585  
4586
4587 /*
4588  * This is code is also distributed under MIT license for use
4589  * with jQuery and prototype JavaScript libraries.
4590  */
4591 /**
4592  * @class Roo.DomQuery
4593 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).
4594 <p>
4595 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>
4596
4597 <p>
4598 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.
4599 </p>
4600 <h4>Element Selectors:</h4>
4601 <ul class="list">
4602     <li> <b>*</b> any element</li>
4603     <li> <b>E</b> an element with the tag E</li>
4604     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4605     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4606     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4607     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4608 </ul>
4609 <h4>Attribute Selectors:</h4>
4610 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4611 <ul class="list">
4612     <li> <b>E[foo]</b> has an attribute "foo"</li>
4613     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4614     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4615     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4616     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4617     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4618     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4619 </ul>
4620 <h4>Pseudo Classes:</h4>
4621 <ul class="list">
4622     <li> <b>E:first-child</b> E is the first child of its parent</li>
4623     <li> <b>E:last-child</b> E is the last child of its parent</li>
4624     <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>
4625     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4626     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4627     <li> <b>E:only-child</b> E is the only child of its parent</li>
4628     <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>
4629     <li> <b>E:first</b> the first E in the resultset</li>
4630     <li> <b>E:last</b> the last E in the resultset</li>
4631     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4632     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4633     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4634     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4635     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4636     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4637     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4638     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4639     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4640 </ul>
4641 <h4>CSS Value Selectors:</h4>
4642 <ul class="list">
4643     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4644     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4645     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4646     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4647     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4648     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4649 </ul>
4650  * @singleton
4651  */
4652 Roo.DomQuery = function(){
4653     var cache = {}, simpleCache = {}, valueCache = {};
4654     var nonSpace = /\S/;
4655     var trimRe = /^\s+|\s+$/g;
4656     var tplRe = /\{(\d+)\}/g;
4657     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4658     var tagTokenRe = /^(#)?([\w-\*]+)/;
4659     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4660
4661     function child(p, index){
4662         var i = 0;
4663         var n = p.firstChild;
4664         while(n){
4665             if(n.nodeType == 1){
4666                if(++i == index){
4667                    return n;
4668                }
4669             }
4670             n = n.nextSibling;
4671         }
4672         return null;
4673     };
4674
4675     function next(n){
4676         while((n = n.nextSibling) && n.nodeType != 1);
4677         return n;
4678     };
4679
4680     function prev(n){
4681         while((n = n.previousSibling) && n.nodeType != 1);
4682         return n;
4683     };
4684
4685     function children(d){
4686         var n = d.firstChild, ni = -1;
4687             while(n){
4688                 var nx = n.nextSibling;
4689                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4690                     d.removeChild(n);
4691                 }else{
4692                     n.nodeIndex = ++ni;
4693                 }
4694                 n = nx;
4695             }
4696             return this;
4697         };
4698
4699     function byClassName(c, a, v){
4700         if(!v){
4701             return c;
4702         }
4703         var r = [], ri = -1, cn;
4704         for(var i = 0, ci; ci = c[i]; i++){
4705             if((' '+ci.className+' ').indexOf(v) != -1){
4706                 r[++ri] = ci;
4707             }
4708         }
4709         return r;
4710     };
4711
4712     function attrValue(n, attr){
4713         if(!n.tagName && typeof n.length != "undefined"){
4714             n = n[0];
4715         }
4716         if(!n){
4717             return null;
4718         }
4719         if(attr == "for"){
4720             return n.htmlFor;
4721         }
4722         if(attr == "class" || attr == "className"){
4723             return n.className;
4724         }
4725         return n.getAttribute(attr) || n[attr];
4726
4727     };
4728
4729     function getNodes(ns, mode, tagName){
4730         var result = [], ri = -1, cs;
4731         if(!ns){
4732             return result;
4733         }
4734         tagName = tagName || "*";
4735         if(typeof ns.getElementsByTagName != "undefined"){
4736             ns = [ns];
4737         }
4738         if(!mode){
4739             for(var i = 0, ni; ni = ns[i]; i++){
4740                 cs = ni.getElementsByTagName(tagName);
4741                 for(var j = 0, ci; ci = cs[j]; j++){
4742                     result[++ri] = ci;
4743                 }
4744             }
4745         }else if(mode == "/" || mode == ">"){
4746             var utag = tagName.toUpperCase();
4747             for(var i = 0, ni, cn; ni = ns[i]; i++){
4748                 cn = ni.children || ni.childNodes;
4749                 for(var j = 0, cj; cj = cn[j]; j++){
4750                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4751                         result[++ri] = cj;
4752                     }
4753                 }
4754             }
4755         }else if(mode == "+"){
4756             var utag = tagName.toUpperCase();
4757             for(var i = 0, n; n = ns[i]; i++){
4758                 while((n = n.nextSibling) && n.nodeType != 1);
4759                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4760                     result[++ri] = n;
4761                 }
4762             }
4763         }else if(mode == "~"){
4764             for(var i = 0, n; n = ns[i]; i++){
4765                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4766                 if(n){
4767                     result[++ri] = n;
4768                 }
4769             }
4770         }
4771         return result;
4772     };
4773
4774     function concat(a, b){
4775         if(b.slice){
4776             return a.concat(b);
4777         }
4778         for(var i = 0, l = b.length; i < l; i++){
4779             a[a.length] = b[i];
4780         }
4781         return a;
4782     }
4783
4784     function byTag(cs, tagName){
4785         if(cs.tagName || cs == document){
4786             cs = [cs];
4787         }
4788         if(!tagName){
4789             return cs;
4790         }
4791         var r = [], ri = -1;
4792         tagName = tagName.toLowerCase();
4793         for(var i = 0, ci; ci = cs[i]; i++){
4794             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4795                 r[++ri] = ci;
4796             }
4797         }
4798         return r;
4799     };
4800
4801     function byId(cs, attr, id){
4802         if(cs.tagName || cs == document){
4803             cs = [cs];
4804         }
4805         if(!id){
4806             return cs;
4807         }
4808         var r = [], ri = -1;
4809         for(var i = 0,ci; ci = cs[i]; i++){
4810             if(ci && ci.id == id){
4811                 r[++ri] = ci;
4812                 return r;
4813             }
4814         }
4815         return r;
4816     };
4817
4818     function byAttribute(cs, attr, value, op, custom){
4819         var r = [], ri = -1, st = custom=="{";
4820         var f = Roo.DomQuery.operators[op];
4821         for(var i = 0, ci; ci = cs[i]; i++){
4822             var a;
4823             if(st){
4824                 a = Roo.DomQuery.getStyle(ci, attr);
4825             }
4826             else if(attr == "class" || attr == "className"){
4827                 a = ci.className;
4828             }else if(attr == "for"){
4829                 a = ci.htmlFor;
4830             }else if(attr == "href"){
4831                 a = ci.getAttribute("href", 2);
4832             }else{
4833                 a = ci.getAttribute(attr);
4834             }
4835             if((f && f(a, value)) || (!f && a)){
4836                 r[++ri] = ci;
4837             }
4838         }
4839         return r;
4840     };
4841
4842     function byPseudo(cs, name, value){
4843         return Roo.DomQuery.pseudos[name](cs, value);
4844     };
4845
4846     // This is for IE MSXML which does not support expandos.
4847     // IE runs the same speed using setAttribute, however FF slows way down
4848     // and Safari completely fails so they need to continue to use expandos.
4849     var isIE = window.ActiveXObject ? true : false;
4850
4851     // this eval is stop the compressor from
4852     // renaming the variable to something shorter
4853     
4854     /** eval:var:batch */
4855     var batch = 30803; 
4856
4857     var key = 30803;
4858
4859     function nodupIEXml(cs){
4860         var d = ++key;
4861         cs[0].setAttribute("_nodup", d);
4862         var r = [cs[0]];
4863         for(var i = 1, len = cs.length; i < len; i++){
4864             var c = cs[i];
4865             if(!c.getAttribute("_nodup") != d){
4866                 c.setAttribute("_nodup", d);
4867                 r[r.length] = c;
4868             }
4869         }
4870         for(var i = 0, len = cs.length; i < len; i++){
4871             cs[i].removeAttribute("_nodup");
4872         }
4873         return r;
4874     }
4875
4876     function nodup(cs){
4877         if(!cs){
4878             return [];
4879         }
4880         var len = cs.length, c, i, r = cs, cj, ri = -1;
4881         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4882             return cs;
4883         }
4884         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4885             return nodupIEXml(cs);
4886         }
4887         var d = ++key;
4888         cs[0]._nodup = d;
4889         for(i = 1; c = cs[i]; i++){
4890             if(c._nodup != d){
4891                 c._nodup = d;
4892             }else{
4893                 r = [];
4894                 for(var j = 0; j < i; j++){
4895                     r[++ri] = cs[j];
4896                 }
4897                 for(j = i+1; cj = cs[j]; j++){
4898                     if(cj._nodup != d){
4899                         cj._nodup = d;
4900                         r[++ri] = cj;
4901                     }
4902                 }
4903                 return r;
4904             }
4905         }
4906         return r;
4907     }
4908
4909     function quickDiffIEXml(c1, c2){
4910         var d = ++key;
4911         for(var i = 0, len = c1.length; i < len; i++){
4912             c1[i].setAttribute("_qdiff", d);
4913         }
4914         var r = [];
4915         for(var i = 0, len = c2.length; i < len; i++){
4916             if(c2[i].getAttribute("_qdiff") != d){
4917                 r[r.length] = c2[i];
4918             }
4919         }
4920         for(var i = 0, len = c1.length; i < len; i++){
4921            c1[i].removeAttribute("_qdiff");
4922         }
4923         return r;
4924     }
4925
4926     function quickDiff(c1, c2){
4927         var len1 = c1.length;
4928         if(!len1){
4929             return c2;
4930         }
4931         if(isIE && c1[0].selectSingleNode){
4932             return quickDiffIEXml(c1, c2);
4933         }
4934         var d = ++key;
4935         for(var i = 0; i < len1; i++){
4936             c1[i]._qdiff = d;
4937         }
4938         var r = [];
4939         for(var i = 0, len = c2.length; i < len; i++){
4940             if(c2[i]._qdiff != d){
4941                 r[r.length] = c2[i];
4942             }
4943         }
4944         return r;
4945     }
4946
4947     function quickId(ns, mode, root, id){
4948         if(ns == root){
4949            var d = root.ownerDocument || root;
4950            return d.getElementById(id);
4951         }
4952         ns = getNodes(ns, mode, "*");
4953         return byId(ns, null, id);
4954     }
4955
4956     return {
4957         getStyle : function(el, name){
4958             return Roo.fly(el).getStyle(name);
4959         },
4960         /**
4961          * Compiles a selector/xpath query into a reusable function. The returned function
4962          * takes one parameter "root" (optional), which is the context node from where the query should start.
4963          * @param {String} selector The selector/xpath query
4964          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4965          * @return {Function}
4966          */
4967         compile : function(path, type){
4968             type = type || "select";
4969             
4970             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4971             var q = path, mode, lq;
4972             var tk = Roo.DomQuery.matchers;
4973             var tklen = tk.length;
4974             var mm;
4975
4976             // accept leading mode switch
4977             var lmode = q.match(modeRe);
4978             if(lmode && lmode[1]){
4979                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4980                 q = q.replace(lmode[1], "");
4981             }
4982             // strip leading slashes
4983             while(path.substr(0, 1)=="/"){
4984                 path = path.substr(1);
4985             }
4986
4987             while(q && lq != q){
4988                 lq = q;
4989                 var tm = q.match(tagTokenRe);
4990                 if(type == "select"){
4991                     if(tm){
4992                         if(tm[1] == "#"){
4993                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4994                         }else{
4995                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4996                         }
4997                         q = q.replace(tm[0], "");
4998                     }else if(q.substr(0, 1) != '@'){
4999                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5000                     }
5001                 }else{
5002                     if(tm){
5003                         if(tm[1] == "#"){
5004                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5005                         }else{
5006                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5007                         }
5008                         q = q.replace(tm[0], "");
5009                     }
5010                 }
5011                 while(!(mm = q.match(modeRe))){
5012                     var matched = false;
5013                     for(var j = 0; j < tklen; j++){
5014                         var t = tk[j];
5015                         var m = q.match(t.re);
5016                         if(m){
5017                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5018                                                     return m[i];
5019                                                 });
5020                             q = q.replace(m[0], "");
5021                             matched = true;
5022                             break;
5023                         }
5024                     }
5025                     // prevent infinite loop on bad selector
5026                     if(!matched){
5027                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5028                     }
5029                 }
5030                 if(mm[1]){
5031                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5032                     q = q.replace(mm[1], "");
5033                 }
5034             }
5035             fn[fn.length] = "return nodup(n);\n}";
5036             
5037              /** 
5038               * list of variables that need from compression as they are used by eval.
5039              *  eval:var:batch 
5040              *  eval:var:nodup
5041              *  eval:var:byTag
5042              *  eval:var:ById
5043              *  eval:var:getNodes
5044              *  eval:var:quickId
5045              *  eval:var:mode
5046              *  eval:var:root
5047              *  eval:var:n
5048              *  eval:var:byClassName
5049              *  eval:var:byPseudo
5050              *  eval:var:byAttribute
5051              *  eval:var:attrValue
5052              * 
5053              **/ 
5054             eval(fn.join(""));
5055             return f;
5056         },
5057
5058         /**
5059          * Selects a group of elements.
5060          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5061          * @param {Node} root (optional) The start of the query (defaults to document).
5062          * @return {Array}
5063          */
5064         select : function(path, root, type){
5065             if(!root || root == document){
5066                 root = document;
5067             }
5068             if(typeof root == "string"){
5069                 root = document.getElementById(root);
5070             }
5071             var paths = path.split(",");
5072             var results = [];
5073             for(var i = 0, len = paths.length; i < len; i++){
5074                 var p = paths[i].replace(trimRe, "");
5075                 if(!cache[p]){
5076                     cache[p] = Roo.DomQuery.compile(p);
5077                     if(!cache[p]){
5078                         throw p + " is not a valid selector";
5079                     }
5080                 }
5081                 var result = cache[p](root);
5082                 if(result && result != document){
5083                     results = results.concat(result);
5084                 }
5085             }
5086             if(paths.length > 1){
5087                 return nodup(results);
5088             }
5089             return results;
5090         },
5091
5092         /**
5093          * Selects a single element.
5094          * @param {String} selector The selector/xpath query
5095          * @param {Node} root (optional) The start of the query (defaults to document).
5096          * @return {Element}
5097          */
5098         selectNode : function(path, root){
5099             return Roo.DomQuery.select(path, root)[0];
5100         },
5101
5102         /**
5103          * Selects the value of a node, optionally replacing null with the defaultValue.
5104          * @param {String} selector The selector/xpath query
5105          * @param {Node} root (optional) The start of the query (defaults to document).
5106          * @param {String} defaultValue
5107          */
5108         selectValue : function(path, root, defaultValue){
5109             path = path.replace(trimRe, "");
5110             if(!valueCache[path]){
5111                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5112             }
5113             var n = valueCache[path](root);
5114             n = n[0] ? n[0] : n;
5115             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5116             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5117         },
5118
5119         /**
5120          * Selects the value of a node, parsing integers and floats.
5121          * @param {String} selector The selector/xpath query
5122          * @param {Node} root (optional) The start of the query (defaults to document).
5123          * @param {Number} defaultValue
5124          * @return {Number}
5125          */
5126         selectNumber : function(path, root, defaultValue){
5127             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5128             return parseFloat(v);
5129         },
5130
5131         /**
5132          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5133          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5134          * @param {String} selector The simple selector to test
5135          * @return {Boolean}
5136          */
5137         is : function(el, ss){
5138             if(typeof el == "string"){
5139                 el = document.getElementById(el);
5140             }
5141             var isArray = (el instanceof Array);
5142             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5143             return isArray ? (result.length == el.length) : (result.length > 0);
5144         },
5145
5146         /**
5147          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5148          * @param {Array} el An array of elements to filter
5149          * @param {String} selector The simple selector to test
5150          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5151          * the selector instead of the ones that match
5152          * @return {Array}
5153          */
5154         filter : function(els, ss, nonMatches){
5155             ss = ss.replace(trimRe, "");
5156             if(!simpleCache[ss]){
5157                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5158             }
5159             var result = simpleCache[ss](els);
5160             return nonMatches ? quickDiff(result, els) : result;
5161         },
5162
5163         /**
5164          * Collection of matching regular expressions and code snippets.
5165          */
5166         matchers : [{
5167                 re: /^\.([\w-]+)/,
5168                 select: 'n = byClassName(n, null, " {1} ");'
5169             }, {
5170                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5171                 select: 'n = byPseudo(n, "{1}", "{2}");'
5172             },{
5173                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5174                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5175             }, {
5176                 re: /^#([\w-]+)/,
5177                 select: 'n = byId(n, null, "{1}");'
5178             },{
5179                 re: /^@([\w-]+)/,
5180                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5181             }
5182         ],
5183
5184         /**
5185          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5186          * 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;.
5187          */
5188         operators : {
5189             "=" : function(a, v){
5190                 return a == v;
5191             },
5192             "!=" : function(a, v){
5193                 return a != v;
5194             },
5195             "^=" : function(a, v){
5196                 return a && a.substr(0, v.length) == v;
5197             },
5198             "$=" : function(a, v){
5199                 return a && a.substr(a.length-v.length) == v;
5200             },
5201             "*=" : function(a, v){
5202                 return a && a.indexOf(v) !== -1;
5203             },
5204             "%=" : function(a, v){
5205                 return (a % v) == 0;
5206             },
5207             "|=" : function(a, v){
5208                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5209             },
5210             "~=" : function(a, v){
5211                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5212             }
5213         },
5214
5215         /**
5216          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5217          * and the argument (if any) supplied in the selector.
5218          */
5219         pseudos : {
5220             "first-child" : function(c){
5221                 var r = [], ri = -1, n;
5222                 for(var i = 0, ci; ci = n = c[i]; i++){
5223                     while((n = n.previousSibling) && n.nodeType != 1);
5224                     if(!n){
5225                         r[++ri] = ci;
5226                     }
5227                 }
5228                 return r;
5229             },
5230
5231             "last-child" : function(c){
5232                 var r = [], ri = -1, n;
5233                 for(var i = 0, ci; ci = n = c[i]; i++){
5234                     while((n = n.nextSibling) && n.nodeType != 1);
5235                     if(!n){
5236                         r[++ri] = ci;
5237                     }
5238                 }
5239                 return r;
5240             },
5241
5242             "nth-child" : function(c, a) {
5243                 var r = [], ri = -1;
5244                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5245                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5246                 for(var i = 0, n; n = c[i]; i++){
5247                     var pn = n.parentNode;
5248                     if (batch != pn._batch) {
5249                         var j = 0;
5250                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5251                             if(cn.nodeType == 1){
5252                                cn.nodeIndex = ++j;
5253                             }
5254                         }
5255                         pn._batch = batch;
5256                     }
5257                     if (f == 1) {
5258                         if (l == 0 || n.nodeIndex == l){
5259                             r[++ri] = n;
5260                         }
5261                     } else if ((n.nodeIndex + l) % f == 0){
5262                         r[++ri] = n;
5263                     }
5264                 }
5265
5266                 return r;
5267             },
5268
5269             "only-child" : function(c){
5270                 var r = [], ri = -1;;
5271                 for(var i = 0, ci; ci = c[i]; i++){
5272                     if(!prev(ci) && !next(ci)){
5273                         r[++ri] = ci;
5274                     }
5275                 }
5276                 return r;
5277             },
5278
5279             "empty" : function(c){
5280                 var r = [], ri = -1;
5281                 for(var i = 0, ci; ci = c[i]; i++){
5282                     var cns = ci.childNodes, j = 0, cn, empty = true;
5283                     while(cn = cns[j]){
5284                         ++j;
5285                         if(cn.nodeType == 1 || cn.nodeType == 3){
5286                             empty = false;
5287                             break;
5288                         }
5289                     }
5290                     if(empty){
5291                         r[++ri] = ci;
5292                     }
5293                 }
5294                 return r;
5295             },
5296
5297             "contains" : function(c, v){
5298                 var r = [], ri = -1;
5299                 for(var i = 0, ci; ci = c[i]; i++){
5300                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5301                         r[++ri] = ci;
5302                     }
5303                 }
5304                 return r;
5305             },
5306
5307             "nodeValue" : function(c, v){
5308                 var r = [], ri = -1;
5309                 for(var i = 0, ci; ci = c[i]; i++){
5310                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5311                         r[++ri] = ci;
5312                     }
5313                 }
5314                 return r;
5315             },
5316
5317             "checked" : function(c){
5318                 var r = [], ri = -1;
5319                 for(var i = 0, ci; ci = c[i]; i++){
5320                     if(ci.checked == true){
5321                         r[++ri] = ci;
5322                     }
5323                 }
5324                 return r;
5325             },
5326
5327             "not" : function(c, ss){
5328                 return Roo.DomQuery.filter(c, ss, true);
5329             },
5330
5331             "odd" : function(c){
5332                 return this["nth-child"](c, "odd");
5333             },
5334
5335             "even" : function(c){
5336                 return this["nth-child"](c, "even");
5337             },
5338
5339             "nth" : function(c, a){
5340                 return c[a-1] || [];
5341             },
5342
5343             "first" : function(c){
5344                 return c[0] || [];
5345             },
5346
5347             "last" : function(c){
5348                 return c[c.length-1] || [];
5349             },
5350
5351             "has" : function(c, ss){
5352                 var s = Roo.DomQuery.select;
5353                 var r = [], ri = -1;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(s(ss, ci).length > 0){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "next" : function(c, ss){
5363                 var is = Roo.DomQuery.is;
5364                 var r = [], ri = -1;
5365                 for(var i = 0, ci; ci = c[i]; i++){
5366                     var n = next(ci);
5367                     if(n && is(n, ss)){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "prev" : function(c, ss){
5375                 var is = Roo.DomQuery.is;
5376                 var r = [], ri = -1;
5377                 for(var i = 0, ci; ci = c[i]; i++){
5378                     var n = prev(ci);
5379                     if(n && is(n, ss)){
5380                         r[++ri] = ci;
5381                     }
5382                 }
5383                 return r;
5384             }
5385         }
5386     };
5387 }();
5388
5389 /**
5390  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5391  * @param {String} path The selector/xpath query
5392  * @param {Node} root (optional) The start of the query (defaults to document).
5393  * @return {Array}
5394  * @member Roo
5395  * @method query
5396  */
5397 Roo.query = Roo.DomQuery.select;
5398 /*
5399  * Based on:
5400  * Ext JS Library 1.1.1
5401  * Copyright(c) 2006-2007, Ext JS, LLC.
5402  *
5403  * Originally Released Under LGPL - original licence link has changed is not relivant.
5404  *
5405  * Fork - LGPL
5406  * <script type="text/javascript">
5407  */
5408
5409 /**
5410  * @class Roo.util.Observable
5411  * Base class that provides a common interface for publishing events. Subclasses are expected to
5412  * to have a property "events" with all the events defined.<br>
5413  * For example:
5414  * <pre><code>
5415  Employee = function(name){
5416     this.name = name;
5417     this.addEvents({
5418         "fired" : true,
5419         "quit" : true
5420     });
5421  }
5422  Roo.extend(Employee, Roo.util.Observable);
5423 </code></pre>
5424  * @param {Object} config properties to use (incuding events / listeners)
5425  */
5426
5427 Roo.util.Observable = function(cfg){
5428     
5429     cfg = cfg|| {};
5430     this.addEvents(cfg.events || {});
5431     if (cfg.events) {
5432         delete cfg.events; // make sure
5433     }
5434      
5435     Roo.apply(this, cfg);
5436     
5437     if(this.listeners){
5438         this.on(this.listeners);
5439         delete this.listeners;
5440     }
5441 };
5442 Roo.util.Observable.prototype = {
5443     /** 
5444  * @cfg {Object} listeners  list of events and functions to call for this object, 
5445  * For example :
5446  * <pre><code>
5447     listeners :  { 
5448        'click' : function(e) {
5449            ..... 
5450         } ,
5451         .... 
5452     } 
5453   </code></pre>
5454  */
5455     
5456     
5457     /**
5458      * Fires the specified event with the passed parameters (minus the event name).
5459      * @param {String} eventName
5460      * @param {Object...} args Variable number of parameters are passed to handlers
5461      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5462      */
5463     fireEvent : function(){
5464         var ce = this.events[arguments[0].toLowerCase()];
5465         if(typeof ce == "object"){
5466             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5467         }else{
5468             return true;
5469         }
5470     },
5471
5472     // private
5473     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5474
5475     /**
5476      * Appends an event handler to this component
5477      * @param {String}   eventName The type of event to listen for
5478      * @param {Function} handler The method the event invokes
5479      * @param {Object}   scope (optional) The scope in which to execute the handler
5480      * function. The handler function's "this" context.
5481      * @param {Object}   options (optional) An object containing handler configuration
5482      * properties. This may contain any of the following properties:<ul>
5483      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5484      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5485      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5486      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5487      * by the specified number of milliseconds. If the event fires again within that time, the original
5488      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5489      * </ul><br>
5490      * <p>
5491      * <b>Combining Options</b><br>
5492      * Using the options argument, it is possible to combine different types of listeners:<br>
5493      * <br>
5494      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5495                 <pre><code>
5496                 el.on('click', this.onClick, this, {
5497                         single: true,
5498                 delay: 100,
5499                 forumId: 4
5500                 });
5501                 </code></pre>
5502      * <p>
5503      * <b>Attaching multiple handlers in 1 call</b><br>
5504      * The method also allows for a single argument to be passed which is a config object containing properties
5505      * which specify multiple handlers.
5506      * <pre><code>
5507                 el.on({
5508                         'click': {
5509                         fn: this.onClick,
5510                         scope: this,
5511                         delay: 100
5512                 }, 
5513                 'mouseover': {
5514                         fn: this.onMouseOver,
5515                         scope: this
5516                 },
5517                 'mouseout': {
5518                         fn: this.onMouseOut,
5519                         scope: this
5520                 }
5521                 });
5522                 </code></pre>
5523      * <p>
5524      * Or a shorthand syntax which passes the same scope object to all handlers:
5525         <pre><code>
5526                 el.on({
5527                         'click': this.onClick,
5528                 'mouseover': this.onMouseOver,
5529                 'mouseout': this.onMouseOut,
5530                 scope: this
5531                 });
5532                 </code></pre>
5533      */
5534     addListener : function(eventName, fn, scope, o){
5535         if(typeof eventName == "object"){
5536             o = eventName;
5537             for(var e in o){
5538                 if(this.filterOptRe.test(e)){
5539                     continue;
5540                 }
5541                 if(typeof o[e] == "function"){
5542                     // shared options
5543                     this.addListener(e, o[e], o.scope,  o);
5544                 }else{
5545                     // individual options
5546                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5547                 }
5548             }
5549             return;
5550         }
5551         o = (!o || typeof o == "boolean") ? {} : o;
5552         eventName = eventName.toLowerCase();
5553         var ce = this.events[eventName] || true;
5554         if(typeof ce == "boolean"){
5555             ce = new Roo.util.Event(this, eventName);
5556             this.events[eventName] = ce;
5557         }
5558         ce.addListener(fn, scope, o);
5559     },
5560
5561     /**
5562      * Removes a listener
5563      * @param {String}   eventName     The type of event to listen for
5564      * @param {Function} handler        The handler to remove
5565      * @param {Object}   scope  (optional) The scope (this object) for the handler
5566      */
5567     removeListener : function(eventName, fn, scope){
5568         var ce = this.events[eventName.toLowerCase()];
5569         if(typeof ce == "object"){
5570             ce.removeListener(fn, scope);
5571         }
5572     },
5573
5574     /**
5575      * Removes all listeners for this object
5576      */
5577     purgeListeners : function(){
5578         for(var evt in this.events){
5579             if(typeof this.events[evt] == "object"){
5580                  this.events[evt].clearListeners();
5581             }
5582         }
5583     },
5584
5585     relayEvents : function(o, events){
5586         var createHandler = function(ename){
5587             return function(){
5588                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5589             };
5590         };
5591         for(var i = 0, len = events.length; i < len; i++){
5592             var ename = events[i];
5593             if(!this.events[ename]){ this.events[ename] = true; };
5594             o.on(ename, createHandler(ename), this);
5595         }
5596     },
5597
5598     /**
5599      * Used to define events on this Observable
5600      * @param {Object} object The object with the events defined
5601      */
5602     addEvents : function(o){
5603         if(!this.events){
5604             this.events = {};
5605         }
5606         Roo.applyIf(this.events, o);
5607     },
5608
5609     /**
5610      * Checks to see if this object has any listeners for a specified event
5611      * @param {String} eventName The name of the event to check for
5612      * @return {Boolean} True if the event is being listened for, else false
5613      */
5614     hasListener : function(eventName){
5615         var e = this.events[eventName];
5616         return typeof e == "object" && e.listeners.length > 0;
5617     }
5618 };
5619 /**
5620  * Appends an event handler to this element (shorthand for addListener)
5621  * @param {String}   eventName     The type of event to listen for
5622  * @param {Function} handler        The method the event invokes
5623  * @param {Object}   scope (optional) The scope in which to execute the handler
5624  * function. The handler function's "this" context.
5625  * @param {Object}   options  (optional)
5626  * @method
5627  */
5628 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5629 /**
5630  * Removes a listener (shorthand for removeListener)
5631  * @param {String}   eventName     The type of event to listen for
5632  * @param {Function} handler        The handler to remove
5633  * @param {Object}   scope  (optional) The scope (this object) for the handler
5634  * @method
5635  */
5636 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5637
5638 /**
5639  * Starts capture on the specified Observable. All events will be passed
5640  * to the supplied function with the event name + standard signature of the event
5641  * <b>before</b> the event is fired. If the supplied function returns false,
5642  * the event will not fire.
5643  * @param {Observable} o The Observable to capture
5644  * @param {Function} fn The function to call
5645  * @param {Object} scope (optional) The scope (this object) for the fn
5646  * @static
5647  */
5648 Roo.util.Observable.capture = function(o, fn, scope){
5649     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5650 };
5651
5652 /**
5653  * Removes <b>all</b> added captures from the Observable.
5654  * @param {Observable} o The Observable to release
5655  * @static
5656  */
5657 Roo.util.Observable.releaseCapture = function(o){
5658     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5659 };
5660
5661 (function(){
5662
5663     var createBuffered = function(h, o, scope){
5664         var task = new Roo.util.DelayedTask();
5665         return function(){
5666             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5667         };
5668     };
5669
5670     var createSingle = function(h, e, fn, scope){
5671         return function(){
5672             e.removeListener(fn, scope);
5673             return h.apply(scope, arguments);
5674         };
5675     };
5676
5677     var createDelayed = function(h, o, scope){
5678         return function(){
5679             var args = Array.prototype.slice.call(arguments, 0);
5680             setTimeout(function(){
5681                 h.apply(scope, args);
5682             }, o.delay || 10);
5683         };
5684     };
5685
5686     Roo.util.Event = function(obj, name){
5687         this.name = name;
5688         this.obj = obj;
5689         this.listeners = [];
5690     };
5691
5692     Roo.util.Event.prototype = {
5693         addListener : function(fn, scope, options){
5694             var o = options || {};
5695             scope = scope || this.obj;
5696             if(!this.isListening(fn, scope)){
5697                 var l = {fn: fn, scope: scope, options: o};
5698                 var h = fn;
5699                 if(o.delay){
5700                     h = createDelayed(h, o, scope);
5701                 }
5702                 if(o.single){
5703                     h = createSingle(h, this, fn, scope);
5704                 }
5705                 if(o.buffer){
5706                     h = createBuffered(h, o, scope);
5707                 }
5708                 l.fireFn = h;
5709                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5710                     this.listeners.push(l);
5711                 }else{
5712                     this.listeners = this.listeners.slice(0);
5713                     this.listeners.push(l);
5714                 }
5715             }
5716         },
5717
5718         findListener : function(fn, scope){
5719             scope = scope || this.obj;
5720             var ls = this.listeners;
5721             for(var i = 0, len = ls.length; i < len; i++){
5722                 var l = ls[i];
5723                 if(l.fn == fn && l.scope == scope){
5724                     return i;
5725                 }
5726             }
5727             return -1;
5728         },
5729
5730         isListening : function(fn, scope){
5731             return this.findListener(fn, scope) != -1;
5732         },
5733
5734         removeListener : function(fn, scope){
5735             var index;
5736             if((index = this.findListener(fn, scope)) != -1){
5737                 if(!this.firing){
5738                     this.listeners.splice(index, 1);
5739                 }else{
5740                     this.listeners = this.listeners.slice(0);
5741                     this.listeners.splice(index, 1);
5742                 }
5743                 return true;
5744             }
5745             return false;
5746         },
5747
5748         clearListeners : function(){
5749             this.listeners = [];
5750         },
5751
5752         fire : function(){
5753             var ls = this.listeners, scope, len = ls.length;
5754             if(len > 0){
5755                 this.firing = true;
5756                 var args = Array.prototype.slice.call(arguments, 0);
5757                 for(var i = 0; i < len; i++){
5758                     var l = ls[i];
5759                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5760                         this.firing = false;
5761                         return false;
5762                     }
5763                 }
5764                 this.firing = false;
5765             }
5766             return true;
5767         }
5768     };
5769 })();/*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779
5780 /**
5781  * @class Roo.EventManager
5782  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5783  * several useful events directly.
5784  * See {@link Roo.EventObject} for more details on normalized event objects.
5785  * @singleton
5786  */
5787 Roo.EventManager = function(){
5788     var docReadyEvent, docReadyProcId, docReadyState = false;
5789     var resizeEvent, resizeTask, textEvent, textSize;
5790     var E = Roo.lib.Event;
5791     var D = Roo.lib.Dom;
5792
5793
5794     var fireDocReady = function(){
5795         if(!docReadyState){
5796             docReadyState = true;
5797             Roo.isReady = true;
5798             if(docReadyProcId){
5799                 clearInterval(docReadyProcId);
5800             }
5801             if(Roo.isGecko || Roo.isOpera) {
5802                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5803             }
5804             if(Roo.isIE){
5805                 var defer = document.getElementById("ie-deferred-loader");
5806                 if(defer){
5807                     defer.onreadystatechange = null;
5808                     defer.parentNode.removeChild(defer);
5809                 }
5810             }
5811             if(docReadyEvent){
5812                 docReadyEvent.fire();
5813                 docReadyEvent.clearListeners();
5814             }
5815         }
5816     };
5817     
5818     var initDocReady = function(){
5819         docReadyEvent = new Roo.util.Event();
5820         if(Roo.isGecko || Roo.isOpera) {
5821             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5822         }else if(Roo.isIE){
5823             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5824             var defer = document.getElementById("ie-deferred-loader");
5825             defer.onreadystatechange = function(){
5826                 if(this.readyState == "complete"){
5827                     fireDocReady();
5828                 }
5829             };
5830         }else if(Roo.isSafari){ 
5831             docReadyProcId = setInterval(function(){
5832                 var rs = document.readyState;
5833                 if(rs == "complete") {
5834                     fireDocReady();     
5835                  }
5836             }, 10);
5837         }
5838         // no matter what, make sure it fires on load
5839         E.on(window, "load", fireDocReady);
5840     };
5841
5842     var createBuffered = function(h, o){
5843         var task = new Roo.util.DelayedTask(h);
5844         return function(e){
5845             // create new event object impl so new events don't wipe out properties
5846             e = new Roo.EventObjectImpl(e);
5847             task.delay(o.buffer, h, null, [e]);
5848         };
5849     };
5850
5851     var createSingle = function(h, el, ename, fn){
5852         return function(e){
5853             Roo.EventManager.removeListener(el, ename, fn);
5854             h(e);
5855         };
5856     };
5857
5858     var createDelayed = function(h, o){
5859         return function(e){
5860             // create new event object impl so new events don't wipe out properties
5861             e = new Roo.EventObjectImpl(e);
5862             setTimeout(function(){
5863                 h(e);
5864             }, o.delay || 10);
5865         };
5866     };
5867
5868     var listen = function(element, ename, opt, fn, scope){
5869         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5870         fn = fn || o.fn; scope = scope || o.scope;
5871         var el = Roo.getDom(element);
5872         if(!el){
5873             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5874         }
5875         var h = function(e){
5876             e = Roo.EventObject.setEvent(e);
5877             var t;
5878             if(o.delegate){
5879                 t = e.getTarget(o.delegate, el);
5880                 if(!t){
5881                     return;
5882                 }
5883             }else{
5884                 t = e.target;
5885             }
5886             if(o.stopEvent === true){
5887                 e.stopEvent();
5888             }
5889             if(o.preventDefault === true){
5890                e.preventDefault();
5891             }
5892             if(o.stopPropagation === true){
5893                 e.stopPropagation();
5894             }
5895
5896             if(o.normalized === false){
5897                 e = e.browserEvent;
5898             }
5899
5900             fn.call(scope || el, e, t, o);
5901         };
5902         if(o.delay){
5903             h = createDelayed(h, o);
5904         }
5905         if(o.single){
5906             h = createSingle(h, el, ename, fn);
5907         }
5908         if(o.buffer){
5909             h = createBuffered(h, o);
5910         }
5911         fn._handlers = fn._handlers || [];
5912         fn._handlers.push([Roo.id(el), ename, h]);
5913
5914         E.on(el, ename, h);
5915         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5916             el.addEventListener("DOMMouseScroll", h, false);
5917             E.on(window, 'unload', function(){
5918                 el.removeEventListener("DOMMouseScroll", h, false);
5919             });
5920         }
5921         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5922             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5923         }
5924         return h;
5925     };
5926
5927     var stopListening = function(el, ename, fn){
5928         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5929         if(hds){
5930             for(var i = 0, len = hds.length; i < len; i++){
5931                 var h = hds[i];
5932                 if(h[0] == id && h[1] == ename){
5933                     hd = h[2];
5934                     hds.splice(i, 1);
5935                     break;
5936                 }
5937             }
5938         }
5939         E.un(el, ename, hd);
5940         el = Roo.getDom(el);
5941         if(ename == "mousewheel" && el.addEventListener){
5942             el.removeEventListener("DOMMouseScroll", hd, false);
5943         }
5944         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5945             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5946         }
5947     };
5948
5949     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5950     
5951     var pub = {
5952         
5953         
5954         /** 
5955          * Fix for doc tools
5956          * @scope Roo.EventManager
5957          */
5958         
5959         
5960         /** 
5961          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5962          * object with a Roo.EventObject
5963          * @param {Function} fn        The method the event invokes
5964          * @param {Object}   scope    An object that becomes the scope of the handler
5965          * @param {boolean}  override If true, the obj passed in becomes
5966          *                             the execution scope of the listener
5967          * @return {Function} The wrapped function
5968          * @deprecated
5969          */
5970         wrap : function(fn, scope, override){
5971             return function(e){
5972                 Roo.EventObject.setEvent(e);
5973                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5974             };
5975         },
5976         
5977         /**
5978      * Appends an event handler to an element (shorthand for addListener)
5979      * @param {String/HTMLElement}   element        The html element or id to assign the
5980      * @param {String}   eventName The type of event to listen for
5981      * @param {Function} handler The method the event invokes
5982      * @param {Object}   scope (optional) The scope in which to execute the handler
5983      * function. The handler function's "this" context.
5984      * @param {Object}   options (optional) An object containing handler configuration
5985      * properties. This may contain any of the following properties:<ul>
5986      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5987      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5988      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5989      * <li>preventDefault {Boolean} True to prevent the default action</li>
5990      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5991      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5992      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5993      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5994      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5995      * by the specified number of milliseconds. If the event fires again within that time, the original
5996      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5997      * </ul><br>
5998      * <p>
5999      * <b>Combining Options</b><br>
6000      * Using the options argument, it is possible to combine different types of listeners:<br>
6001      * <br>
6002      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6003      * Code:<pre><code>
6004 el.on('click', this.onClick, this, {
6005     single: true,
6006     delay: 100,
6007     stopEvent : true,
6008     forumId: 4
6009 });</code></pre>
6010      * <p>
6011      * <b>Attaching multiple handlers in 1 call</b><br>
6012       * The method also allows for a single argument to be passed which is a config object containing properties
6013      * which specify multiple handlers.
6014      * <p>
6015      * Code:<pre><code>
6016 el.on({
6017     'click' : {
6018         fn: this.onClick
6019         scope: this,
6020         delay: 100
6021     },
6022     'mouseover' : {
6023         fn: this.onMouseOver
6024         scope: this
6025     },
6026     'mouseout' : {
6027         fn: this.onMouseOut
6028         scope: this
6029     }
6030 });</code></pre>
6031      * <p>
6032      * Or a shorthand syntax:<br>
6033      * Code:<pre><code>
6034 el.on({
6035     'click' : this.onClick,
6036     'mouseover' : this.onMouseOver,
6037     'mouseout' : this.onMouseOut
6038     scope: this
6039 });</code></pre>
6040      */
6041         addListener : function(element, eventName, fn, scope, options){
6042             if(typeof eventName == "object"){
6043                 var o = eventName;
6044                 for(var e in o){
6045                     if(propRe.test(e)){
6046                         continue;
6047                     }
6048                     if(typeof o[e] == "function"){
6049                         // shared options
6050                         listen(element, e, o, o[e], o.scope);
6051                     }else{
6052                         // individual options
6053                         listen(element, e, o[e]);
6054                     }
6055                 }
6056                 return;
6057             }
6058             return listen(element, eventName, options, fn, scope);
6059         },
6060         
6061         /**
6062          * Removes an event handler
6063          *
6064          * @param {String/HTMLElement}   element        The id or html element to remove the 
6065          *                             event from
6066          * @param {String}   eventName     The type of event
6067          * @param {Function} fn
6068          * @return {Boolean} True if a listener was actually removed
6069          */
6070         removeListener : function(element, eventName, fn){
6071             return stopListening(element, eventName, fn);
6072         },
6073         
6074         /**
6075          * Fires when the document is ready (before onload and before images are loaded). Can be 
6076          * accessed shorthanded Roo.onReady().
6077          * @param {Function} fn        The method the event invokes
6078          * @param {Object}   scope    An  object that becomes the scope of the handler
6079          * @param {boolean}  options
6080          */
6081         onDocumentReady : function(fn, scope, options){
6082             if(docReadyState){ // if it already fired
6083                 docReadyEvent.addListener(fn, scope, options);
6084                 docReadyEvent.fire();
6085                 docReadyEvent.clearListeners();
6086                 return;
6087             }
6088             if(!docReadyEvent){
6089                 initDocReady();
6090             }
6091             docReadyEvent.addListener(fn, scope, options);
6092         },
6093         
6094         /**
6095          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6096          * @param {Function} fn        The method the event invokes
6097          * @param {Object}   scope    An object that becomes the scope of the handler
6098          * @param {boolean}  options
6099          */
6100         onWindowResize : function(fn, scope, options){
6101             if(!resizeEvent){
6102                 resizeEvent = new Roo.util.Event();
6103                 resizeTask = new Roo.util.DelayedTask(function(){
6104                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6105                 });
6106                 E.on(window, "resize", function(){
6107                     if(Roo.isIE){
6108                         resizeTask.delay(50);
6109                     }else{
6110                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6111                     }
6112                 });
6113             }
6114             resizeEvent.addListener(fn, scope, options);
6115         },
6116
6117         /**
6118          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6119          * @param {Function} fn        The method the event invokes
6120          * @param {Object}   scope    An object that becomes the scope of the handler
6121          * @param {boolean}  options
6122          */
6123         onTextResize : function(fn, scope, options){
6124             if(!textEvent){
6125                 textEvent = new Roo.util.Event();
6126                 var textEl = new Roo.Element(document.createElement('div'));
6127                 textEl.dom.className = 'x-text-resize';
6128                 textEl.dom.innerHTML = 'X';
6129                 textEl.appendTo(document.body);
6130                 textSize = textEl.dom.offsetHeight;
6131                 setInterval(function(){
6132                     if(textEl.dom.offsetHeight != textSize){
6133                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6134                     }
6135                 }, this.textResizeInterval);
6136             }
6137             textEvent.addListener(fn, scope, options);
6138         },
6139
6140         /**
6141          * Removes the passed window resize listener.
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    The scope of handler
6144          */
6145         removeResizeListener : function(fn, scope){
6146             if(resizeEvent){
6147                 resizeEvent.removeListener(fn, scope);
6148             }
6149         },
6150
6151         // private
6152         fireResize : function(){
6153             if(resizeEvent){
6154                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6155             }   
6156         },
6157         /**
6158          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6159          */
6160         ieDeferSrc : false,
6161         /**
6162          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6163          */
6164         textResizeInterval : 50
6165     };
6166     
6167     /**
6168      * Fix for doc tools
6169      * @scopeAlias pub=Roo.EventManager
6170      */
6171     
6172      /**
6173      * Appends an event handler to an element (shorthand for addListener)
6174      * @param {String/HTMLElement}   element        The html element or id to assign the
6175      * @param {String}   eventName The type of event to listen for
6176      * @param {Function} handler The method the event invokes
6177      * @param {Object}   scope (optional) The scope in which to execute the handler
6178      * function. The handler function's "this" context.
6179      * @param {Object}   options (optional) An object containing handler configuration
6180      * properties. This may contain any of the following properties:<ul>
6181      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6182      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6183      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6184      * <li>preventDefault {Boolean} True to prevent the default action</li>
6185      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6186      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6187      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6188      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6189      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6190      * by the specified number of milliseconds. If the event fires again within that time, the original
6191      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6192      * </ul><br>
6193      * <p>
6194      * <b>Combining Options</b><br>
6195      * Using the options argument, it is possible to combine different types of listeners:<br>
6196      * <br>
6197      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6198      * Code:<pre><code>
6199 el.on('click', this.onClick, this, {
6200     single: true,
6201     delay: 100,
6202     stopEvent : true,
6203     forumId: 4
6204 });</code></pre>
6205      * <p>
6206      * <b>Attaching multiple handlers in 1 call</b><br>
6207       * The method also allows for a single argument to be passed which is a config object containing properties
6208      * which specify multiple handlers.
6209      * <p>
6210      * Code:<pre><code>
6211 el.on({
6212     'click' : {
6213         fn: this.onClick
6214         scope: this,
6215         delay: 100
6216     },
6217     'mouseover' : {
6218         fn: this.onMouseOver
6219         scope: this
6220     },
6221     'mouseout' : {
6222         fn: this.onMouseOut
6223         scope: this
6224     }
6225 });</code></pre>
6226      * <p>
6227      * Or a shorthand syntax:<br>
6228      * Code:<pre><code>
6229 el.on({
6230     'click' : this.onClick,
6231     'mouseover' : this.onMouseOver,
6232     'mouseout' : this.onMouseOut
6233     scope: this
6234 });</code></pre>
6235      */
6236     pub.on = pub.addListener;
6237     pub.un = pub.removeListener;
6238
6239     pub.stoppedMouseDownEvent = new Roo.util.Event();
6240     return pub;
6241 }();
6242 /**
6243   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6244   * @param {Function} fn        The method the event invokes
6245   * @param {Object}   scope    An  object that becomes the scope of the handler
6246   * @param {boolean}  override If true, the obj passed in becomes
6247   *                             the execution scope of the listener
6248   * @member Roo
6249   * @method onReady
6250  */
6251 Roo.onReady = Roo.EventManager.onDocumentReady;
6252
6253 Roo.onReady(function(){
6254     var bd = Roo.get(document.body);
6255     if(!bd){ return; }
6256
6257     var cls = [
6258             Roo.isIE ? "roo-ie"
6259             : Roo.isGecko ? "roo-gecko"
6260             : Roo.isOpera ? "roo-opera"
6261             : Roo.isSafari ? "roo-safari" : ""];
6262
6263     if(Roo.isMac){
6264         cls.push("roo-mac");
6265     }
6266     if(Roo.isLinux){
6267         cls.push("roo-linux");
6268     }
6269     if(Roo.isBorderBox){
6270         cls.push('roo-border-box');
6271     }
6272     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6273         var p = bd.dom.parentNode;
6274         if(p){
6275             p.className += ' roo-strict';
6276         }
6277     }
6278     bd.addClass(cls.join(' '));
6279 });
6280
6281 /**
6282  * @class Roo.EventObject
6283  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6284  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6285  * Example:
6286  * <pre><code>
6287  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6288     e.preventDefault();
6289     var target = e.getTarget();
6290     ...
6291  }
6292  var myDiv = Roo.get("myDiv");
6293  myDiv.on("click", handleClick);
6294  //or
6295  Roo.EventManager.on("myDiv", 'click', handleClick);
6296  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6297  </code></pre>
6298  * @singleton
6299  */
6300 Roo.EventObject = function(){
6301     
6302     var E = Roo.lib.Event;
6303     
6304     // safari keypress events for special keys return bad keycodes
6305     var safariKeys = {
6306         63234 : 37, // left
6307         63235 : 39, // right
6308         63232 : 38, // up
6309         63233 : 40, // down
6310         63276 : 33, // page up
6311         63277 : 34, // page down
6312         63272 : 46, // delete
6313         63273 : 36, // home
6314         63275 : 35  // end
6315     };
6316
6317     // normalize button clicks
6318     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6319                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6320
6321     Roo.EventObjectImpl = function(e){
6322         if(e){
6323             this.setEvent(e.browserEvent || e);
6324         }
6325     };
6326     Roo.EventObjectImpl.prototype = {
6327         /**
6328          * Used to fix doc tools.
6329          * @scope Roo.EventObject.prototype
6330          */
6331             
6332
6333         
6334         
6335         /** The normal browser event */
6336         browserEvent : null,
6337         /** The button pressed in a mouse event */
6338         button : -1,
6339         /** True if the shift key was down during the event */
6340         shiftKey : false,
6341         /** True if the control key was down during the event */
6342         ctrlKey : false,
6343         /** True if the alt key was down during the event */
6344         altKey : false,
6345
6346         /** Key constant 
6347         * @type Number */
6348         BACKSPACE : 8,
6349         /** Key constant 
6350         * @type Number */
6351         TAB : 9,
6352         /** Key constant 
6353         * @type Number */
6354         RETURN : 13,
6355         /** Key constant 
6356         * @type Number */
6357         ENTER : 13,
6358         /** Key constant 
6359         * @type Number */
6360         SHIFT : 16,
6361         /** Key constant 
6362         * @type Number */
6363         CONTROL : 17,
6364         /** Key constant 
6365         * @type Number */
6366         ESC : 27,
6367         /** Key constant 
6368         * @type Number */
6369         SPACE : 32,
6370         /** Key constant 
6371         * @type Number */
6372         PAGEUP : 33,
6373         /** Key constant 
6374         * @type Number */
6375         PAGEDOWN : 34,
6376         /** Key constant 
6377         * @type Number */
6378         END : 35,
6379         /** Key constant 
6380         * @type Number */
6381         HOME : 36,
6382         /** Key constant 
6383         * @type Number */
6384         LEFT : 37,
6385         /** Key constant 
6386         * @type Number */
6387         UP : 38,
6388         /** Key constant 
6389         * @type Number */
6390         RIGHT : 39,
6391         /** Key constant 
6392         * @type Number */
6393         DOWN : 40,
6394         /** Key constant 
6395         * @type Number */
6396         DELETE : 46,
6397         /** Key constant 
6398         * @type Number */
6399         F5 : 116,
6400
6401            /** @private */
6402         setEvent : function(e){
6403             if(e == this || (e && e.browserEvent)){ // already wrapped
6404                 return e;
6405             }
6406             this.browserEvent = e;
6407             if(e){
6408                 // normalize buttons
6409                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6410                 if(e.type == 'click' && this.button == -1){
6411                     this.button = 0;
6412                 }
6413                 this.type = e.type;
6414                 this.shiftKey = e.shiftKey;
6415                 // mac metaKey behaves like ctrlKey
6416                 this.ctrlKey = e.ctrlKey || e.metaKey;
6417                 this.altKey = e.altKey;
6418                 // in getKey these will be normalized for the mac
6419                 this.keyCode = e.keyCode;
6420                 // keyup warnings on firefox.
6421                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6422                 // cache the target for the delayed and or buffered events
6423                 this.target = E.getTarget(e);
6424                 // same for XY
6425                 this.xy = E.getXY(e);
6426             }else{
6427                 this.button = -1;
6428                 this.shiftKey = false;
6429                 this.ctrlKey = false;
6430                 this.altKey = false;
6431                 this.keyCode = 0;
6432                 this.charCode =0;
6433                 this.target = null;
6434                 this.xy = [0, 0];
6435             }
6436             return this;
6437         },
6438
6439         /**
6440          * Stop the event (preventDefault and stopPropagation)
6441          */
6442         stopEvent : function(){
6443             if(this.browserEvent){
6444                 if(this.browserEvent.type == 'mousedown'){
6445                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6446                 }
6447                 E.stopEvent(this.browserEvent);
6448             }
6449         },
6450
6451         /**
6452          * Prevents the browsers default handling of the event.
6453          */
6454         preventDefault : function(){
6455             if(this.browserEvent){
6456                 E.preventDefault(this.browserEvent);
6457             }
6458         },
6459
6460         /** @private */
6461         isNavKeyPress : function(){
6462             var k = this.keyCode;
6463             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6464             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6465         },
6466
6467         isSpecialKey : function(){
6468             var k = this.keyCode;
6469             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6470             (k == 16) || (k == 17) ||
6471             (k >= 18 && k <= 20) ||
6472             (k >= 33 && k <= 35) ||
6473             (k >= 36 && k <= 39) ||
6474             (k >= 44 && k <= 45);
6475         },
6476         /**
6477          * Cancels bubbling of the event.
6478          */
6479         stopPropagation : function(){
6480             if(this.browserEvent){
6481                 if(this.type == 'mousedown'){
6482                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6483                 }
6484                 E.stopPropagation(this.browserEvent);
6485             }
6486         },
6487
6488         /**
6489          * Gets the key code for the event.
6490          * @return {Number}
6491          */
6492         getCharCode : function(){
6493             return this.charCode || this.keyCode;
6494         },
6495
6496         /**
6497          * Returns a normalized keyCode for the event.
6498          * @return {Number} The key code
6499          */
6500         getKey : function(){
6501             var k = this.keyCode || this.charCode;
6502             return Roo.isSafari ? (safariKeys[k] || k) : k;
6503         },
6504
6505         /**
6506          * Gets the x coordinate of the event.
6507          * @return {Number}
6508          */
6509         getPageX : function(){
6510             return this.xy[0];
6511         },
6512
6513         /**
6514          * Gets the y coordinate of the event.
6515          * @return {Number}
6516          */
6517         getPageY : function(){
6518             return this.xy[1];
6519         },
6520
6521         /**
6522          * Gets the time of the event.
6523          * @return {Number}
6524          */
6525         getTime : function(){
6526             if(this.browserEvent){
6527                 return E.getTime(this.browserEvent);
6528             }
6529             return null;
6530         },
6531
6532         /**
6533          * Gets the page coordinates of the event.
6534          * @return {Array} The xy values like [x, y]
6535          */
6536         getXY : function(){
6537             return this.xy;
6538         },
6539
6540         /**
6541          * Gets the target for the event.
6542          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6543          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6544                 search as a number or element (defaults to 10 || document.body)
6545          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6546          * @return {HTMLelement}
6547          */
6548         getTarget : function(selector, maxDepth, returnEl){
6549             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6550         },
6551         /**
6552          * Gets the related target.
6553          * @return {HTMLElement}
6554          */
6555         getRelatedTarget : function(){
6556             if(this.browserEvent){
6557                 return E.getRelatedTarget(this.browserEvent);
6558             }
6559             return null;
6560         },
6561
6562         /**
6563          * Normalizes mouse wheel delta across browsers
6564          * @return {Number} The delta
6565          */
6566         getWheelDelta : function(){
6567             var e = this.browserEvent;
6568             var delta = 0;
6569             if(e.wheelDelta){ /* IE/Opera. */
6570                 delta = e.wheelDelta/120;
6571             }else if(e.detail){ /* Mozilla case. */
6572                 delta = -e.detail/3;
6573             }
6574             return delta;
6575         },
6576
6577         /**
6578          * Returns true if the control, meta, shift or alt key was pressed during this event.
6579          * @return {Boolean}
6580          */
6581         hasModifier : function(){
6582             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6583         },
6584
6585         /**
6586          * Returns true if the target of this event equals el or is a child of el
6587          * @param {String/HTMLElement/Element} el
6588          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6589          * @return {Boolean}
6590          */
6591         within : function(el, related){
6592             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6593             return t && Roo.fly(el).contains(t);
6594         },
6595
6596         getPoint : function(){
6597             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6598         }
6599     };
6600
6601     return new Roo.EventObjectImpl();
6602 }();
6603             
6604     /*
6605  * Based on:
6606  * Ext JS Library 1.1.1
6607  * Copyright(c) 2006-2007, Ext JS, LLC.
6608  *
6609  * Originally Released Under LGPL - original licence link has changed is not relivant.
6610  *
6611  * Fork - LGPL
6612  * <script type="text/javascript">
6613  */
6614
6615  
6616 // was in Composite Element!??!?!
6617  
6618 (function(){
6619     var D = Roo.lib.Dom;
6620     var E = Roo.lib.Event;
6621     var A = Roo.lib.Anim;
6622
6623     // local style camelizing for speed
6624     var propCache = {};
6625     var camelRe = /(-[a-z])/gi;
6626     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6627     var view = document.defaultView;
6628
6629 /**
6630  * @class Roo.Element
6631  * Represents an Element in the DOM.<br><br>
6632  * Usage:<br>
6633 <pre><code>
6634 var el = Roo.get("my-div");
6635
6636 // or with getEl
6637 var el = getEl("my-div");
6638
6639 // or with a DOM element
6640 var el = Roo.get(myDivElement);
6641 </code></pre>
6642  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6643  * each call instead of constructing a new one.<br><br>
6644  * <b>Animations</b><br />
6645  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6646  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6647 <pre>
6648 Option    Default   Description
6649 --------- --------  ---------------------------------------------
6650 duration  .35       The duration of the animation in seconds
6651 easing    easeOut   The YUI easing method
6652 callback  none      A function to execute when the anim completes
6653 scope     this      The scope (this) of the callback function
6654 </pre>
6655 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6656 * manipulate the animation. Here's an example:
6657 <pre><code>
6658 var el = Roo.get("my-div");
6659
6660 // no animation
6661 el.setWidth(100);
6662
6663 // default animation
6664 el.setWidth(100, true);
6665
6666 // animation with some options set
6667 el.setWidth(100, {
6668     duration: 1,
6669     callback: this.foo,
6670     scope: this
6671 });
6672
6673 // using the "anim" property to get the Anim object
6674 var opt = {
6675     duration: 1,
6676     callback: this.foo,
6677     scope: this
6678 };
6679 el.setWidth(100, opt);
6680 ...
6681 if(opt.anim.isAnimated()){
6682     opt.anim.stop();
6683 }
6684 </code></pre>
6685 * <b> Composite (Collections of) Elements</b><br />
6686  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6687  * @constructor Create a new Element directly.
6688  * @param {String/HTMLElement} element
6689  * @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).
6690  */
6691     Roo.Element = function(element, forceNew){
6692         var dom = typeof element == "string" ?
6693                 document.getElementById(element) : element;
6694         if(!dom){ // invalid id/element
6695             return null;
6696         }
6697         var id = dom.id;
6698         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6699             return Roo.Element.cache[id];
6700         }
6701
6702         /**
6703          * The DOM element
6704          * @type HTMLElement
6705          */
6706         this.dom = dom;
6707
6708         /**
6709          * The DOM element ID
6710          * @type String
6711          */
6712         this.id = id || Roo.id(dom);
6713     };
6714
6715     var El = Roo.Element;
6716
6717     El.prototype = {
6718         /**
6719          * The element's default display mode  (defaults to "")
6720          * @type String
6721          */
6722         originalDisplay : "",
6723
6724         visibilityMode : 1,
6725         /**
6726          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6727          * @type String
6728          */
6729         defaultUnit : "px",
6730         /**
6731          * Sets the element's visibility mode. When setVisible() is called it
6732          * will use this to determine whether to set the visibility or the display property.
6733          * @param visMode Element.VISIBILITY or Element.DISPLAY
6734          * @return {Roo.Element} this
6735          */
6736         setVisibilityMode : function(visMode){
6737             this.visibilityMode = visMode;
6738             return this;
6739         },
6740         /**
6741          * Convenience method for setVisibilityMode(Element.DISPLAY)
6742          * @param {String} display (optional) What to set display to when visible
6743          * @return {Roo.Element} this
6744          */
6745         enableDisplayMode : function(display){
6746             this.setVisibilityMode(El.DISPLAY);
6747             if(typeof display != "undefined") this.originalDisplay = display;
6748             return this;
6749         },
6750
6751         /**
6752          * 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)
6753          * @param {String} selector The simple selector to test
6754          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6755                 search as a number or element (defaults to 10 || document.body)
6756          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6757          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6758          */
6759         findParent : function(simpleSelector, maxDepth, returnEl){
6760             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6761             maxDepth = maxDepth || 50;
6762             if(typeof maxDepth != "number"){
6763                 stopEl = Roo.getDom(maxDepth);
6764                 maxDepth = 10;
6765             }
6766             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6767                 if(dq.is(p, simpleSelector)){
6768                     return returnEl ? Roo.get(p) : p;
6769                 }
6770                 depth++;
6771                 p = p.parentNode;
6772             }
6773             return null;
6774         },
6775
6776
6777         /**
6778          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6779          * @param {String} selector The simple selector to test
6780          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6781                 search as a number or element (defaults to 10 || document.body)
6782          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6783          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6784          */
6785         findParentNode : function(simpleSelector, maxDepth, returnEl){
6786             var p = Roo.fly(this.dom.parentNode, '_internal');
6787             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6788         },
6789
6790         /**
6791          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6792          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6793          * @param {String} selector The simple selector to test
6794          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6795                 search as a number or element (defaults to 10 || document.body)
6796          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6797          */
6798         up : function(simpleSelector, maxDepth){
6799             return this.findParentNode(simpleSelector, maxDepth, true);
6800         },
6801
6802
6803
6804         /**
6805          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6806          * @param {String} selector The simple selector to test
6807          * @return {Boolean} True if this element matches the selector, else false
6808          */
6809         is : function(simpleSelector){
6810             return Roo.DomQuery.is(this.dom, simpleSelector);
6811         },
6812
6813         /**
6814          * Perform animation on this element.
6815          * @param {Object} args The YUI animation control args
6816          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6817          * @param {Function} onComplete (optional) Function to call when animation completes
6818          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6819          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6820          * @return {Roo.Element} this
6821          */
6822         animate : function(args, duration, onComplete, easing, animType){
6823             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6824             return this;
6825         },
6826
6827         /*
6828          * @private Internal animation call
6829          */
6830         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6831             animType = animType || 'run';
6832             opt = opt || {};
6833             var anim = Roo.lib.Anim[animType](
6834                 this.dom, args,
6835                 (opt.duration || defaultDur) || .35,
6836                 (opt.easing || defaultEase) || 'easeOut',
6837                 function(){
6838                     Roo.callback(cb, this);
6839                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6840                 },
6841                 this
6842             );
6843             opt.anim = anim;
6844             return anim;
6845         },
6846
6847         // private legacy anim prep
6848         preanim : function(a, i){
6849             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6850         },
6851
6852         /**
6853          * Removes worthless text nodes
6854          * @param {Boolean} forceReclean (optional) By default the element
6855          * keeps track if it has been cleaned already so
6856          * you can call this over and over. However, if you update the element and
6857          * need to force a reclean, you can pass true.
6858          */
6859         clean : function(forceReclean){
6860             if(this.isCleaned && forceReclean !== true){
6861                 return this;
6862             }
6863             var ns = /\S/;
6864             var d = this.dom, n = d.firstChild, ni = -1;
6865             while(n){
6866                 var nx = n.nextSibling;
6867                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6868                     d.removeChild(n);
6869                 }else{
6870                     n.nodeIndex = ++ni;
6871                 }
6872                 n = nx;
6873             }
6874             this.isCleaned = true;
6875             return this;
6876         },
6877
6878         // private
6879         calcOffsetsTo : function(el){
6880             el = Roo.get(el);
6881             var d = el.dom;
6882             var restorePos = false;
6883             if(el.getStyle('position') == 'static'){
6884                 el.position('relative');
6885                 restorePos = true;
6886             }
6887             var x = 0, y =0;
6888             var op = this.dom;
6889             while(op && op != d && op.tagName != 'HTML'){
6890                 x+= op.offsetLeft;
6891                 y+= op.offsetTop;
6892                 op = op.offsetParent;
6893             }
6894             if(restorePos){
6895                 el.position('static');
6896             }
6897             return [x, y];
6898         },
6899
6900         /**
6901          * Scrolls this element into view within the passed container.
6902          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6903          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6904          * @return {Roo.Element} this
6905          */
6906         scrollIntoView : function(container, hscroll){
6907             var c = Roo.getDom(container) || document.body;
6908             var el = this.dom;
6909
6910             var o = this.calcOffsetsTo(c),
6911                 l = o[0],
6912                 t = o[1],
6913                 b = t+el.offsetHeight,
6914                 r = l+el.offsetWidth;
6915
6916             var ch = c.clientHeight;
6917             var ct = parseInt(c.scrollTop, 10);
6918             var cl = parseInt(c.scrollLeft, 10);
6919             var cb = ct + ch;
6920             var cr = cl + c.clientWidth;
6921
6922             if(t < ct){
6923                 c.scrollTop = t;
6924             }else if(b > cb){
6925                 c.scrollTop = b-ch;
6926             }
6927
6928             if(hscroll !== false){
6929                 if(l < cl){
6930                     c.scrollLeft = l;
6931                 }else if(r > cr){
6932                     c.scrollLeft = r-c.clientWidth;
6933                 }
6934             }
6935             return this;
6936         },
6937
6938         // private
6939         scrollChildIntoView : function(child, hscroll){
6940             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6941         },
6942
6943         /**
6944          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6945          * the new height may not be available immediately.
6946          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6947          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6948          * @param {Function} onComplete (optional) Function to call when animation completes
6949          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6950          * @return {Roo.Element} this
6951          */
6952         autoHeight : function(animate, duration, onComplete, easing){
6953             var oldHeight = this.getHeight();
6954             this.clip();
6955             this.setHeight(1); // force clipping
6956             setTimeout(function(){
6957                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6958                 if(!animate){
6959                     this.setHeight(height);
6960                     this.unclip();
6961                     if(typeof onComplete == "function"){
6962                         onComplete();
6963                     }
6964                 }else{
6965                     this.setHeight(oldHeight); // restore original height
6966                     this.setHeight(height, animate, duration, function(){
6967                         this.unclip();
6968                         if(typeof onComplete == "function") onComplete();
6969                     }.createDelegate(this), easing);
6970                 }
6971             }.createDelegate(this), 0);
6972             return this;
6973         },
6974
6975         /**
6976          * Returns true if this element is an ancestor of the passed element
6977          * @param {HTMLElement/String} el The element to check
6978          * @return {Boolean} True if this element is an ancestor of el, else false
6979          */
6980         contains : function(el){
6981             if(!el){return false;}
6982             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6983         },
6984
6985         /**
6986          * Checks whether the element is currently visible using both visibility and display properties.
6987          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6988          * @return {Boolean} True if the element is currently visible, else false
6989          */
6990         isVisible : function(deep) {
6991             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6992             if(deep !== true || !vis){
6993                 return vis;
6994             }
6995             var p = this.dom.parentNode;
6996             while(p && p.tagName.toLowerCase() != "body"){
6997                 if(!Roo.fly(p, '_isVisible').isVisible()){
6998                     return false;
6999                 }
7000                 p = p.parentNode;
7001             }
7002             return true;
7003         },
7004
7005         /**
7006          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7007          * @param {String} selector The CSS selector
7008          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7009          * @return {CompositeElement/CompositeElementLite} The composite element
7010          */
7011         select : function(selector, unique){
7012             return El.select(selector, unique, this.dom);
7013         },
7014
7015         /**
7016          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7017          * @param {String} selector The CSS selector
7018          * @return {Array} An array of the matched nodes
7019          */
7020         query : function(selector, unique){
7021             return Roo.DomQuery.select(selector, this.dom);
7022         },
7023
7024         /**
7025          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7026          * @param {String} selector The CSS selector
7027          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7028          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7029          */
7030         child : function(selector, returnDom){
7031             var n = Roo.DomQuery.selectNode(selector, this.dom);
7032             return returnDom ? n : Roo.get(n);
7033         },
7034
7035         /**
7036          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7037          * @param {String} selector The CSS selector
7038          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7039          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7040          */
7041         down : function(selector, returnDom){
7042             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7043             return returnDom ? n : Roo.get(n);
7044         },
7045
7046         /**
7047          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7048          * @param {String} group The group the DD object is member of
7049          * @param {Object} config The DD config object
7050          * @param {Object} overrides An object containing methods to override/implement on the DD object
7051          * @return {Roo.dd.DD} The DD object
7052          */
7053         initDD : function(group, config, overrides){
7054             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7055             return Roo.apply(dd, overrides);
7056         },
7057
7058         /**
7059          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7060          * @param {String} group The group the DDProxy object is member of
7061          * @param {Object} config The DDProxy config object
7062          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7063          * @return {Roo.dd.DDProxy} The DDProxy object
7064          */
7065         initDDProxy : function(group, config, overrides){
7066             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7067             return Roo.apply(dd, overrides);
7068         },
7069
7070         /**
7071          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7072          * @param {String} group The group the DDTarget object is member of
7073          * @param {Object} config The DDTarget config object
7074          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7075          * @return {Roo.dd.DDTarget} The DDTarget object
7076          */
7077         initDDTarget : function(group, config, overrides){
7078             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7079             return Roo.apply(dd, overrides);
7080         },
7081
7082         /**
7083          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7084          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7085          * @param {Boolean} visible Whether the element is visible
7086          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7087          * @return {Roo.Element} this
7088          */
7089          setVisible : function(visible, animate){
7090             if(!animate || !A){
7091                 if(this.visibilityMode == El.DISPLAY){
7092                     this.setDisplayed(visible);
7093                 }else{
7094                     this.fixDisplay();
7095                     this.dom.style.visibility = visible ? "visible" : "hidden";
7096                 }
7097             }else{
7098                 // closure for composites
7099                 var dom = this.dom;
7100                 var visMode = this.visibilityMode;
7101                 if(visible){
7102                     this.setOpacity(.01);
7103                     this.setVisible(true);
7104                 }
7105                 this.anim({opacity: { to: (visible?1:0) }},
7106                       this.preanim(arguments, 1),
7107                       null, .35, 'easeIn', function(){
7108                          if(!visible){
7109                              if(visMode == El.DISPLAY){
7110                                  dom.style.display = "none";
7111                              }else{
7112                                  dom.style.visibility = "hidden";
7113                              }
7114                              Roo.get(dom).setOpacity(1);
7115                          }
7116                      });
7117             }
7118             return this;
7119         },
7120
7121         /**
7122          * Returns true if display is not "none"
7123          * @return {Boolean}
7124          */
7125         isDisplayed : function() {
7126             return this.getStyle("display") != "none";
7127         },
7128
7129         /**
7130          * Toggles the element's visibility or display, depending on visibility mode.
7131          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7132          * @return {Roo.Element} this
7133          */
7134         toggle : function(animate){
7135             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7136             return this;
7137         },
7138
7139         /**
7140          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7141          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7142          * @return {Roo.Element} this
7143          */
7144         setDisplayed : function(value) {
7145             if(typeof value == "boolean"){
7146                value = value ? this.originalDisplay : "none";
7147             }
7148             this.setStyle("display", value);
7149             return this;
7150         },
7151
7152         /**
7153          * Tries to focus the element. Any exceptions are caught and ignored.
7154          * @return {Roo.Element} this
7155          */
7156         focus : function() {
7157             try{
7158                 this.dom.focus();
7159             }catch(e){}
7160             return this;
7161         },
7162
7163         /**
7164          * Tries to blur the element. Any exceptions are caught and ignored.
7165          * @return {Roo.Element} this
7166          */
7167         blur : function() {
7168             try{
7169                 this.dom.blur();
7170             }catch(e){}
7171             return this;
7172         },
7173
7174         /**
7175          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7176          * @param {String/Array} className The CSS class to add, or an array of classes
7177          * @return {Roo.Element} this
7178          */
7179         addClass : function(className){
7180             if(className instanceof Array){
7181                 for(var i = 0, len = className.length; i < len; i++) {
7182                     this.addClass(className[i]);
7183                 }
7184             }else{
7185                 if(className && !this.hasClass(className)){
7186                     this.dom.className = this.dom.className + " " + className;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         /**
7193          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7194          * @param {String/Array} className The CSS class to add, or an array of classes
7195          * @return {Roo.Element} this
7196          */
7197         radioClass : function(className){
7198             var siblings = this.dom.parentNode.childNodes;
7199             for(var i = 0; i < siblings.length; i++) {
7200                 var s = siblings[i];
7201                 if(s.nodeType == 1){
7202                     Roo.get(s).removeClass(className);
7203                 }
7204             }
7205             this.addClass(className);
7206             return this;
7207         },
7208
7209         /**
7210          * Removes one or more CSS classes from the element.
7211          * @param {String/Array} className The CSS class to remove, or an array of classes
7212          * @return {Roo.Element} this
7213          */
7214         removeClass : function(className){
7215             if(!className || !this.dom.className){
7216                 return this;
7217             }
7218             if(className instanceof Array){
7219                 for(var i = 0, len = className.length; i < len; i++) {
7220                     this.removeClass(className[i]);
7221                 }
7222             }else{
7223                 if(this.hasClass(className)){
7224                     var re = this.classReCache[className];
7225                     if (!re) {
7226                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7227                        this.classReCache[className] = re;
7228                     }
7229                     this.dom.className =
7230                         this.dom.className.replace(re, " ");
7231                 }
7232             }
7233             return this;
7234         },
7235
7236         // private
7237         classReCache: {},
7238
7239         /**
7240          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7241          * @param {String} className The CSS class to toggle
7242          * @return {Roo.Element} this
7243          */
7244         toggleClass : function(className){
7245             if(this.hasClass(className)){
7246                 this.removeClass(className);
7247             }else{
7248                 this.addClass(className);
7249             }
7250             return this;
7251         },
7252
7253         /**
7254          * Checks if the specified CSS class exists on this element's DOM node.
7255          * @param {String} className The CSS class to check for
7256          * @return {Boolean} True if the class exists, else false
7257          */
7258         hasClass : function(className){
7259             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7260         },
7261
7262         /**
7263          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7264          * @param {String} oldClassName The CSS class to replace
7265          * @param {String} newClassName The replacement CSS class
7266          * @return {Roo.Element} this
7267          */
7268         replaceClass : function(oldClassName, newClassName){
7269             this.removeClass(oldClassName);
7270             this.addClass(newClassName);
7271             return this;
7272         },
7273
7274         /**
7275          * Returns an object with properties matching the styles requested.
7276          * For example, el.getStyles('color', 'font-size', 'width') might return
7277          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7278          * @param {String} style1 A style name
7279          * @param {String} style2 A style name
7280          * @param {String} etc.
7281          * @return {Object} The style object
7282          */
7283         getStyles : function(){
7284             var a = arguments, len = a.length, r = {};
7285             for(var i = 0; i < len; i++){
7286                 r[a[i]] = this.getStyle(a[i]);
7287             }
7288             return r;
7289         },
7290
7291         /**
7292          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7293          * @param {String} property The style property whose value is returned.
7294          * @return {String} The current value of the style property for this element.
7295          */
7296         getStyle : function(){
7297             return view && view.getComputedStyle ?
7298                 function(prop){
7299                     var el = this.dom, v, cs, camel;
7300                     if(prop == 'float'){
7301                         prop = "cssFloat";
7302                     }
7303                     if(el.style && (v = el.style[prop])){
7304                         return v;
7305                     }
7306                     if(cs = view.getComputedStyle(el, "")){
7307                         if(!(camel = propCache[prop])){
7308                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7309                         }
7310                         return cs[camel];
7311                     }
7312                     return null;
7313                 } :
7314                 function(prop){
7315                     var el = this.dom, v, cs, camel;
7316                     if(prop == 'opacity'){
7317                         if(typeof el.style.filter == 'string'){
7318                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7319                             if(m){
7320                                 var fv = parseFloat(m[1]);
7321                                 if(!isNaN(fv)){
7322                                     return fv ? fv / 100 : 0;
7323                                 }
7324                             }
7325                         }
7326                         return 1;
7327                     }else if(prop == 'float'){
7328                         prop = "styleFloat";
7329                     }
7330                     if(!(camel = propCache[prop])){
7331                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7332                     }
7333                     if(v = el.style[camel]){
7334                         return v;
7335                     }
7336                     if(cs = el.currentStyle){
7337                         return cs[camel];
7338                     }
7339                     return null;
7340                 };
7341         }(),
7342
7343         /**
7344          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7345          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7346          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7347          * @return {Roo.Element} this
7348          */
7349         setStyle : function(prop, value){
7350             if(typeof prop == "string"){
7351                 var camel;
7352                 if(!(camel = propCache[prop])){
7353                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7354                 }
7355                 if(camel == 'opacity') {
7356                     this.setOpacity(value);
7357                 }else{
7358                     this.dom.style[camel] = value;
7359                 }
7360             }else{
7361                 for(var style in prop){
7362                     if(typeof prop[style] != "function"){
7363                        this.setStyle(style, prop[style]);
7364                     }
7365                 }
7366             }
7367             return this;
7368         },
7369
7370         /**
7371          * More flexible version of {@link #setStyle} for setting style properties.
7372          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7373          * a function which returns such a specification.
7374          * @return {Roo.Element} this
7375          */
7376         applyStyles : function(style){
7377             Roo.DomHelper.applyStyles(this.dom, style);
7378             return this;
7379         },
7380
7381         /**
7382           * 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).
7383           * @return {Number} The X position of the element
7384           */
7385         getX : function(){
7386             return D.getX(this.dom);
7387         },
7388
7389         /**
7390           * 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).
7391           * @return {Number} The Y position of the element
7392           */
7393         getY : function(){
7394             return D.getY(this.dom);
7395         },
7396
7397         /**
7398           * 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).
7399           * @return {Array} The XY position of the element
7400           */
7401         getXY : function(){
7402             return D.getXY(this.dom);
7403         },
7404
7405         /**
7406          * 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).
7407          * @param {Number} The X position of the element
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411         setX : function(x, animate){
7412             if(!animate || !A){
7413                 D.setX(this.dom, x);
7414             }else{
7415                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7416             }
7417             return this;
7418         },
7419
7420         /**
7421          * 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).
7422          * @param {Number} The Y position of the element
7423          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7424          * @return {Roo.Element} this
7425          */
7426         setY : function(y, animate){
7427             if(!animate || !A){
7428                 D.setY(this.dom, y);
7429             }else{
7430                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7437          * @param {String} left The left CSS property value
7438          * @return {Roo.Element} this
7439          */
7440         setLeft : function(left){
7441             this.setStyle("left", this.addUnits(left));
7442             return this;
7443         },
7444
7445         /**
7446          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7447          * @param {String} top The top CSS property value
7448          * @return {Roo.Element} this
7449          */
7450         setTop : function(top){
7451             this.setStyle("top", this.addUnits(top));
7452             return this;
7453         },
7454
7455         /**
7456          * Sets the element's CSS right style.
7457          * @param {String} right The right CSS property value
7458          * @return {Roo.Element} this
7459          */
7460         setRight : function(right){
7461             this.setStyle("right", this.addUnits(right));
7462             return this;
7463         },
7464
7465         /**
7466          * Sets the element's CSS bottom style.
7467          * @param {String} bottom The bottom CSS property value
7468          * @return {Roo.Element} this
7469          */
7470         setBottom : function(bottom){
7471             this.setStyle("bottom", this.addUnits(bottom));
7472             return this;
7473         },
7474
7475         /**
7476          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7477          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7478          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7479          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7480          * @return {Roo.Element} this
7481          */
7482         setXY : function(pos, animate){
7483             if(!animate || !A){
7484                 D.setXY(this.dom, pos);
7485             }else{
7486                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7487             }
7488             return this;
7489         },
7490
7491         /**
7492          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7493          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7494          * @param {Number} x X value for new position (coordinates are page-based)
7495          * @param {Number} y Y value for new position (coordinates are page-based)
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Roo.Element} this
7498          */
7499         setLocation : function(x, y, animate){
7500             this.setXY([x, y], this.preanim(arguments, 2));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Number} x X value for new position (coordinates are page-based)
7508          * @param {Number} y Y value for new position (coordinates are page-based)
7509          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7510          * @return {Roo.Element} this
7511          */
7512         moveTo : function(x, y, animate){
7513             this.setXY([x, y], this.preanim(arguments, 2));
7514             return this;
7515         },
7516
7517         /**
7518          * Returns the region of the given element.
7519          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7520          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7521          */
7522         getRegion : function(){
7523             return D.getRegion(this.dom);
7524         },
7525
7526         /**
7527          * Returns the offset height of the element
7528          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7529          * @return {Number} The element's height
7530          */
7531         getHeight : function(contentHeight){
7532             var h = this.dom.offsetHeight || 0;
7533             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7534         },
7535
7536         /**
7537          * Returns the offset width of the element
7538          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7539          * @return {Number} The element's width
7540          */
7541         getWidth : function(contentWidth){
7542             var w = this.dom.offsetWidth || 0;
7543             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7544         },
7545
7546         /**
7547          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7548          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7549          * if a height has not been set using CSS.
7550          * @return {Number}
7551          */
7552         getComputedHeight : function(){
7553             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7554             if(!h){
7555                 h = parseInt(this.getStyle('height'), 10) || 0;
7556                 if(!this.isBorderBox()){
7557                     h += this.getFrameWidth('tb');
7558                 }
7559             }
7560             return h;
7561         },
7562
7563         /**
7564          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7565          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7566          * if a width has not been set using CSS.
7567          * @return {Number}
7568          */
7569         getComputedWidth : function(){
7570             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7571             if(!w){
7572                 w = parseInt(this.getStyle('width'), 10) || 0;
7573                 if(!this.isBorderBox()){
7574                     w += this.getFrameWidth('lr');
7575                 }
7576             }
7577             return w;
7578         },
7579
7580         /**
7581          * Returns the size of the element.
7582          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7583          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7584          */
7585         getSize : function(contentSize){
7586             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7587         },
7588
7589         /**
7590          * Returns the width and height of the viewport.
7591          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7592          */
7593         getViewSize : function(){
7594             var d = this.dom, doc = document, aw = 0, ah = 0;
7595             if(d == doc || d == doc.body){
7596                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7597             }else{
7598                 return {
7599                     width : d.clientWidth,
7600                     height: d.clientHeight
7601                 };
7602             }
7603         },
7604
7605         /**
7606          * Returns the value of the "value" attribute
7607          * @param {Boolean} asNumber true to parse the value as a number
7608          * @return {String/Number}
7609          */
7610         getValue : function(asNumber){
7611             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7612         },
7613
7614         // private
7615         adjustWidth : function(width){
7616             if(typeof width == "number"){
7617                 if(this.autoBoxAdjust && !this.isBorderBox()){
7618                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7619                 }
7620                 if(width < 0){
7621                     width = 0;
7622                 }
7623             }
7624             return width;
7625         },
7626
7627         // private
7628         adjustHeight : function(height){
7629             if(typeof height == "number"){
7630                if(this.autoBoxAdjust && !this.isBorderBox()){
7631                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7632                }
7633                if(height < 0){
7634                    height = 0;
7635                }
7636             }
7637             return height;
7638         },
7639
7640         /**
7641          * Set the width of the element
7642          * @param {Number} width The new width
7643          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7644          * @return {Roo.Element} this
7645          */
7646         setWidth : function(width, animate){
7647             width = this.adjustWidth(width);
7648             if(!animate || !A){
7649                 this.dom.style.width = this.addUnits(width);
7650             }else{
7651                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7652             }
7653             return this;
7654         },
7655
7656         /**
7657          * Set the height of the element
7658          * @param {Number} height The new height
7659          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662          setHeight : function(height, animate){
7663             height = this.adjustHeight(height);
7664             if(!animate || !A){
7665                 this.dom.style.height = this.addUnits(height);
7666             }else{
7667                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7668             }
7669             return this;
7670         },
7671
7672         /**
7673          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7674          * @param {Number} width The new width
7675          * @param {Number} height The new height
7676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7677          * @return {Roo.Element} this
7678          */
7679          setSize : function(width, height, animate){
7680             if(typeof width == "object"){ // in case of object from getSize()
7681                 height = width.height; width = width.width;
7682             }
7683             width = this.adjustWidth(width); height = this.adjustHeight(height);
7684             if(!animate || !A){
7685                 this.dom.style.width = this.addUnits(width);
7686                 this.dom.style.height = this.addUnits(height);
7687             }else{
7688                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7689             }
7690             return this;
7691         },
7692
7693         /**
7694          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7695          * @param {Number} x X value for new position (coordinates are page-based)
7696          * @param {Number} y Y value for new position (coordinates are page-based)
7697          * @param {Number} width The new width
7698          * @param {Number} height The new height
7699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7700          * @return {Roo.Element} this
7701          */
7702         setBounds : function(x, y, width, height, animate){
7703             if(!animate || !A){
7704                 this.setSize(width, height);
7705                 this.setLocation(x, y);
7706             }else{
7707                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7708                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7709                               this.preanim(arguments, 4), 'motion');
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * 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.
7716          * @param {Roo.lib.Region} region The region to fill
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         setRegion : function(region, animate){
7721             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7722             return this;
7723         },
7724
7725         /**
7726          * Appends an event handler
7727          *
7728          * @param {String}   eventName     The type of event to append
7729          * @param {Function} fn        The method the event invokes
7730          * @param {Object} scope       (optional) The scope (this object) of the fn
7731          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7732          */
7733         addListener : function(eventName, fn, scope, options){
7734             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7735         },
7736
7737         /**
7738          * Removes an event handler from this element
7739          * @param {String} eventName the type of event to remove
7740          * @param {Function} fn the method the event invokes
7741          * @return {Roo.Element} this
7742          */
7743         removeListener : function(eventName, fn){
7744             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7745             return this;
7746         },
7747
7748         /**
7749          * Removes all previous added listeners from this element
7750          * @return {Roo.Element} this
7751          */
7752         removeAllListeners : function(){
7753             E.purgeElement(this.dom);
7754             return this;
7755         },
7756
7757         relayEvent : function(eventName, observable){
7758             this.on(eventName, function(e){
7759                 observable.fireEvent(eventName, e);
7760             });
7761         },
7762
7763         /**
7764          * Set the opacity of the element
7765          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setOpacity : function(opacity, animate){
7770             if(!animate || !A){
7771                 var s = this.dom.style;
7772                 if(Roo.isIE){
7773                     s.zoom = 1;
7774                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7775                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7776                 }else{
7777                     s.opacity = opacity;
7778                 }
7779             }else{
7780                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7781             }
7782             return this;
7783         },
7784
7785         /**
7786          * Gets the left X coordinate
7787          * @param {Boolean} local True to get the local css position instead of page coordinate
7788          * @return {Number}
7789          */
7790         getLeft : function(local){
7791             if(!local){
7792                 return this.getX();
7793             }else{
7794                 return parseInt(this.getStyle("left"), 10) || 0;
7795             }
7796         },
7797
7798         /**
7799          * Gets the right X coordinate of the element (element X position + element width)
7800          * @param {Boolean} local True to get the local css position instead of page coordinate
7801          * @return {Number}
7802          */
7803         getRight : function(local){
7804             if(!local){
7805                 return this.getX() + this.getWidth();
7806             }else{
7807                 return (this.getLeft(true) + this.getWidth()) || 0;
7808             }
7809         },
7810
7811         /**
7812          * Gets the top Y coordinate
7813          * @param {Boolean} local True to get the local css position instead of page coordinate
7814          * @return {Number}
7815          */
7816         getTop : function(local) {
7817             if(!local){
7818                 return this.getY();
7819             }else{
7820                 return parseInt(this.getStyle("top"), 10) || 0;
7821             }
7822         },
7823
7824         /**
7825          * Gets the bottom Y coordinate of the element (element Y position + element height)
7826          * @param {Boolean} local True to get the local css position instead of page coordinate
7827          * @return {Number}
7828          */
7829         getBottom : function(local){
7830             if(!local){
7831                 return this.getY() + this.getHeight();
7832             }else{
7833                 return (this.getTop(true) + this.getHeight()) || 0;
7834             }
7835         },
7836
7837         /**
7838         * Initializes positioning on this element. If a desired position is not passed, it will make the
7839         * the element positioned relative IF it is not already positioned.
7840         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7841         * @param {Number} zIndex (optional) The zIndex to apply
7842         * @param {Number} x (optional) Set the page X position
7843         * @param {Number} y (optional) Set the page Y position
7844         */
7845         position : function(pos, zIndex, x, y){
7846             if(!pos){
7847                if(this.getStyle('position') == 'static'){
7848                    this.setStyle('position', 'relative');
7849                }
7850             }else{
7851                 this.setStyle("position", pos);
7852             }
7853             if(zIndex){
7854                 this.setStyle("z-index", zIndex);
7855             }
7856             if(x !== undefined && y !== undefined){
7857                 this.setXY([x, y]);
7858             }else if(x !== undefined){
7859                 this.setX(x);
7860             }else if(y !== undefined){
7861                 this.setY(y);
7862             }
7863         },
7864
7865         /**
7866         * Clear positioning back to the default when the document was loaded
7867         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7868         * @return {Roo.Element} this
7869          */
7870         clearPositioning : function(value){
7871             value = value ||'';
7872             this.setStyle({
7873                 "left": value,
7874                 "right": value,
7875                 "top": value,
7876                 "bottom": value,
7877                 "z-index": "",
7878                 "position" : "static"
7879             });
7880             return this;
7881         },
7882
7883         /**
7884         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7885         * snapshot before performing an update and then restoring the element.
7886         * @return {Object}
7887         */
7888         getPositioning : function(){
7889             var l = this.getStyle("left");
7890             var t = this.getStyle("top");
7891             return {
7892                 "position" : this.getStyle("position"),
7893                 "left" : l,
7894                 "right" : l ? "" : this.getStyle("right"),
7895                 "top" : t,
7896                 "bottom" : t ? "" : this.getStyle("bottom"),
7897                 "z-index" : this.getStyle("z-index")
7898             };
7899         },
7900
7901         /**
7902          * Gets the width of the border(s) for the specified side(s)
7903          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7904          * passing lr would get the border (l)eft width + the border (r)ight width.
7905          * @return {Number} The width of the sides passed added together
7906          */
7907         getBorderWidth : function(side){
7908             return this.addStyles(side, El.borders);
7909         },
7910
7911         /**
7912          * Gets the width of the padding(s) for the specified side(s)
7913          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7914          * passing lr would get the padding (l)eft + the padding (r)ight.
7915          * @return {Number} The padding of the sides passed added together
7916          */
7917         getPadding : function(side){
7918             return this.addStyles(side, El.paddings);
7919         },
7920
7921         /**
7922         * Set positioning with an object returned by getPositioning().
7923         * @param {Object} posCfg
7924         * @return {Roo.Element} this
7925          */
7926         setPositioning : function(pc){
7927             this.applyStyles(pc);
7928             if(pc.right == "auto"){
7929                 this.dom.style.right = "";
7930             }
7931             if(pc.bottom == "auto"){
7932                 this.dom.style.bottom = "";
7933             }
7934             return this;
7935         },
7936
7937         // private
7938         fixDisplay : function(){
7939             if(this.getStyle("display") == "none"){
7940                 this.setStyle("visibility", "hidden");
7941                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7942                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7943                     this.setStyle("display", "block");
7944                 }
7945             }
7946         },
7947
7948         /**
7949          * Quick set left and top adding default units
7950          * @param {String} left The left CSS property value
7951          * @param {String} top The top CSS property value
7952          * @return {Roo.Element} this
7953          */
7954          setLeftTop : function(left, top){
7955             this.dom.style.left = this.addUnits(left);
7956             this.dom.style.top = this.addUnits(top);
7957             return this;
7958         },
7959
7960         /**
7961          * Move this element relative to its current position.
7962          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7963          * @param {Number} distance How far to move the element in pixels
7964          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7965          * @return {Roo.Element} this
7966          */
7967          move : function(direction, distance, animate){
7968             var xy = this.getXY();
7969             direction = direction.toLowerCase();
7970             switch(direction){
7971                 case "l":
7972                 case "left":
7973                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7974                     break;
7975                case "r":
7976                case "right":
7977                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7978                     break;
7979                case "t":
7980                case "top":
7981                case "up":
7982                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7983                     break;
7984                case "b":
7985                case "bottom":
7986                case "down":
7987                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7988                     break;
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7995          * @return {Roo.Element} this
7996          */
7997         clip : function(){
7998             if(!this.isClipped){
7999                this.isClipped = true;
8000                this.originalClip = {
8001                    "o": this.getStyle("overflow"),
8002                    "x": this.getStyle("overflow-x"),
8003                    "y": this.getStyle("overflow-y")
8004                };
8005                this.setStyle("overflow", "hidden");
8006                this.setStyle("overflow-x", "hidden");
8007                this.setStyle("overflow-y", "hidden");
8008             }
8009             return this;
8010         },
8011
8012         /**
8013          *  Return clipping (overflow) to original clipping before clip() was called
8014          * @return {Roo.Element} this
8015          */
8016         unclip : function(){
8017             if(this.isClipped){
8018                 this.isClipped = false;
8019                 var o = this.originalClip;
8020                 if(o.o){this.setStyle("overflow", o.o);}
8021                 if(o.x){this.setStyle("overflow-x", o.x);}
8022                 if(o.y){this.setStyle("overflow-y", o.y);}
8023             }
8024             return this;
8025         },
8026
8027
8028         /**
8029          * Gets the x,y coordinates specified by the anchor position on the element.
8030          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8031          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8032          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8033          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8034          * @return {Array} [x, y] An array containing the element's x and y coordinates
8035          */
8036         getAnchorXY : function(anchor, local, s){
8037             //Passing a different size is useful for pre-calculating anchors,
8038             //especially for anchored animations that change the el size.
8039
8040             var w, h, vp = false;
8041             if(!s){
8042                 var d = this.dom;
8043                 if(d == document.body || d == document){
8044                     vp = true;
8045                     w = D.getViewWidth(); h = D.getViewHeight();
8046                 }else{
8047                     w = this.getWidth(); h = this.getHeight();
8048                 }
8049             }else{
8050                 w = s.width;  h = s.height;
8051             }
8052             var x = 0, y = 0, r = Math.round;
8053             switch((anchor || "tl").toLowerCase()){
8054                 case "c":
8055                     x = r(w*.5);
8056                     y = r(h*.5);
8057                 break;
8058                 case "t":
8059                     x = r(w*.5);
8060                     y = 0;
8061                 break;
8062                 case "l":
8063                     x = 0;
8064                     y = r(h*.5);
8065                 break;
8066                 case "r":
8067                     x = w;
8068                     y = r(h*.5);
8069                 break;
8070                 case "b":
8071                     x = r(w*.5);
8072                     y = h;
8073                 break;
8074                 case "tl":
8075                     x = 0;
8076                     y = 0;
8077                 break;
8078                 case "bl":
8079                     x = 0;
8080                     y = h;
8081                 break;
8082                 case "br":
8083                     x = w;
8084                     y = h;
8085                 break;
8086                 case "tr":
8087                     x = w;
8088                     y = 0;
8089                 break;
8090             }
8091             if(local === true){
8092                 return [x, y];
8093             }
8094             if(vp){
8095                 var sc = this.getScroll();
8096                 return [x + sc.left, y + sc.top];
8097             }
8098             //Add the element's offset xy
8099             var o = this.getXY();
8100             return [x+o[0], y+o[1]];
8101         },
8102
8103         /**
8104          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8105          * supported position values.
8106          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8107          * @param {String} position The position to align to.
8108          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8109          * @return {Array} [x, y]
8110          */
8111         getAlignToXY : function(el, p, o){
8112             el = Roo.get(el);
8113             var d = this.dom;
8114             if(!el.dom){
8115                 throw "Element.alignTo with an element that doesn't exist";
8116             }
8117             var c = false; //constrain to viewport
8118             var p1 = "", p2 = "";
8119             o = o || [0,0];
8120
8121             if(!p){
8122                 p = "tl-bl";
8123             }else if(p == "?"){
8124                 p = "tl-bl?";
8125             }else if(p.indexOf("-") == -1){
8126                 p = "tl-" + p;
8127             }
8128             p = p.toLowerCase();
8129             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8130             if(!m){
8131                throw "Element.alignTo with an invalid alignment " + p;
8132             }
8133             p1 = m[1]; p2 = m[2]; c = !!m[3];
8134
8135             //Subtract the aligned el's internal xy from the target's offset xy
8136             //plus custom offset to get the aligned el's new offset xy
8137             var a1 = this.getAnchorXY(p1, true);
8138             var a2 = el.getAnchorXY(p2, false);
8139             var x = a2[0] - a1[0] + o[0];
8140             var y = a2[1] - a1[1] + o[1];
8141             if(c){
8142                 //constrain the aligned el to viewport if necessary
8143                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8144                 // 5px of margin for ie
8145                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8146
8147                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8148                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8149                 //otherwise swap the aligned el to the opposite border of the target.
8150                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8151                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8152                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8153                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8154
8155                var doc = document;
8156                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8157                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8158
8159                if((x+w) > dw + scrollX){
8160                     x = swapX ? r.left-w : dw+scrollX-w;
8161                 }
8162                if(x < scrollX){
8163                    x = swapX ? r.right : scrollX;
8164                }
8165                if((y+h) > dh + scrollY){
8166                     y = swapY ? r.top-h : dh+scrollY-h;
8167                 }
8168                if (y < scrollY){
8169                    y = swapY ? r.bottom : scrollY;
8170                }
8171             }
8172             return [x,y];
8173         },
8174
8175         // private
8176         getConstrainToXY : function(){
8177             var os = {top:0, left:0, bottom:0, right: 0};
8178
8179             return function(el, local, offsets, proposedXY){
8180                 el = Roo.get(el);
8181                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8182
8183                 var vw, vh, vx = 0, vy = 0;
8184                 if(el.dom == document.body || el.dom == document){
8185                     vw = Roo.lib.Dom.getViewWidth();
8186                     vh = Roo.lib.Dom.getViewHeight();
8187                 }else{
8188                     vw = el.dom.clientWidth;
8189                     vh = el.dom.clientHeight;
8190                     if(!local){
8191                         var vxy = el.getXY();
8192                         vx = vxy[0];
8193                         vy = vxy[1];
8194                     }
8195                 }
8196
8197                 var s = el.getScroll();
8198
8199                 vx += offsets.left + s.left;
8200                 vy += offsets.top + s.top;
8201
8202                 vw -= offsets.right;
8203                 vh -= offsets.bottom;
8204
8205                 var vr = vx+vw;
8206                 var vb = vy+vh;
8207
8208                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8209                 var x = xy[0], y = xy[1];
8210                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8211
8212                 // only move it if it needs it
8213                 var moved = false;
8214
8215                 // first validate right/bottom
8216                 if((x + w) > vr){
8217                     x = vr - w;
8218                     moved = true;
8219                 }
8220                 if((y + h) > vb){
8221                     y = vb - h;
8222                     moved = true;
8223                 }
8224                 // then make sure top/left isn't negative
8225                 if(x < vx){
8226                     x = vx;
8227                     moved = true;
8228                 }
8229                 if(y < vy){
8230                     y = vy;
8231                     moved = true;
8232                 }
8233                 return moved ? [x, y] : false;
8234             };
8235         }(),
8236
8237         // private
8238         adjustForConstraints : function(xy, parent, offsets){
8239             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8240         },
8241
8242         /**
8243          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8244          * document it aligns it to the viewport.
8245          * The position parameter is optional, and can be specified in any one of the following formats:
8246          * <ul>
8247          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8248          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8249          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8250          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8251          *   <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
8252          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8253          * </ul>
8254          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8255          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8256          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8257          * that specified in order to enforce the viewport constraints.
8258          * Following are all of the supported anchor positions:
8259     <pre>
8260     Value  Description
8261     -----  -----------------------------
8262     tl     The top left corner (default)
8263     t      The center of the top edge
8264     tr     The top right corner
8265     l      The center of the left edge
8266     c      In the center of the element
8267     r      The center of the right edge
8268     bl     The bottom left corner
8269     b      The center of the bottom edge
8270     br     The bottom right corner
8271     </pre>
8272     Example Usage:
8273     <pre><code>
8274     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8275     el.alignTo("other-el");
8276
8277     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8278     el.alignTo("other-el", "tr?");
8279
8280     // align the bottom right corner of el with the center left edge of other-el
8281     el.alignTo("other-el", "br-l?");
8282
8283     // align the center of el with the bottom left corner of other-el and
8284     // adjust the x position by -6 pixels (and the y position by 0)
8285     el.alignTo("other-el", "c-bl", [-6, 0]);
8286     </code></pre>
8287          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8288          * @param {String} position The position to align to.
8289          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8290          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8291          * @return {Roo.Element} this
8292          */
8293         alignTo : function(element, position, offsets, animate){
8294             var xy = this.getAlignToXY(element, position, offsets);
8295             this.setXY(xy, this.preanim(arguments, 3));
8296             return this;
8297         },
8298
8299         /**
8300          * Anchors an element to another element and realigns it when the window is resized.
8301          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8302          * @param {String} position The position to align to.
8303          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8304          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8305          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8306          * is a number, it is used as the buffer delay (defaults to 50ms).
8307          * @param {Function} callback The function to call after the animation finishes
8308          * @return {Roo.Element} this
8309          */
8310         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8311             var action = function(){
8312                 this.alignTo(el, alignment, offsets, animate);
8313                 Roo.callback(callback, this);
8314             };
8315             Roo.EventManager.onWindowResize(action, this);
8316             var tm = typeof monitorScroll;
8317             if(tm != 'undefined'){
8318                 Roo.EventManager.on(window, 'scroll', action, this,
8319                     {buffer: tm == 'number' ? monitorScroll : 50});
8320             }
8321             action.call(this); // align immediately
8322             return this;
8323         },
8324         /**
8325          * Clears any opacity settings from this element. Required in some cases for IE.
8326          * @return {Roo.Element} this
8327          */
8328         clearOpacity : function(){
8329             if (window.ActiveXObject) {
8330                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8331                     this.dom.style.filter = "";
8332                 }
8333             } else {
8334                 this.dom.style.opacity = "";
8335                 this.dom.style["-moz-opacity"] = "";
8336                 this.dom.style["-khtml-opacity"] = "";
8337             }
8338             return this;
8339         },
8340
8341         /**
8342          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8343          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8344          * @return {Roo.Element} this
8345          */
8346         hide : function(animate){
8347             this.setVisible(false, this.preanim(arguments, 0));
8348             return this;
8349         },
8350
8351         /**
8352         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8353         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8354          * @return {Roo.Element} this
8355          */
8356         show : function(animate){
8357             this.setVisible(true, this.preanim(arguments, 0));
8358             return this;
8359         },
8360
8361         /**
8362          * @private Test if size has a unit, otherwise appends the default
8363          */
8364         addUnits : function(size){
8365             return Roo.Element.addUnits(size, this.defaultUnit);
8366         },
8367
8368         /**
8369          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8370          * @return {Roo.Element} this
8371          */
8372         beginMeasure : function(){
8373             var el = this.dom;
8374             if(el.offsetWidth || el.offsetHeight){
8375                 return this; // offsets work already
8376             }
8377             var changed = [];
8378             var p = this.dom, b = document.body; // start with this element
8379             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8380                 var pe = Roo.get(p);
8381                 if(pe.getStyle('display') == 'none'){
8382                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8383                     p.style.visibility = "hidden";
8384                     p.style.display = "block";
8385                 }
8386                 p = p.parentNode;
8387             }
8388             this._measureChanged = changed;
8389             return this;
8390
8391         },
8392
8393         /**
8394          * Restores displays to before beginMeasure was called
8395          * @return {Roo.Element} this
8396          */
8397         endMeasure : function(){
8398             var changed = this._measureChanged;
8399             if(changed){
8400                 for(var i = 0, len = changed.length; i < len; i++) {
8401                     var r = changed[i];
8402                     r.el.style.visibility = r.visibility;
8403                     r.el.style.display = "none";
8404                 }
8405                 this._measureChanged = null;
8406             }
8407             return this;
8408         },
8409
8410         /**
8411         * Update the innerHTML of this element, optionally searching for and processing scripts
8412         * @param {String} html The new HTML
8413         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8414         * @param {Function} callback For async script loading you can be noticed when the update completes
8415         * @return {Roo.Element} this
8416          */
8417         update : function(html, loadScripts, callback){
8418             if(typeof html == "undefined"){
8419                 html = "";
8420             }
8421             if(loadScripts !== true){
8422                 this.dom.innerHTML = html;
8423                 if(typeof callback == "function"){
8424                     callback();
8425                 }
8426                 return this;
8427             }
8428             var id = Roo.id();
8429             var dom = this.dom;
8430
8431             html += '<span id="' + id + '"></span>';
8432
8433             E.onAvailable(id, function(){
8434                 var hd = document.getElementsByTagName("head")[0];
8435                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8436                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8437                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8438
8439                 var match;
8440                 while(match = re.exec(html)){
8441                     var attrs = match[1];
8442                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8443                     if(srcMatch && srcMatch[2]){
8444                        var s = document.createElement("script");
8445                        s.src = srcMatch[2];
8446                        var typeMatch = attrs.match(typeRe);
8447                        if(typeMatch && typeMatch[2]){
8448                            s.type = typeMatch[2];
8449                        }
8450                        hd.appendChild(s);
8451                     }else if(match[2] && match[2].length > 0){
8452                         if(window.execScript) {
8453                            window.execScript(match[2]);
8454                         } else {
8455                             /**
8456                              * eval:var:id
8457                              * eval:var:dom
8458                              * eval:var:html
8459                              * 
8460                              */
8461                            window.eval(match[2]);
8462                         }
8463                     }
8464                 }
8465                 var el = document.getElementById(id);
8466                 if(el){el.parentNode.removeChild(el);}
8467                 if(typeof callback == "function"){
8468                     callback();
8469                 }
8470             });
8471             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8472             return this;
8473         },
8474
8475         /**
8476          * Direct access to the UpdateManager update() method (takes the same parameters).
8477          * @param {String/Function} url The url for this request or a function to call to get the url
8478          * @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}
8479          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8480          * @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.
8481          * @return {Roo.Element} this
8482          */
8483         load : function(){
8484             var um = this.getUpdateManager();
8485             um.update.apply(um, arguments);
8486             return this;
8487         },
8488
8489         /**
8490         * Gets this element's UpdateManager
8491         * @return {Roo.UpdateManager} The UpdateManager
8492         */
8493         getUpdateManager : function(){
8494             if(!this.updateManager){
8495                 this.updateManager = new Roo.UpdateManager(this);
8496             }
8497             return this.updateManager;
8498         },
8499
8500         /**
8501          * Disables text selection for this element (normalized across browsers)
8502          * @return {Roo.Element} this
8503          */
8504         unselectable : function(){
8505             this.dom.unselectable = "on";
8506             this.swallowEvent("selectstart", true);
8507             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8508             this.addClass("x-unselectable");
8509             return this;
8510         },
8511
8512         /**
8513         * Calculates the x, y to center this element on the screen
8514         * @return {Array} The x, y values [x, y]
8515         */
8516         getCenterXY : function(){
8517             return this.getAlignToXY(document, 'c-c');
8518         },
8519
8520         /**
8521         * Centers the Element in either the viewport, or another Element.
8522         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8523         */
8524         center : function(centerIn){
8525             this.alignTo(centerIn || document, 'c-c');
8526             return this;
8527         },
8528
8529         /**
8530          * Tests various css rules/browsers to determine if this element uses a border box
8531          * @return {Boolean}
8532          */
8533         isBorderBox : function(){
8534             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8535         },
8536
8537         /**
8538          * Return a box {x, y, width, height} that can be used to set another elements
8539          * size/location to match this element.
8540          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8541          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8542          * @return {Object} box An object in the format {x, y, width, height}
8543          */
8544         getBox : function(contentBox, local){
8545             var xy;
8546             if(!local){
8547                 xy = this.getXY();
8548             }else{
8549                 var left = parseInt(this.getStyle("left"), 10) || 0;
8550                 var top = parseInt(this.getStyle("top"), 10) || 0;
8551                 xy = [left, top];
8552             }
8553             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8554             if(!contentBox){
8555                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8556             }else{
8557                 var l = this.getBorderWidth("l")+this.getPadding("l");
8558                 var r = this.getBorderWidth("r")+this.getPadding("r");
8559                 var t = this.getBorderWidth("t")+this.getPadding("t");
8560                 var b = this.getBorderWidth("b")+this.getPadding("b");
8561                 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)};
8562             }
8563             bx.right = bx.x + bx.width;
8564             bx.bottom = bx.y + bx.height;
8565             return bx;
8566         },
8567
8568         /**
8569          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8570          for more information about the sides.
8571          * @param {String} sides
8572          * @return {Number}
8573          */
8574         getFrameWidth : function(sides, onlyContentBox){
8575             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8576         },
8577
8578         /**
8579          * 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.
8580          * @param {Object} box The box to fill {x, y, width, height}
8581          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8582          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8583          * @return {Roo.Element} this
8584          */
8585         setBox : function(box, adjust, animate){
8586             var w = box.width, h = box.height;
8587             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8588                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8589                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8590             }
8591             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8592             return this;
8593         },
8594
8595         /**
8596          * Forces the browser to repaint this element
8597          * @return {Roo.Element} this
8598          */
8599          repaint : function(){
8600             var dom = this.dom;
8601             this.addClass("x-repaint");
8602             setTimeout(function(){
8603                 Roo.get(dom).removeClass("x-repaint");
8604             }, 1);
8605             return this;
8606         },
8607
8608         /**
8609          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8610          * then it returns the calculated width of the sides (see getPadding)
8611          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8612          * @return {Object/Number}
8613          */
8614         getMargins : function(side){
8615             if(!side){
8616                 return {
8617                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8618                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8619                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8620                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8621                 };
8622             }else{
8623                 return this.addStyles(side, El.margins);
8624              }
8625         },
8626
8627         // private
8628         addStyles : function(sides, styles){
8629             var val = 0, v, w;
8630             for(var i = 0, len = sides.length; i < len; i++){
8631                 v = this.getStyle(styles[sides.charAt(i)]);
8632                 if(v){
8633                      w = parseInt(v, 10);
8634                      if(w){ val += w; }
8635                 }
8636             }
8637             return val;
8638         },
8639
8640         /**
8641          * Creates a proxy element of this element
8642          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8643          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8644          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8645          * @return {Roo.Element} The new proxy element
8646          */
8647         createProxy : function(config, renderTo, matchBox){
8648             if(renderTo){
8649                 renderTo = Roo.getDom(renderTo);
8650             }else{
8651                 renderTo = document.body;
8652             }
8653             config = typeof config == "object" ?
8654                 config : {tag : "div", cls: config};
8655             var proxy = Roo.DomHelper.append(renderTo, config, true);
8656             if(matchBox){
8657                proxy.setBox(this.getBox());
8658             }
8659             return proxy;
8660         },
8661
8662         /**
8663          * Puts a mask over this element to disable user interaction. Requires core.css.
8664          * This method can only be applied to elements which accept child nodes.
8665          * @param {String} msg (optional) A message to display in the mask
8666          * @param {String} msgCls (optional) A css class to apply to the msg element
8667          * @return {Element} The mask  element
8668          */
8669         mask : function(msg, msgCls){
8670             if(this.getStyle("position") == "static"){
8671                 this.setStyle("position", "relative");
8672             }
8673             if(!this._mask){
8674                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8675             }
8676             this.addClass("x-masked");
8677             this._mask.setDisplayed(true);
8678             if(typeof msg == 'string'){
8679                 if(!this._maskMsg){
8680                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8681                 }
8682                 var mm = this._maskMsg;
8683                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8684                 mm.dom.firstChild.innerHTML = msg;
8685                 mm.setDisplayed(true);
8686                 mm.center(this);
8687             }
8688             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8689                 this._mask.setHeight(this.getHeight());
8690             }
8691             return this._mask;
8692         },
8693
8694         /**
8695          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8696          * it is cached for reuse.
8697          */
8698         unmask : function(removeEl){
8699             if(this._mask){
8700                 if(removeEl === true){
8701                     this._mask.remove();
8702                     delete this._mask;
8703                     if(this._maskMsg){
8704                         this._maskMsg.remove();
8705                         delete this._maskMsg;
8706                     }
8707                 }else{
8708                     this._mask.setDisplayed(false);
8709                     if(this._maskMsg){
8710                         this._maskMsg.setDisplayed(false);
8711                     }
8712                 }
8713             }
8714             this.removeClass("x-masked");
8715         },
8716
8717         /**
8718          * Returns true if this element is masked
8719          * @return {Boolean}
8720          */
8721         isMasked : function(){
8722             return this._mask && this._mask.isVisible();
8723         },
8724
8725         /**
8726          * Creates an iframe shim for this element to keep selects and other windowed objects from
8727          * showing through.
8728          * @return {Roo.Element} The new shim element
8729          */
8730         createShim : function(){
8731             var el = document.createElement('iframe');
8732             el.frameBorder = 'no';
8733             el.className = 'roo-shim';
8734             if(Roo.isIE && Roo.isSecure){
8735                 el.src = Roo.SSL_SECURE_URL;
8736             }
8737             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8738             shim.autoBoxAdjust = false;
8739             return shim;
8740         },
8741
8742         /**
8743          * Removes this element from the DOM and deletes it from the cache
8744          */
8745         remove : function(){
8746             if(this.dom.parentNode){
8747                 this.dom.parentNode.removeChild(this.dom);
8748             }
8749             delete El.cache[this.dom.id];
8750         },
8751
8752         /**
8753          * Sets up event handlers to add and remove a css class when the mouse is over this element
8754          * @param {String} className
8755          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8756          * mouseout events for children elements
8757          * @return {Roo.Element} this
8758          */
8759         addClassOnOver : function(className, preventFlicker){
8760             this.on("mouseover", function(){
8761                 Roo.fly(this, '_internal').addClass(className);
8762             }, this.dom);
8763             var removeFn = function(e){
8764                 if(preventFlicker !== true || !e.within(this, true)){
8765                     Roo.fly(this, '_internal').removeClass(className);
8766                 }
8767             };
8768             this.on("mouseout", removeFn, this.dom);
8769             return this;
8770         },
8771
8772         /**
8773          * Sets up event handlers to add and remove a css class when this element has the focus
8774          * @param {String} className
8775          * @return {Roo.Element} this
8776          */
8777         addClassOnFocus : function(className){
8778             this.on("focus", function(){
8779                 Roo.fly(this, '_internal').addClass(className);
8780             }, this.dom);
8781             this.on("blur", function(){
8782                 Roo.fly(this, '_internal').removeClass(className);
8783             }, this.dom);
8784             return this;
8785         },
8786         /**
8787          * 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)
8788          * @param {String} className
8789          * @return {Roo.Element} this
8790          */
8791         addClassOnClick : function(className){
8792             var dom = this.dom;
8793             this.on("mousedown", function(){
8794                 Roo.fly(dom, '_internal').addClass(className);
8795                 var d = Roo.get(document);
8796                 var fn = function(){
8797                     Roo.fly(dom, '_internal').removeClass(className);
8798                     d.removeListener("mouseup", fn);
8799                 };
8800                 d.on("mouseup", fn);
8801             });
8802             return this;
8803         },
8804
8805         /**
8806          * Stops the specified event from bubbling and optionally prevents the default action
8807          * @param {String} eventName
8808          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8809          * @return {Roo.Element} this
8810          */
8811         swallowEvent : function(eventName, preventDefault){
8812             var fn = function(e){
8813                 e.stopPropagation();
8814                 if(preventDefault){
8815                     e.preventDefault();
8816                 }
8817             };
8818             if(eventName instanceof Array){
8819                 for(var i = 0, len = eventName.length; i < len; i++){
8820                      this.on(eventName[i], fn);
8821                 }
8822                 return this;
8823             }
8824             this.on(eventName, fn);
8825             return this;
8826         },
8827
8828         /**
8829          * @private
8830          */
8831       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8832
8833         /**
8834          * Sizes this element to its parent element's dimensions performing
8835          * neccessary box adjustments.
8836          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8837          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8838          * @return {Roo.Element} this
8839          */
8840         fitToParent : function(monitorResize, targetParent) {
8841           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8842           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8843           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8844             return;
8845           }
8846           var p = Roo.get(targetParent || this.dom.parentNode);
8847           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8848           if (monitorResize === true) {
8849             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8850             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8851           }
8852           return this;
8853         },
8854
8855         /**
8856          * Gets the next sibling, skipping text nodes
8857          * @return {HTMLElement} The next sibling or null
8858          */
8859         getNextSibling : function(){
8860             var n = this.dom.nextSibling;
8861             while(n && n.nodeType != 1){
8862                 n = n.nextSibling;
8863             }
8864             return n;
8865         },
8866
8867         /**
8868          * Gets the previous sibling, skipping text nodes
8869          * @return {HTMLElement} The previous sibling or null
8870          */
8871         getPrevSibling : function(){
8872             var n = this.dom.previousSibling;
8873             while(n && n.nodeType != 1){
8874                 n = n.previousSibling;
8875             }
8876             return n;
8877         },
8878
8879
8880         /**
8881          * Appends the passed element(s) to this element
8882          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8883          * @return {Roo.Element} this
8884          */
8885         appendChild: function(el){
8886             el = Roo.get(el);
8887             el.appendTo(this);
8888             return this;
8889         },
8890
8891         /**
8892          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8893          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8894          * automatically generated with the specified attributes.
8895          * @param {HTMLElement} insertBefore (optional) a child element of this element
8896          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8897          * @return {Roo.Element} The new child element
8898          */
8899         createChild: function(config, insertBefore, returnDom){
8900             config = config || {tag:'div'};
8901             if(insertBefore){
8902                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8903             }
8904             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8905         },
8906
8907         /**
8908          * Appends this element to the passed element
8909          * @param {String/HTMLElement/Element} el The new parent element
8910          * @return {Roo.Element} this
8911          */
8912         appendTo: function(el){
8913             el = Roo.getDom(el);
8914             el.appendChild(this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Inserts this element before the passed element in the DOM
8920          * @param {String/HTMLElement/Element} el The element to insert before
8921          * @return {Roo.Element} this
8922          */
8923         insertBefore: function(el){
8924             el = Roo.getDom(el);
8925             el.parentNode.insertBefore(this.dom, el);
8926             return this;
8927         },
8928
8929         /**
8930          * Inserts this element after the passed element in the DOM
8931          * @param {String/HTMLElement/Element} el The element to insert after
8932          * @return {Roo.Element} this
8933          */
8934         insertAfter: function(el){
8935             el = Roo.getDom(el);
8936             el.parentNode.insertBefore(this.dom, el.nextSibling);
8937             return this;
8938         },
8939
8940         /**
8941          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8942          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8943          * @return {Roo.Element} The new child
8944          */
8945         insertFirst: function(el, returnDom){
8946             el = el || {};
8947             if(typeof el == 'object' && !el.nodeType){ // dh config
8948                 return this.createChild(el, this.dom.firstChild, returnDom);
8949             }else{
8950                 el = Roo.getDom(el);
8951                 this.dom.insertBefore(el, this.dom.firstChild);
8952                 return !returnDom ? Roo.get(el) : el;
8953             }
8954         },
8955
8956         /**
8957          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8958          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8959          * @param {String} where (optional) 'before' or 'after' defaults to before
8960          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8961          * @return {Roo.Element} the inserted Element
8962          */
8963         insertSibling: function(el, where, returnDom){
8964             where = where ? where.toLowerCase() : 'before';
8965             el = el || {};
8966             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8967
8968             if(typeof el == 'object' && !el.nodeType){ // dh config
8969                 if(where == 'after' && !this.dom.nextSibling){
8970                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8971                 }else{
8972                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8973                 }
8974
8975             }else{
8976                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8977                             where == 'before' ? this.dom : this.dom.nextSibling);
8978                 if(!returnDom){
8979                     rt = Roo.get(rt);
8980                 }
8981             }
8982             return rt;
8983         },
8984
8985         /**
8986          * Creates and wraps this element with another element
8987          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8988          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8989          * @return {HTMLElement/Element} The newly created wrapper element
8990          */
8991         wrap: function(config, returnDom){
8992             if(!config){
8993                 config = {tag: "div"};
8994             }
8995             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8996             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8997             return newEl;
8998         },
8999
9000         /**
9001          * Replaces the passed element with this element
9002          * @param {String/HTMLElement/Element} el The element to replace
9003          * @return {Roo.Element} this
9004          */
9005         replace: function(el){
9006             el = Roo.get(el);
9007             this.insertBefore(el);
9008             el.remove();
9009             return this;
9010         },
9011
9012         /**
9013          * Inserts an html fragment into this element
9014          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9015          * @param {String} html The HTML fragment
9016          * @param {Boolean} returnEl True to return an Roo.Element
9017          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9018          */
9019         insertHtml : function(where, html, returnEl){
9020             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9021             return returnEl ? Roo.get(el) : el;
9022         },
9023
9024         /**
9025          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9026          * @param {Object} o The object with the attributes
9027          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9028          * @return {Roo.Element} this
9029          */
9030         set : function(o, useSet){
9031             var el = this.dom;
9032             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9033             for(var attr in o){
9034                 if(attr == "style" || typeof o[attr] == "function") continue;
9035                 if(attr=="cls"){
9036                     el.className = o["cls"];
9037                 }else{
9038                     if(useSet) el.setAttribute(attr, o[attr]);
9039                     else el[attr] = o[attr];
9040                 }
9041             }
9042             if(o.style){
9043                 Roo.DomHelper.applyStyles(el, o.style);
9044             }
9045             return this;
9046         },
9047
9048         /**
9049          * Convenience method for constructing a KeyMap
9050          * @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:
9051          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9052          * @param {Function} fn The function to call
9053          * @param {Object} scope (optional) The scope of the function
9054          * @return {Roo.KeyMap} The KeyMap created
9055          */
9056         addKeyListener : function(key, fn, scope){
9057             var config;
9058             if(typeof key != "object" || key instanceof Array){
9059                 config = {
9060                     key: key,
9061                     fn: fn,
9062                     scope: scope
9063                 };
9064             }else{
9065                 config = {
9066                     key : key.key,
9067                     shift : key.shift,
9068                     ctrl : key.ctrl,
9069                     alt : key.alt,
9070                     fn: fn,
9071                     scope: scope
9072                 };
9073             }
9074             return new Roo.KeyMap(this, config);
9075         },
9076
9077         /**
9078          * Creates a KeyMap for this element
9079          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9080          * @return {Roo.KeyMap} The KeyMap created
9081          */
9082         addKeyMap : function(config){
9083             return new Roo.KeyMap(this, config);
9084         },
9085
9086         /**
9087          * Returns true if this element is scrollable.
9088          * @return {Boolean}
9089          */
9090          isScrollable : function(){
9091             var dom = this.dom;
9092             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9093         },
9094
9095         /**
9096          * 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().
9097          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9098          * @param {Number} value The new scroll value
9099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9100          * @return {Element} this
9101          */
9102
9103         scrollTo : function(side, value, animate){
9104             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9105             if(!animate || !A){
9106                 this.dom[prop] = value;
9107             }else{
9108                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9109                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9110             }
9111             return this;
9112         },
9113
9114         /**
9115          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9116          * within this element's scrollable range.
9117          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9118          * @param {Number} distance How far to scroll the element in pixels
9119          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9120          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9121          * was scrolled as far as it could go.
9122          */
9123          scroll : function(direction, distance, animate){
9124              if(!this.isScrollable()){
9125                  return;
9126              }
9127              var el = this.dom;
9128              var l = el.scrollLeft, t = el.scrollTop;
9129              var w = el.scrollWidth, h = el.scrollHeight;
9130              var cw = el.clientWidth, ch = el.clientHeight;
9131              direction = direction.toLowerCase();
9132              var scrolled = false;
9133              var a = this.preanim(arguments, 2);
9134              switch(direction){
9135                  case "l":
9136                  case "left":
9137                      if(w - l > cw){
9138                          var v = Math.min(l + distance, w-cw);
9139                          this.scrollTo("left", v, a);
9140                          scrolled = true;
9141                      }
9142                      break;
9143                 case "r":
9144                 case "right":
9145                      if(l > 0){
9146                          var v = Math.max(l - distance, 0);
9147                          this.scrollTo("left", v, a);
9148                          scrolled = true;
9149                      }
9150                      break;
9151                 case "t":
9152                 case "top":
9153                 case "up":
9154                      if(t > 0){
9155                          var v = Math.max(t - distance, 0);
9156                          this.scrollTo("top", v, a);
9157                          scrolled = true;
9158                      }
9159                      break;
9160                 case "b":
9161                 case "bottom":
9162                 case "down":
9163                      if(h - t > ch){
9164                          var v = Math.min(t + distance, h-ch);
9165                          this.scrollTo("top", v, a);
9166                          scrolled = true;
9167                      }
9168                      break;
9169              }
9170              return scrolled;
9171         },
9172
9173         /**
9174          * Translates the passed page coordinates into left/top css values for this element
9175          * @param {Number/Array} x The page x or an array containing [x, y]
9176          * @param {Number} y The page y
9177          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9178          */
9179         translatePoints : function(x, y){
9180             if(typeof x == 'object' || x instanceof Array){
9181                 y = x[1]; x = x[0];
9182             }
9183             var p = this.getStyle('position');
9184             var o = this.getXY();
9185
9186             var l = parseInt(this.getStyle('left'), 10);
9187             var t = parseInt(this.getStyle('top'), 10);
9188
9189             if(isNaN(l)){
9190                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9191             }
9192             if(isNaN(t)){
9193                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9194             }
9195
9196             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9197         },
9198
9199         /**
9200          * Returns the current scroll position of the element.
9201          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9202          */
9203         getScroll : function(){
9204             var d = this.dom, doc = document;
9205             if(d == doc || d == doc.body){
9206                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9207                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9208                 return {left: l, top: t};
9209             }else{
9210                 return {left: d.scrollLeft, top: d.scrollTop};
9211             }
9212         },
9213
9214         /**
9215          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9216          * are convert to standard 6 digit hex color.
9217          * @param {String} attr The css attribute
9218          * @param {String} defaultValue The default value to use when a valid color isn't found
9219          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9220          * YUI color anims.
9221          */
9222         getColor : function(attr, defaultValue, prefix){
9223             var v = this.getStyle(attr);
9224             if(!v || v == "transparent" || v == "inherit") {
9225                 return defaultValue;
9226             }
9227             var color = typeof prefix == "undefined" ? "#" : prefix;
9228             if(v.substr(0, 4) == "rgb("){
9229                 var rvs = v.slice(4, v.length -1).split(",");
9230                 for(var i = 0; i < 3; i++){
9231                     var h = parseInt(rvs[i]).toString(16);
9232                     if(h < 16){
9233                         h = "0" + h;
9234                     }
9235                     color += h;
9236                 }
9237             } else {
9238                 if(v.substr(0, 1) == "#"){
9239                     if(v.length == 4) {
9240                         for(var i = 1; i < 4; i++){
9241                             var c = v.charAt(i);
9242                             color +=  c + c;
9243                         }
9244                     }else if(v.length == 7){
9245                         color += v.substr(1);
9246                     }
9247                 }
9248             }
9249             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9250         },
9251
9252         /**
9253          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9254          * gradient background, rounded corners and a 4-way shadow.
9255          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9256          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9257          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9258          * @return {Roo.Element} this
9259          */
9260         boxWrap : function(cls){
9261             cls = cls || 'x-box';
9262             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9263             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9264             return el;
9265         },
9266
9267         /**
9268          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9269          * @param {String} namespace The namespace in which to look for the attribute
9270          * @param {String} name The attribute name
9271          * @return {String} The attribute value
9272          */
9273         getAttributeNS : Roo.isIE ? function(ns, name){
9274             var d = this.dom;
9275             var type = typeof d[ns+":"+name];
9276             if(type != 'undefined' && type != 'unknown'){
9277                 return d[ns+":"+name];
9278             }
9279             return d[name];
9280         } : function(ns, name){
9281             var d = this.dom;
9282             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9283         }
9284     };
9285
9286     var ep = El.prototype;
9287
9288     /**
9289      * Appends an event handler (Shorthand for addListener)
9290      * @param {String}   eventName     The type of event to append
9291      * @param {Function} fn        The method the event invokes
9292      * @param {Object} scope       (optional) The scope (this object) of the fn
9293      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9294      * @method
9295      */
9296     ep.on = ep.addListener;
9297         // backwards compat
9298     ep.mon = ep.addListener;
9299
9300     /**
9301      * Removes an event handler from this element (shorthand for removeListener)
9302      * @param {String} eventName the type of event to remove
9303      * @param {Function} fn the method the event invokes
9304      * @return {Roo.Element} this
9305      * @method
9306      */
9307     ep.un = ep.removeListener;
9308
9309     /**
9310      * true to automatically adjust width and height settings for box-model issues (default to true)
9311      */
9312     ep.autoBoxAdjust = true;
9313
9314     // private
9315     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9316
9317     // private
9318     El.addUnits = function(v, defaultUnit){
9319         if(v === "" || v == "auto"){
9320             return v;
9321         }
9322         if(v === undefined){
9323             return '';
9324         }
9325         if(typeof v == "number" || !El.unitPattern.test(v)){
9326             return v + (defaultUnit || 'px');
9327         }
9328         return v;
9329     };
9330
9331     // special markup used throughout Roo when box wrapping elements
9332     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>';
9333     /**
9334      * Visibility mode constant - Use visibility to hide element
9335      * @static
9336      * @type Number
9337      */
9338     El.VISIBILITY = 1;
9339     /**
9340      * Visibility mode constant - Use display to hide element
9341      * @static
9342      * @type Number
9343      */
9344     El.DISPLAY = 2;
9345
9346     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9347     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9348     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9349
9350
9351
9352     /**
9353      * @private
9354      */
9355     El.cache = {};
9356
9357     var docEl;
9358
9359     /**
9360      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9361      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9362      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9363      * @return {Element} The Element object
9364      * @static
9365      */
9366     El.get = function(el){
9367         var ex, elm, id;
9368         if(!el){ return null; }
9369         if(typeof el == "string"){ // element id
9370             if(!(elm = document.getElementById(el))){
9371                 return null;
9372             }
9373             if(ex = El.cache[el]){
9374                 ex.dom = elm;
9375             }else{
9376                 ex = El.cache[el] = new El(elm);
9377             }
9378             return ex;
9379         }else if(el.tagName){ // dom element
9380             if(!(id = el.id)){
9381                 id = Roo.id(el);
9382             }
9383             if(ex = El.cache[id]){
9384                 ex.dom = el;
9385             }else{
9386                 ex = El.cache[id] = new El(el);
9387             }
9388             return ex;
9389         }else if(el instanceof El){
9390             if(el != docEl){
9391                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9392                                                               // catch case where it hasn't been appended
9393                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9394             }
9395             return el;
9396         }else if(el.isComposite){
9397             return el;
9398         }else if(el instanceof Array){
9399             return El.select(el);
9400         }else if(el == document){
9401             // create a bogus element object representing the document object
9402             if(!docEl){
9403                 var f = function(){};
9404                 f.prototype = El.prototype;
9405                 docEl = new f();
9406                 docEl.dom = document;
9407             }
9408             return docEl;
9409         }
9410         return null;
9411     };
9412
9413     // private
9414     El.uncache = function(el){
9415         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9416             if(a[i]){
9417                 delete El.cache[a[i].id || a[i]];
9418             }
9419         }
9420     };
9421
9422     // private
9423     // Garbage collection - uncache elements/purge listeners on orphaned elements
9424     // so we don't hold a reference and cause the browser to retain them
9425     El.garbageCollect = function(){
9426         if(!Roo.enableGarbageCollector){
9427             clearInterval(El.collectorThread);
9428             return;
9429         }
9430         for(var eid in El.cache){
9431             var el = El.cache[eid], d = el.dom;
9432             // -------------------------------------------------------
9433             // Determining what is garbage:
9434             // -------------------------------------------------------
9435             // !d
9436             // dom node is null, definitely garbage
9437             // -------------------------------------------------------
9438             // !d.parentNode
9439             // no parentNode == direct orphan, definitely garbage
9440             // -------------------------------------------------------
9441             // !d.offsetParent && !document.getElementById(eid)
9442             // display none elements have no offsetParent so we will
9443             // also try to look it up by it's id. However, check
9444             // offsetParent first so we don't do unneeded lookups.
9445             // This enables collection of elements that are not orphans
9446             // directly, but somewhere up the line they have an orphan
9447             // parent.
9448             // -------------------------------------------------------
9449             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9450                 delete El.cache[eid];
9451                 if(d && Roo.enableListenerCollection){
9452                     E.purgeElement(d);
9453                 }
9454             }
9455         }
9456     }
9457     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9458
9459
9460     // dom is optional
9461     El.Flyweight = function(dom){
9462         this.dom = dom;
9463     };
9464     El.Flyweight.prototype = El.prototype;
9465
9466     El._flyweights = {};
9467     /**
9468      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9469      * the dom node can be overwritten by other code.
9470      * @param {String/HTMLElement} el The dom node or id
9471      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9472      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9473      * @static
9474      * @return {Element} The shared Element object
9475      */
9476     El.fly = function(el, named){
9477         named = named || '_global';
9478         el = Roo.getDom(el);
9479         if(!el){
9480             return null;
9481         }
9482         if(!El._flyweights[named]){
9483             El._flyweights[named] = new El.Flyweight();
9484         }
9485         El._flyweights[named].dom = el;
9486         return El._flyweights[named];
9487     };
9488
9489     /**
9490      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9491      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9492      * Shorthand of {@link Roo.Element#get}
9493      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9494      * @return {Element} The Element object
9495      * @member Roo
9496      * @method get
9497      */
9498     Roo.get = El.get;
9499     /**
9500      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9501      * the dom node can be overwritten by other code.
9502      * Shorthand of {@link Roo.Element#fly}
9503      * @param {String/HTMLElement} el The dom node or id
9504      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9505      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9506      * @static
9507      * @return {Element} The shared Element object
9508      * @member Roo
9509      * @method fly
9510      */
9511     Roo.fly = El.fly;
9512
9513     // speedy lookup for elements never to box adjust
9514     var noBoxAdjust = Roo.isStrict ? {
9515         select:1
9516     } : {
9517         input:1, select:1, textarea:1
9518     };
9519     if(Roo.isIE || Roo.isGecko){
9520         noBoxAdjust['button'] = 1;
9521     }
9522
9523
9524     Roo.EventManager.on(window, 'unload', function(){
9525         delete El.cache;
9526         delete El._flyweights;
9527     });
9528 })();
9529
9530
9531
9532
9533 if(Roo.DomQuery){
9534     Roo.Element.selectorFunction = Roo.DomQuery.select;
9535 }
9536
9537 Roo.Element.select = function(selector, unique, root){
9538     var els;
9539     if(typeof selector == "string"){
9540         els = Roo.Element.selectorFunction(selector, root);
9541     }else if(selector.length !== undefined){
9542         els = selector;
9543     }else{
9544         throw "Invalid selector";
9545     }
9546     if(unique === true){
9547         return new Roo.CompositeElement(els);
9548     }else{
9549         return new Roo.CompositeElementLite(els);
9550     }
9551 };
9552 /**
9553  * Selects elements based on the passed CSS selector to enable working on them as 1.
9554  * @param {String/Array} selector The CSS selector or an array of elements
9555  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9556  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9557  * @return {CompositeElementLite/CompositeElement}
9558  * @member Roo
9559  * @method select
9560  */
9561 Roo.select = Roo.Element.select;
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576 /*
9577  * Based on:
9578  * Ext JS Library 1.1.1
9579  * Copyright(c) 2006-2007, Ext JS, LLC.
9580  *
9581  * Originally Released Under LGPL - original licence link has changed is not relivant.
9582  *
9583  * Fork - LGPL
9584  * <script type="text/javascript">
9585  */
9586
9587
9588
9589 //Notifies Element that fx methods are available
9590 Roo.enableFx = true;
9591
9592 /**
9593  * @class Roo.Fx
9594  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9595  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9596  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9597  * Element effects to work.</p><br/>
9598  *
9599  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9600  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9601  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9602  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9603  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9604  * expected results and should be done with care.</p><br/>
9605  *
9606  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9607  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9608 <pre>
9609 Value  Description
9610 -----  -----------------------------
9611 tl     The top left corner
9612 t      The center of the top edge
9613 tr     The top right corner
9614 l      The center of the left edge
9615 r      The center of the right edge
9616 bl     The bottom left corner
9617 b      The center of the bottom edge
9618 br     The bottom right corner
9619 </pre>
9620  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9621  * below are common options that can be passed to any Fx method.</b>
9622  * @cfg {Function} callback A function called when the effect is finished
9623  * @cfg {Object} scope The scope of the effect function
9624  * @cfg {String} easing A valid Easing value for the effect
9625  * @cfg {String} afterCls A css class to apply after the effect
9626  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9627  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9628  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9629  * effects that end with the element being visually hidden, ignored otherwise)
9630  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9631  * a function which returns such a specification that will be applied to the Element after the effect finishes
9632  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9633  * @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
9634  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9635  */
9636 Roo.Fx = {
9637         /**
9638          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9639          * origin for the slide effect.  This function automatically handles wrapping the element with
9640          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9641          * Usage:
9642          *<pre><code>
9643 // default: slide the element in from the top
9644 el.slideIn();
9645
9646 // custom: slide the element in from the right with a 2-second duration
9647 el.slideIn('r', { duration: 2 });
9648
9649 // common config options shown with default values
9650 el.slideIn('t', {
9651     easing: 'easeOut',
9652     duration: .5
9653 });
9654 </code></pre>
9655          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9656          * @param {Object} options (optional) Object literal with any of the Fx config options
9657          * @return {Roo.Element} The Element
9658          */
9659     slideIn : function(anchor, o){
9660         var el = this.getFxEl();
9661         o = o || {};
9662
9663         el.queueFx(o, function(){
9664
9665             anchor = anchor || "t";
9666
9667             // fix display to visibility
9668             this.fixDisplay();
9669
9670             // restore values after effect
9671             var r = this.getFxRestore();
9672             var b = this.getBox();
9673             // fixed size for slide
9674             this.setSize(b);
9675
9676             // wrap if needed
9677             var wrap = this.fxWrap(r.pos, o, "hidden");
9678
9679             var st = this.dom.style;
9680             st.visibility = "visible";
9681             st.position = "absolute";
9682
9683             // clear out temp styles after slide and unwrap
9684             var after = function(){
9685                 el.fxUnwrap(wrap, r.pos, o);
9686                 st.width = r.width;
9687                 st.height = r.height;
9688                 el.afterFx(o);
9689             };
9690             // time to calc the positions
9691             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9692
9693             switch(anchor.toLowerCase()){
9694                 case "t":
9695                     wrap.setSize(b.width, 0);
9696                     st.left = st.bottom = "0";
9697                     a = {height: bh};
9698                 break;
9699                 case "l":
9700                     wrap.setSize(0, b.height);
9701                     st.right = st.top = "0";
9702                     a = {width: bw};
9703                 break;
9704                 case "r":
9705                     wrap.setSize(0, b.height);
9706                     wrap.setX(b.right);
9707                     st.left = st.top = "0";
9708                     a = {width: bw, points: pt};
9709                 break;
9710                 case "b":
9711                     wrap.setSize(b.width, 0);
9712                     wrap.setY(b.bottom);
9713                     st.left = st.top = "0";
9714                     a = {height: bh, points: pt};
9715                 break;
9716                 case "tl":
9717                     wrap.setSize(0, 0);
9718                     st.right = st.bottom = "0";
9719                     a = {width: bw, height: bh};
9720                 break;
9721                 case "bl":
9722                     wrap.setSize(0, 0);
9723                     wrap.setY(b.y+b.height);
9724                     st.right = st.top = "0";
9725                     a = {width: bw, height: bh, points: pt};
9726                 break;
9727                 case "br":
9728                     wrap.setSize(0, 0);
9729                     wrap.setXY([b.right, b.bottom]);
9730                     st.left = st.top = "0";
9731                     a = {width: bw, height: bh, points: pt};
9732                 break;
9733                 case "tr":
9734                     wrap.setSize(0, 0);
9735                     wrap.setX(b.x+b.width);
9736                     st.left = st.bottom = "0";
9737                     a = {width: bw, height: bh, points: pt};
9738                 break;
9739             }
9740             this.dom.style.visibility = "visible";
9741             wrap.show();
9742
9743             arguments.callee.anim = wrap.fxanim(a,
9744                 o,
9745                 'motion',
9746                 .5,
9747                 'easeOut', after);
9748         });
9749         return this;
9750     },
9751     
9752         /**
9753          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9754          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9755          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9756          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9757          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9758          * Usage:
9759          *<pre><code>
9760 // default: slide the element out to the top
9761 el.slideOut();
9762
9763 // custom: slide the element out to the right with a 2-second duration
9764 el.slideOut('r', { duration: 2 });
9765
9766 // common config options shown with default values
9767 el.slideOut('t', {
9768     easing: 'easeOut',
9769     duration: .5,
9770     remove: false,
9771     useDisplay: false
9772 });
9773 </code></pre>
9774          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9775          * @param {Object} options (optional) Object literal with any of the Fx config options
9776          * @return {Roo.Element} The Element
9777          */
9778     slideOut : function(anchor, o){
9779         var el = this.getFxEl();
9780         o = o || {};
9781
9782         el.queueFx(o, function(){
9783
9784             anchor = anchor || "t";
9785
9786             // restore values after effect
9787             var r = this.getFxRestore();
9788             
9789             var b = this.getBox();
9790             // fixed size for slide
9791             this.setSize(b);
9792
9793             // wrap if needed
9794             var wrap = this.fxWrap(r.pos, o, "visible");
9795
9796             var st = this.dom.style;
9797             st.visibility = "visible";
9798             st.position = "absolute";
9799
9800             wrap.setSize(b);
9801
9802             var after = function(){
9803                 if(o.useDisplay){
9804                     el.setDisplayed(false);
9805                 }else{
9806                     el.hide();
9807                 }
9808
9809                 el.fxUnwrap(wrap, r.pos, o);
9810
9811                 st.width = r.width;
9812                 st.height = r.height;
9813
9814                 el.afterFx(o);
9815             };
9816
9817             var a, zero = {to: 0};
9818             switch(anchor.toLowerCase()){
9819                 case "t":
9820                     st.left = st.bottom = "0";
9821                     a = {height: zero};
9822                 break;
9823                 case "l":
9824                     st.right = st.top = "0";
9825                     a = {width: zero};
9826                 break;
9827                 case "r":
9828                     st.left = st.top = "0";
9829                     a = {width: zero, points: {to:[b.right, b.y]}};
9830                 break;
9831                 case "b":
9832                     st.left = st.top = "0";
9833                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9834                 break;
9835                 case "tl":
9836                     st.right = st.bottom = "0";
9837                     a = {width: zero, height: zero};
9838                 break;
9839                 case "bl":
9840                     st.right = st.top = "0";
9841                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9842                 break;
9843                 case "br":
9844                     st.left = st.top = "0";
9845                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9846                 break;
9847                 case "tr":
9848                     st.left = st.bottom = "0";
9849                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9850                 break;
9851             }
9852
9853             arguments.callee.anim = wrap.fxanim(a,
9854                 o,
9855                 'motion',
9856                 .5,
9857                 "easeOut", after);
9858         });
9859         return this;
9860     },
9861
9862         /**
9863          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9864          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9865          * The element must be removed from the DOM using the 'remove' config option if desired.
9866          * Usage:
9867          *<pre><code>
9868 // default
9869 el.puff();
9870
9871 // common config options shown with default values
9872 el.puff({
9873     easing: 'easeOut',
9874     duration: .5,
9875     remove: false,
9876     useDisplay: false
9877 });
9878 </code></pre>
9879          * @param {Object} options (optional) Object literal with any of the Fx config options
9880          * @return {Roo.Element} The Element
9881          */
9882     puff : function(o){
9883         var el = this.getFxEl();
9884         o = o || {};
9885
9886         el.queueFx(o, function(){
9887             this.clearOpacity();
9888             this.show();
9889
9890             // restore values after effect
9891             var r = this.getFxRestore();
9892             var st = this.dom.style;
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.clearOpacity();
9902
9903                 el.setPositioning(r.pos);
9904                 st.width = r.width;
9905                 st.height = r.height;
9906                 st.fontSize = '';
9907                 el.afterFx(o);
9908             };
9909
9910             var width = this.getWidth();
9911             var height = this.getHeight();
9912
9913             arguments.callee.anim = this.fxanim({
9914                     width : {to: this.adjustWidth(width * 2)},
9915                     height : {to: this.adjustHeight(height * 2)},
9916                     points : {by: [-(width * .5), -(height * .5)]},
9917                     opacity : {to: 0},
9918                     fontSize: {to:200, unit: "%"}
9919                 },
9920                 o,
9921                 'motion',
9922                 .5,
9923                 "easeOut", after);
9924         });
9925         return this;
9926     },
9927
9928         /**
9929          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9930          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9931          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9932          * Usage:
9933          *<pre><code>
9934 // default
9935 el.switchOff();
9936
9937 // all config options shown with default values
9938 el.switchOff({
9939     easing: 'easeIn',
9940     duration: .3,
9941     remove: false,
9942     useDisplay: false
9943 });
9944 </code></pre>
9945          * @param {Object} options (optional) Object literal with any of the Fx config options
9946          * @return {Roo.Element} The Element
9947          */
9948     switchOff : function(o){
9949         var el = this.getFxEl();
9950         o = o || {};
9951
9952         el.queueFx(o, function(){
9953             this.clearOpacity();
9954             this.clip();
9955
9956             // restore values after effect
9957             var r = this.getFxRestore();
9958             var st = this.dom.style;
9959
9960             var after = function(){
9961                 if(o.useDisplay){
9962                     el.setDisplayed(false);
9963                 }else{
9964                     el.hide();
9965                 }
9966
9967                 el.clearOpacity();
9968                 el.setPositioning(r.pos);
9969                 st.width = r.width;
9970                 st.height = r.height;
9971
9972                 el.afterFx(o);
9973             };
9974
9975             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9976                 this.clearOpacity();
9977                 (function(){
9978                     this.fxanim({
9979                         height:{to:1},
9980                         points:{by:[0, this.getHeight() * .5]}
9981                     }, o, 'motion', 0.3, 'easeIn', after);
9982                 }).defer(100, this);
9983             });
9984         });
9985         return this;
9986     },
9987
9988     /**
9989      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9990      * changed using the "attr" config option) and then fading back to the original color. If no original
9991      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9992      * Usage:
9993 <pre><code>
9994 // default: highlight background to yellow
9995 el.highlight();
9996
9997 // custom: highlight foreground text to blue for 2 seconds
9998 el.highlight("0000ff", { attr: 'color', duration: 2 });
9999
10000 // common config options shown with default values
10001 el.highlight("ffff9c", {
10002     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10003     endColor: (current color) or "ffffff",
10004     easing: 'easeIn',
10005     duration: 1
10006 });
10007 </code></pre>
10008      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10009      * @param {Object} options (optional) Object literal with any of the Fx config options
10010      * @return {Roo.Element} The Element
10011      */ 
10012     highlight : function(color, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017             color = color || "ffff9c";
10018             attr = o.attr || "backgroundColor";
10019
10020             this.clearOpacity();
10021             this.show();
10022
10023             var origColor = this.getColor(attr);
10024             var restoreColor = this.dom.style[attr];
10025             endColor = (o.endColor || origColor) || "ffffff";
10026
10027             var after = function(){
10028                 el.dom.style[attr] = restoreColor;
10029                 el.afterFx(o);
10030             };
10031
10032             var a = {};
10033             a[attr] = {from: color, to: endColor};
10034             arguments.callee.anim = this.fxanim(a,
10035                 o,
10036                 'color',
10037                 1,
10038                 'easeIn', after);
10039         });
10040         return this;
10041     },
10042
10043    /**
10044     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10045     * Usage:
10046 <pre><code>
10047 // default: a single light blue ripple
10048 el.frame();
10049
10050 // custom: 3 red ripples lasting 3 seconds total
10051 el.frame("ff0000", 3, { duration: 3 });
10052
10053 // common config options shown with default values
10054 el.frame("C3DAF9", 1, {
10055     duration: 1 //duration of entire animation (not each individual ripple)
10056     // Note: Easing is not configurable and will be ignored if included
10057 });
10058 </code></pre>
10059     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10060     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10061     * @param {Object} options (optional) Object literal with any of the Fx config options
10062     * @return {Roo.Element} The Element
10063     */
10064     frame : function(color, count, o){
10065         var el = this.getFxEl();
10066         o = o || {};
10067
10068         el.queueFx(o, function(){
10069             color = color || "#C3DAF9";
10070             if(color.length == 6){
10071                 color = "#" + color;
10072             }
10073             count = count || 1;
10074             duration = o.duration || 1;
10075             this.show();
10076
10077             var b = this.getBox();
10078             var animFn = function(){
10079                 var proxy = this.createProxy({
10080
10081                      style:{
10082                         visbility:"hidden",
10083                         position:"absolute",
10084                         "z-index":"35000", // yee haw
10085                         border:"0px solid " + color
10086                      }
10087                   });
10088                 var scale = Roo.isBorderBox ? 2 : 1;
10089                 proxy.animate({
10090                     top:{from:b.y, to:b.y - 20},
10091                     left:{from:b.x, to:b.x - 20},
10092                     borderWidth:{from:0, to:10},
10093                     opacity:{from:1, to:0},
10094                     height:{from:b.height, to:(b.height + (20*scale))},
10095                     width:{from:b.width, to:(b.width + (20*scale))}
10096                 }, duration, function(){
10097                     proxy.remove();
10098                 });
10099                 if(--count > 0){
10100                      animFn.defer((duration/2)*1000, this);
10101                 }else{
10102                     el.afterFx(o);
10103                 }
10104             };
10105             animFn.call(this);
10106         });
10107         return this;
10108     },
10109
10110    /**
10111     * Creates a pause before any subsequent queued effects begin.  If there are
10112     * no effects queued after the pause it will have no effect.
10113     * Usage:
10114 <pre><code>
10115 el.pause(1);
10116 </code></pre>
10117     * @param {Number} seconds The length of time to pause (in seconds)
10118     * @return {Roo.Element} The Element
10119     */
10120     pause : function(seconds){
10121         var el = this.getFxEl();
10122         var o = {};
10123
10124         el.queueFx(o, function(){
10125             setTimeout(function(){
10126                 el.afterFx(o);
10127             }, seconds * 1000);
10128         });
10129         return this;
10130     },
10131
10132    /**
10133     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10134     * using the "endOpacity" config option.
10135     * Usage:
10136 <pre><code>
10137 // default: fade in from opacity 0 to 100%
10138 el.fadeIn();
10139
10140 // custom: fade in from opacity 0 to 75% over 2 seconds
10141 el.fadeIn({ endOpacity: .75, duration: 2});
10142
10143 // common config options shown with default values
10144 el.fadeIn({
10145     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10146     easing: 'easeOut',
10147     duration: .5
10148 });
10149 </code></pre>
10150     * @param {Object} options (optional) Object literal with any of the Fx config options
10151     * @return {Roo.Element} The Element
10152     */
10153     fadeIn : function(o){
10154         var el = this.getFxEl();
10155         o = o || {};
10156         el.queueFx(o, function(){
10157             this.setOpacity(0);
10158             this.fixDisplay();
10159             this.dom.style.visibility = 'visible';
10160             var to = o.endOpacity || 1;
10161             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10162                 o, null, .5, "easeOut", function(){
10163                 if(to == 1){
10164                     this.clearOpacity();
10165                 }
10166                 el.afterFx(o);
10167             });
10168         });
10169         return this;
10170     },
10171
10172    /**
10173     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10174     * using the "endOpacity" config option.
10175     * Usage:
10176 <pre><code>
10177 // default: fade out from the element's current opacity to 0
10178 el.fadeOut();
10179
10180 // custom: fade out from the element's current opacity to 25% over 2 seconds
10181 el.fadeOut({ endOpacity: .25, duration: 2});
10182
10183 // common config options shown with default values
10184 el.fadeOut({
10185     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10186     easing: 'easeOut',
10187     duration: .5
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192     * @param {Object} options (optional) Object literal with any of the Fx config options
10193     * @return {Roo.Element} The Element
10194     */
10195     fadeOut : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198         el.queueFx(o, function(){
10199             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10200                 o, null, .5, "easeOut", function(){
10201                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10202                      this.dom.style.display = "none";
10203                 }else{
10204                      this.dom.style.visibility = "hidden";
10205                 }
10206                 this.clearOpacity();
10207                 el.afterFx(o);
10208             });
10209         });
10210         return this;
10211     },
10212
10213    /**
10214     * Animates the transition of an element's dimensions from a starting height/width
10215     * to an ending height/width.
10216     * Usage:
10217 <pre><code>
10218 // change height and width to 100x100 pixels
10219 el.scale(100, 100);
10220
10221 // common config options shown with default values.  The height and width will default to
10222 // the element's existing values if passed as null.
10223 el.scale(
10224     [element's width],
10225     [element's height], {
10226     easing: 'easeOut',
10227     duration: .35
10228 });
10229 </code></pre>
10230     * @param {Number} width  The new width (pass undefined to keep the original width)
10231     * @param {Number} height  The new height (pass undefined to keep the original height)
10232     * @param {Object} options (optional) Object literal with any of the Fx config options
10233     * @return {Roo.Element} The Element
10234     */
10235     scale : function(w, h, o){
10236         this.shift(Roo.apply({}, o, {
10237             width: w,
10238             height: h
10239         }));
10240         return this;
10241     },
10242
10243    /**
10244     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10245     * Any of these properties not specified in the config object will not be changed.  This effect 
10246     * requires that at least one new dimension, position or opacity setting must be passed in on
10247     * the config object in order for the function to have any effect.
10248     * Usage:
10249 <pre><code>
10250 // slide the element horizontally to x position 200 while changing the height and opacity
10251 el.shift({ x: 200, height: 50, opacity: .8 });
10252
10253 // common config options shown with default values.
10254 el.shift({
10255     width: [element's width],
10256     height: [element's height],
10257     x: [element's x position],
10258     y: [element's y position],
10259     opacity: [element's opacity],
10260     easing: 'easeOut',
10261     duration: .35
10262 });
10263 </code></pre>
10264     * @param {Object} options  Object literal with any of the Fx config options
10265     * @return {Roo.Element} The Element
10266     */
10267     shift : function(o){
10268         var el = this.getFxEl();
10269         o = o || {};
10270         el.queueFx(o, function(){
10271             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10272             if(w !== undefined){
10273                 a.width = {to: this.adjustWidth(w)};
10274             }
10275             if(h !== undefined){
10276                 a.height = {to: this.adjustHeight(h)};
10277             }
10278             if(x !== undefined || y !== undefined){
10279                 a.points = {to: [
10280                     x !== undefined ? x : this.getX(),
10281                     y !== undefined ? y : this.getY()
10282                 ]};
10283             }
10284             if(op !== undefined){
10285                 a.opacity = {to: op};
10286             }
10287             if(o.xy !== undefined){
10288                 a.points = {to: o.xy};
10289             }
10290             arguments.callee.anim = this.fxanim(a,
10291                 o, 'motion', .35, "easeOut", function(){
10292                 el.afterFx(o);
10293             });
10294         });
10295         return this;
10296     },
10297
10298         /**
10299          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10300          * ending point of the effect.
10301          * Usage:
10302          *<pre><code>
10303 // default: slide the element downward while fading out
10304 el.ghost();
10305
10306 // custom: slide the element out to the right with a 2-second duration
10307 el.ghost('r', { duration: 2 });
10308
10309 // common config options shown with default values
10310 el.ghost('b', {
10311     easing: 'easeOut',
10312     duration: .5
10313     remove: false,
10314     useDisplay: false
10315 });
10316 </code></pre>
10317          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10318          * @param {Object} options (optional) Object literal with any of the Fx config options
10319          * @return {Roo.Element} The Element
10320          */
10321     ghost : function(anchor, o){
10322         var el = this.getFxEl();
10323         o = o || {};
10324
10325         el.queueFx(o, function(){
10326             anchor = anchor || "b";
10327
10328             // restore values after effect
10329             var r = this.getFxRestore();
10330             var w = this.getWidth(),
10331                 h = this.getHeight();
10332
10333             var st = this.dom.style;
10334
10335             var after = function(){
10336                 if(o.useDisplay){
10337                     el.setDisplayed(false);
10338                 }else{
10339                     el.hide();
10340                 }
10341
10342                 el.clearOpacity();
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346
10347                 el.afterFx(o);
10348             };
10349
10350             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10351             switch(anchor.toLowerCase()){
10352                 case "t":
10353                     pt.by = [0, -h];
10354                 break;
10355                 case "l":
10356                     pt.by = [-w, 0];
10357                 break;
10358                 case "r":
10359                     pt.by = [w, 0];
10360                 break;
10361                 case "b":
10362                     pt.by = [0, h];
10363                 break;
10364                 case "tl":
10365                     pt.by = [-w, -h];
10366                 break;
10367                 case "bl":
10368                     pt.by = [-w, h];
10369                 break;
10370                 case "br":
10371                     pt.by = [w, h];
10372                 break;
10373                 case "tr":
10374                     pt.by = [w, -h];
10375                 break;
10376             }
10377
10378             arguments.callee.anim = this.fxanim(a,
10379                 o,
10380                 'motion',
10381                 .5,
10382                 "easeOut", after);
10383         });
10384         return this;
10385     },
10386
10387         /**
10388          * Ensures that all effects queued after syncFx is called on the element are
10389          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10390          * @return {Roo.Element} The Element
10391          */
10392     syncFx : function(){
10393         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10394             block : false,
10395             concurrent : true,
10396             stopFx : false
10397         });
10398         return this;
10399     },
10400
10401         /**
10402          * Ensures that all effects queued after sequenceFx is called on the element are
10403          * run in sequence.  This is the opposite of {@link #syncFx}.
10404          * @return {Roo.Element} The Element
10405          */
10406     sequenceFx : function(){
10407         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10408             block : false,
10409             concurrent : false,
10410             stopFx : false
10411         });
10412         return this;
10413     },
10414
10415         /* @private */
10416     nextFx : function(){
10417         var ef = this.fxQueue[0];
10418         if(ef){
10419             ef.call(this);
10420         }
10421     },
10422
10423         /**
10424          * Returns true if the element has any effects actively running or queued, else returns false.
10425          * @return {Boolean} True if element has active effects, else false
10426          */
10427     hasActiveFx : function(){
10428         return this.fxQueue && this.fxQueue[0];
10429     },
10430
10431         /**
10432          * Stops any running effects and clears the element's internal effects queue if it contains
10433          * any additional effects that haven't started yet.
10434          * @return {Roo.Element} The Element
10435          */
10436     stopFx : function(){
10437         if(this.hasActiveFx()){
10438             var cur = this.fxQueue[0];
10439             if(cur && cur.anim && cur.anim.isAnimated()){
10440                 this.fxQueue = [cur]; // clear out others
10441                 cur.anim.stop(true);
10442             }
10443         }
10444         return this;
10445     },
10446
10447         /* @private */
10448     beforeFx : function(o){
10449         if(this.hasActiveFx() && !o.concurrent){
10450            if(o.stopFx){
10451                this.stopFx();
10452                return true;
10453            }
10454            return false;
10455         }
10456         return true;
10457     },
10458
10459         /**
10460          * Returns true if the element is currently blocking so that no other effect can be queued
10461          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10462          * used to ensure that an effect initiated by a user action runs to completion prior to the
10463          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10464          * @return {Boolean} True if blocking, else false
10465          */
10466     hasFxBlock : function(){
10467         var q = this.fxQueue;
10468         return q && q[0] && q[0].block;
10469     },
10470
10471         /* @private */
10472     queueFx : function(o, fn){
10473         if(!this.fxQueue){
10474             this.fxQueue = [];
10475         }
10476         if(!this.hasFxBlock()){
10477             Roo.applyIf(o, this.fxDefaults);
10478             if(!o.concurrent){
10479                 var run = this.beforeFx(o);
10480                 fn.block = o.block;
10481                 this.fxQueue.push(fn);
10482                 if(run){
10483                     this.nextFx();
10484                 }
10485             }else{
10486                 fn.call(this);
10487             }
10488         }
10489         return this;
10490     },
10491
10492         /* @private */
10493     fxWrap : function(pos, o, vis){
10494         var wrap;
10495         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10496             var wrapXY;
10497             if(o.fixPosition){
10498                 wrapXY = this.getXY();
10499             }
10500             var div = document.createElement("div");
10501             div.style.visibility = vis;
10502             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10503             wrap.setPositioning(pos);
10504             if(wrap.getStyle("position") == "static"){
10505                 wrap.position("relative");
10506             }
10507             this.clearPositioning('auto');
10508             wrap.clip();
10509             wrap.dom.appendChild(this.dom);
10510             if(wrapXY){
10511                 wrap.setXY(wrapXY);
10512             }
10513         }
10514         return wrap;
10515     },
10516
10517         /* @private */
10518     fxUnwrap : function(wrap, pos, o){
10519         this.clearPositioning();
10520         this.setPositioning(pos);
10521         if(!o.wrap){
10522             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10523             wrap.remove();
10524         }
10525     },
10526
10527         /* @private */
10528     getFxRestore : function(){
10529         var st = this.dom.style;
10530         return {pos: this.getPositioning(), width: st.width, height : st.height};
10531     },
10532
10533         /* @private */
10534     afterFx : function(o){
10535         if(o.afterStyle){
10536             this.applyStyles(o.afterStyle);
10537         }
10538         if(o.afterCls){
10539             this.addClass(o.afterCls);
10540         }
10541         if(o.remove === true){
10542             this.remove();
10543         }
10544         Roo.callback(o.callback, o.scope, [this]);
10545         if(!o.concurrent){
10546             this.fxQueue.shift();
10547             this.nextFx();
10548         }
10549     },
10550
10551         /* @private */
10552     getFxEl : function(){ // support for composite element fx
10553         return Roo.get(this.dom);
10554     },
10555
10556         /* @private */
10557     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10558         animType = animType || 'run';
10559         opt = opt || {};
10560         var anim = Roo.lib.Anim[animType](
10561             this.dom, args,
10562             (opt.duration || defaultDur) || .35,
10563             (opt.easing || defaultEase) || 'easeOut',
10564             function(){
10565                 Roo.callback(cb, this);
10566             },
10567             this
10568         );
10569         opt.anim = anim;
10570         return anim;
10571     }
10572 };
10573
10574 // backwords compat
10575 Roo.Fx.resize = Roo.Fx.scale;
10576
10577 //When included, Roo.Fx is automatically applied to Element so that all basic
10578 //effects are available directly via the Element API
10579 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590
10591 /**
10592  * @class Roo.CompositeElement
10593  * Standard composite class. Creates a Roo.Element for every element in the collection.
10594  * <br><br>
10595  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10596  * actions will be performed on all the elements in this collection.</b>
10597  * <br><br>
10598  * All methods return <i>this</i> and can be chained.
10599  <pre><code>
10600  var els = Roo.select("#some-el div.some-class", true);
10601  // or select directly from an existing element
10602  var el = Roo.get('some-el');
10603  el.select('div.some-class', true);
10604
10605  els.setWidth(100); // all elements become 100 width
10606  els.hide(true); // all elements fade out and hide
10607  // or
10608  els.setWidth(100).hide(true);
10609  </code></pre>
10610  */
10611 Roo.CompositeElement = function(els){
10612     this.elements = [];
10613     this.addElements(els);
10614 };
10615 Roo.CompositeElement.prototype = {
10616     isComposite: true,
10617     addElements : function(els){
10618         if(!els) return this;
10619         if(typeof els == "string"){
10620             els = Roo.Element.selectorFunction(els);
10621         }
10622         var yels = this.elements;
10623         var index = yels.length-1;
10624         for(var i = 0, len = els.length; i < len; i++) {
10625                 yels[++index] = Roo.get(els[i]);
10626         }
10627         return this;
10628     },
10629
10630     /**
10631     * Clears this composite and adds the elements returned by the passed selector.
10632     * @param {String/Array} els A string CSS selector, an array of elements or an element
10633     * @return {CompositeElement} this
10634     */
10635     fill : function(els){
10636         this.elements = [];
10637         this.add(els);
10638         return this;
10639     },
10640
10641     /**
10642     * Filters this composite to only elements that match the passed selector.
10643     * @param {String} selector A string CSS selector
10644     * @return {CompositeElement} this
10645     */
10646     filter : function(selector){
10647         var els = [];
10648         this.each(function(el){
10649             if(el.is(selector)){
10650                 els[els.length] = el.dom;
10651             }
10652         });
10653         this.fill(els);
10654         return this;
10655     },
10656
10657     invoke : function(fn, args){
10658         var els = this.elements;
10659         for(var i = 0, len = els.length; i < len; i++) {
10660                 Roo.Element.prototype[fn].apply(els[i], args);
10661         }
10662         return this;
10663     },
10664     /**
10665     * Adds elements to this composite.
10666     * @param {String/Array} els A string CSS selector, an array of elements or an element
10667     * @return {CompositeElement} this
10668     */
10669     add : function(els){
10670         if(typeof els == "string"){
10671             this.addElements(Roo.Element.selectorFunction(els));
10672         }else if(els.length !== undefined){
10673             this.addElements(els);
10674         }else{
10675             this.addElements([els]);
10676         }
10677         return this;
10678     },
10679     /**
10680     * Calls the passed function passing (el, this, index) for each element in this composite.
10681     * @param {Function} fn The function to call
10682     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10683     * @return {CompositeElement} this
10684     */
10685     each : function(fn, scope){
10686         var els = this.elements;
10687         for(var i = 0, len = els.length; i < len; i++){
10688             if(fn.call(scope || els[i], els[i], this, i) === false) {
10689                 break;
10690             }
10691         }
10692         return this;
10693     },
10694
10695     /**
10696      * Returns the Element object at the specified index
10697      * @param {Number} index
10698      * @return {Roo.Element}
10699      */
10700     item : function(index){
10701         return this.elements[index] || null;
10702     },
10703
10704     /**
10705      * Returns the first Element
10706      * @return {Roo.Element}
10707      */
10708     first : function(){
10709         return this.item(0);
10710     },
10711
10712     /**
10713      * Returns the last Element
10714      * @return {Roo.Element}
10715      */
10716     last : function(){
10717         return this.item(this.elements.length-1);
10718     },
10719
10720     /**
10721      * Returns the number of elements in this composite
10722      * @return Number
10723      */
10724     getCount : function(){
10725         return this.elements.length;
10726     },
10727
10728     /**
10729      * Returns true if this composite contains the passed element
10730      * @return Boolean
10731      */
10732     contains : function(el){
10733         return this.indexOf(el) !== -1;
10734     },
10735
10736     /**
10737      * Returns true if this composite contains the passed element
10738      * @return Boolean
10739      */
10740     indexOf : function(el){
10741         return this.elements.indexOf(Roo.get(el));
10742     },
10743
10744
10745     /**
10746     * Removes the specified element(s).
10747     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10748     * or an array of any of those.
10749     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10750     * @return {CompositeElement} this
10751     */
10752     removeElement : function(el, removeDom){
10753         if(el instanceof Array){
10754             for(var i = 0, len = el.length; i < len; i++){
10755                 this.removeElement(el[i]);
10756             }
10757             return this;
10758         }
10759         var index = typeof el == 'number' ? el : this.indexOf(el);
10760         if(index !== -1){
10761             if(removeDom){
10762                 var d = this.elements[index];
10763                 if(d.dom){
10764                     d.remove();
10765                 }else{
10766                     d.parentNode.removeChild(d);
10767                 }
10768             }
10769             this.elements.splice(index, 1);
10770         }
10771         return this;
10772     },
10773
10774     /**
10775     * Replaces the specified element with the passed element.
10776     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10777     * to replace.
10778     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10779     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10780     * @return {CompositeElement} this
10781     */
10782     replaceElement : function(el, replacement, domReplace){
10783         var index = typeof el == 'number' ? el : this.indexOf(el);
10784         if(index !== -1){
10785             if(domReplace){
10786                 this.elements[index].replaceWith(replacement);
10787             }else{
10788                 this.elements.splice(index, 1, Roo.get(replacement))
10789             }
10790         }
10791         return this;
10792     },
10793
10794     /**
10795      * Removes all elements.
10796      */
10797     clear : function(){
10798         this.elements = [];
10799     }
10800 };
10801 (function(){
10802     Roo.CompositeElement.createCall = function(proto, fnName){
10803         if(!proto[fnName]){
10804             proto[fnName] = function(){
10805                 return this.invoke(fnName, arguments);
10806             };
10807         }
10808     };
10809     for(var fnName in Roo.Element.prototype){
10810         if(typeof Roo.Element.prototype[fnName] == "function"){
10811             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10812         }
10813     };
10814 })();
10815 /*
10816  * Based on:
10817  * Ext JS Library 1.1.1
10818  * Copyright(c) 2006-2007, Ext JS, LLC.
10819  *
10820  * Originally Released Under LGPL - original licence link has changed is not relivant.
10821  *
10822  * Fork - LGPL
10823  * <script type="text/javascript">
10824  */
10825
10826 /**
10827  * @class Roo.CompositeElementLite
10828  * @extends Roo.CompositeElement
10829  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10830  <pre><code>
10831  var els = Roo.select("#some-el div.some-class");
10832  // or select directly from an existing element
10833  var el = Roo.get('some-el');
10834  el.select('div.some-class');
10835
10836  els.setWidth(100); // all elements become 100 width
10837  els.hide(true); // all elements fade out and hide
10838  // or
10839  els.setWidth(100).hide(true);
10840  </code></pre><br><br>
10841  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10842  * actions will be performed on all the elements in this collection.</b>
10843  */
10844 Roo.CompositeElementLite = function(els){
10845     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10846     this.el = new Roo.Element.Flyweight();
10847 };
10848 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10849     addElements : function(els){
10850         if(els){
10851             if(els instanceof Array){
10852                 this.elements = this.elements.concat(els);
10853             }else{
10854                 var yels = this.elements;
10855                 var index = yels.length-1;
10856                 for(var i = 0, len = els.length; i < len; i++) {
10857                     yels[++index] = els[i];
10858                 }
10859             }
10860         }
10861         return this;
10862     },
10863     invoke : function(fn, args){
10864         var els = this.elements;
10865         var el = this.el;
10866         for(var i = 0, len = els.length; i < len; i++) {
10867             el.dom = els[i];
10868                 Roo.Element.prototype[fn].apply(el, args);
10869         }
10870         return this;
10871     },
10872     /**
10873      * Returns a flyweight Element of the dom element object at the specified index
10874      * @param {Number} index
10875      * @return {Roo.Element}
10876      */
10877     item : function(index){
10878         if(!this.elements[index]){
10879             return null;
10880         }
10881         this.el.dom = this.elements[index];
10882         return this.el;
10883     },
10884
10885     // fixes scope with flyweight
10886     addListener : function(eventName, handler, scope, opt){
10887         var els = this.elements;
10888         for(var i = 0, len = els.length; i < len; i++) {
10889             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10890         }
10891         return this;
10892     },
10893
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10896     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10897     * a reference to the dom node, use el.dom.</b>
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         var el = this.el;
10905         for(var i = 0, len = els.length; i < len; i++){
10906             el.dom = els[i];
10907                 if(fn.call(scope || el, el, this, i) === false){
10908                 break;
10909             }
10910         }
10911         return this;
10912     },
10913
10914     indexOf : function(el){
10915         return this.elements.indexOf(Roo.getDom(el));
10916     },
10917
10918     replaceElement : function(el, replacement, domReplace){
10919         var index = typeof el == 'number' ? el : this.indexOf(el);
10920         if(index !== -1){
10921             replacement = Roo.getDom(replacement);
10922             if(domReplace){
10923                 var d = this.elements[index];
10924                 d.parentNode.insertBefore(replacement, d);
10925                 d.parentNode.removeChild(d);
10926             }
10927             this.elements.splice(index, 1, replacement);
10928         }
10929         return this;
10930     }
10931 });
10932 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10933
10934 /*
10935  * Based on:
10936  * Ext JS Library 1.1.1
10937  * Copyright(c) 2006-2007, Ext JS, LLC.
10938  *
10939  * Originally Released Under LGPL - original licence link has changed is not relivant.
10940  *
10941  * Fork - LGPL
10942  * <script type="text/javascript">
10943  */
10944
10945  
10946
10947 /**
10948  * @class Roo.data.Connection
10949  * @extends Roo.util.Observable
10950  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10951  * either to a configured URL, or to a URL specified at request time.<br><br>
10952  * <p>
10953  * Requests made by this class are asynchronous, and will return immediately. No data from
10954  * the server will be available to the statement immediately following the {@link #request} call.
10955  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10956  * <p>
10957  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10958  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10959  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10960  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10961  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10962  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10963  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10964  * standard DOM methods.
10965  * @constructor
10966  * @param {Object} config a configuration object.
10967  */
10968 Roo.data.Connection = function(config){
10969     Roo.apply(this, config);
10970     this.addEvents({
10971         /**
10972          * @event beforerequest
10973          * Fires before a network request is made to retrieve a data object.
10974          * @param {Connection} conn This Connection object.
10975          * @param {Object} options The options config object passed to the {@link #request} method.
10976          */
10977         "beforerequest" : true,
10978         /**
10979          * @event requestcomplete
10980          * Fires if the request was successfully completed.
10981          * @param {Connection} conn This Connection object.
10982          * @param {Object} response The XHR object containing the response data.
10983          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10984          * @param {Object} options The options config object passed to the {@link #request} method.
10985          */
10986         "requestcomplete" : true,
10987         /**
10988          * @event requestexception
10989          * Fires if an error HTTP status was returned from the server.
10990          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10991          * @param {Connection} conn This Connection object.
10992          * @param {Object} response The XHR object containing the response data.
10993          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10994          * @param {Object} options The options config object passed to the {@link #request} method.
10995          */
10996         "requestexception" : true
10997     });
10998     Roo.data.Connection.superclass.constructor.call(this);
10999 };
11000
11001 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11002     /**
11003      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11004      */
11005     /**
11006      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11007      * extra parameters to each request made by this object. (defaults to undefined)
11008      */
11009     /**
11010      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11011      *  to each request made by this object. (defaults to undefined)
11012      */
11013     /**
11014      * @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)
11015      */
11016     /**
11017      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11018      */
11019     timeout : 30000,
11020     /**
11021      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11022      * @type Boolean
11023      */
11024     autoAbort:false,
11025
11026     /**
11027      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11028      * @type Boolean
11029      */
11030     disableCaching: true,
11031
11032     /**
11033      * Sends an HTTP request to a remote server.
11034      * @param {Object} options An object which may contain the following properties:<ul>
11035      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11036      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11037      * request, a url encoded string or a function to call to get either.</li>
11038      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11039      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11040      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11041      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11042      * <li>options {Object} The parameter to the request call.</li>
11043      * <li>success {Boolean} True if the request succeeded.</li>
11044      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11045      * </ul></li>
11046      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11047      * The callback is passed the following parameters:<ul>
11048      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11049      * <li>options {Object} The parameter to the request call.</li>
11050      * </ul></li>
11051      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11052      * The callback is passed the following parameters:<ul>
11053      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11054      * <li>options {Object} The parameter to the request call.</li>
11055      * </ul></li>
11056      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11057      * for the callback function. Defaults to the browser window.</li>
11058      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11059      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11060      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11061      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11062      * params for the post data. Any params will be appended to the URL.</li>
11063      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11064      * </ul>
11065      * @return {Number} transactionId
11066      */
11067     request : function(o){
11068         if(this.fireEvent("beforerequest", this, o) !== false){
11069             var p = o.params;
11070
11071             if(typeof p == "function"){
11072                 p = p.call(o.scope||window, o);
11073             }
11074             if(typeof p == "object"){
11075                 p = Roo.urlEncode(o.params);
11076             }
11077             if(this.extraParams){
11078                 var extras = Roo.urlEncode(this.extraParams);
11079                 p = p ? (p + '&' + extras) : extras;
11080             }
11081
11082             var url = o.url || this.url;
11083             if(typeof url == 'function'){
11084                 url = url.call(o.scope||window, o);
11085             }
11086
11087             if(o.form){
11088                 var form = Roo.getDom(o.form);
11089                 url = url || form.action;
11090
11091                 var enctype = form.getAttribute("enctype");
11092                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11093                     return this.doFormUpload(o, p, url);
11094                 }
11095                 var f = Roo.lib.Ajax.serializeForm(form);
11096                 p = p ? (p + '&' + f) : f;
11097             }
11098
11099             var hs = o.headers;
11100             if(this.defaultHeaders){
11101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11102                 if(!o.headers){
11103                     o.headers = hs;
11104                 }
11105             }
11106
11107             var cb = {
11108                 success: this.handleResponse,
11109                 failure: this.handleFailure,
11110                 scope: this,
11111                 argument: {options: o},
11112                 timeout : this.timeout
11113             };
11114
11115             var method = o.method||this.method||(p ? "POST" : "GET");
11116
11117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11119             }
11120
11121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11122                 if(o.autoAbort){
11123                     this.abort();
11124                 }
11125             }else if(this.autoAbort !== false){
11126                 this.abort();
11127             }
11128
11129             if((method == 'GET' && p) || o.xmlData){
11130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11131                 p = '';
11132             }
11133             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11134             return this.transId;
11135         }else{
11136             Roo.callback(o.callback, o.scope, [o, null, null]);
11137             return null;
11138         }
11139     },
11140
11141     /**
11142      * Determine whether this object has a request outstanding.
11143      * @param {Number} transactionId (Optional) defaults to the last transaction
11144      * @return {Boolean} True if there is an outstanding request.
11145      */
11146     isLoading : function(transId){
11147         if(transId){
11148             return Roo.lib.Ajax.isCallInProgress(transId);
11149         }else{
11150             return this.transId ? true : false;
11151         }
11152     },
11153
11154     /**
11155      * Aborts any outstanding request.
11156      * @param {Number} transactionId (Optional) defaults to the last transaction
11157      */
11158     abort : function(transId){
11159         if(transId || this.isLoading()){
11160             Roo.lib.Ajax.abort(transId || this.transId);
11161         }
11162     },
11163
11164     // private
11165     handleResponse : function(response){
11166         this.transId = false;
11167         var options = response.argument.options;
11168         response.argument = options ? options.argument : null;
11169         this.fireEvent("requestcomplete", this, response, options);
11170         Roo.callback(options.success, options.scope, [response, options]);
11171         Roo.callback(options.callback, options.scope, [options, true, response]);
11172     },
11173
11174     // private
11175     handleFailure : function(response, e){
11176         this.transId = false;
11177         var options = response.argument.options;
11178         response.argument = options ? options.argument : null;
11179         this.fireEvent("requestexception", this, response, options, e);
11180         Roo.callback(options.failure, options.scope, [response, options]);
11181         Roo.callback(options.callback, options.scope, [options, false, response]);
11182     },
11183
11184     // private
11185     doFormUpload : function(o, ps, url){
11186         var id = Roo.id();
11187         var frame = document.createElement('iframe');
11188         frame.id = id;
11189         frame.name = id;
11190         frame.className = 'x-hidden';
11191         if(Roo.isIE){
11192             frame.src = Roo.SSL_SECURE_URL;
11193         }
11194         document.body.appendChild(frame);
11195
11196         if(Roo.isIE){
11197            document.frames[id].name = id;
11198         }
11199
11200         var form = Roo.getDom(o.form);
11201         form.target = id;
11202         form.method = 'POST';
11203         form.enctype = form.encoding = 'multipart/form-data';
11204         if(url){
11205             form.action = url;
11206         }
11207
11208         var hiddens, hd;
11209         if(ps){ // add dynamic params
11210             hiddens = [];
11211             ps = Roo.urlDecode(ps, false);
11212             for(var k in ps){
11213                 if(ps.hasOwnProperty(k)){
11214                     hd = document.createElement('input');
11215                     hd.type = 'hidden';
11216                     hd.name = k;
11217                     hd.value = ps[k];
11218                     form.appendChild(hd);
11219                     hiddens.push(hd);
11220                 }
11221             }
11222         }
11223
11224         function cb(){
11225             var r = {  // bogus response object
11226                 responseText : '',
11227                 responseXML : null
11228             };
11229
11230             r.argument = o ? o.argument : null;
11231
11232             try { //
11233                 var doc;
11234                 if(Roo.isIE){
11235                     doc = frame.contentWindow.document;
11236                 }else {
11237                     doc = (frame.contentDocument || window.frames[id].document);
11238                 }
11239                 if(doc && doc.body){
11240                     r.responseText = doc.body.innerHTML;
11241                 }
11242                 if(doc && doc.XMLDocument){
11243                     r.responseXML = doc.XMLDocument;
11244                 }else {
11245                     r.responseXML = doc;
11246                 }
11247             }
11248             catch(e) {
11249                 // ignore
11250             }
11251
11252             Roo.EventManager.removeListener(frame, 'load', cb, this);
11253
11254             this.fireEvent("requestcomplete", this, r, o);
11255             Roo.callback(o.success, o.scope, [r, o]);
11256             Roo.callback(o.callback, o.scope, [o, true, r]);
11257
11258             setTimeout(function(){document.body.removeChild(frame);}, 100);
11259         }
11260
11261         Roo.EventManager.on(frame, 'load', cb, this);
11262         form.submit();
11263
11264         if(hiddens){ // remove dynamic params
11265             for(var i = 0, len = hiddens.length; i < len; i++){
11266                 form.removeChild(hiddens[i]);
11267             }
11268         }
11269     }
11270 });
11271
11272 /**
11273  * @class Roo.Ajax
11274  * @extends Roo.data.Connection
11275  * Global Ajax request class.
11276  *
11277  * @singleton
11278  */
11279 Roo.Ajax = new Roo.data.Connection({
11280     // fix up the docs
11281    /**
11282      * @cfg {String} url @hide
11283      */
11284     /**
11285      * @cfg {Object} extraParams @hide
11286      */
11287     /**
11288      * @cfg {Object} defaultHeaders @hide
11289      */
11290     /**
11291      * @cfg {String} method (Optional) @hide
11292      */
11293     /**
11294      * @cfg {Number} timeout (Optional) @hide
11295      */
11296     /**
11297      * @cfg {Boolean} autoAbort (Optional) @hide
11298      */
11299
11300     /**
11301      * @cfg {Boolean} disableCaching (Optional) @hide
11302      */
11303
11304     /**
11305      * @property  disableCaching
11306      * True to add a unique cache-buster param to GET requests. (defaults to true)
11307      * @type Boolean
11308      */
11309     /**
11310      * @property  url
11311      * The default URL to be used for requests to the server. (defaults to undefined)
11312      * @type String
11313      */
11314     /**
11315      * @property  extraParams
11316      * An object containing properties which are used as
11317      * extra parameters to each request made by this object. (defaults to undefined)
11318      * @type Object
11319      */
11320     /**
11321      * @property  defaultHeaders
11322      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11323      * @type Object
11324      */
11325     /**
11326      * @property  method
11327      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11328      * @type String
11329      */
11330     /**
11331      * @property  timeout
11332      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11333      * @type Number
11334      */
11335
11336     /**
11337      * @property  autoAbort
11338      * Whether a new request should abort any pending requests. (defaults to false)
11339      * @type Boolean
11340      */
11341     autoAbort : false,
11342
11343     /**
11344      * Serialize the passed form into a url encoded string
11345      * @param {String/HTMLElement} form
11346      * @return {String}
11347      */
11348     serializeForm : function(form){
11349         return Roo.lib.Ajax.serializeForm(form);
11350     }
11351 });/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361  
11362 /**
11363  * @class Roo.Ajax
11364  * @extends Roo.data.Connection
11365  * Global Ajax request class.
11366  *
11367  * @instanceOf  Roo.data.Connection
11368  */
11369 Roo.Ajax = new Roo.data.Connection({
11370     // fix up the docs
11371     
11372     /**
11373      * fix up scoping
11374      * @scope Roo.Ajax
11375      */
11376     
11377    /**
11378      * @cfg {String} url @hide
11379      */
11380     /**
11381      * @cfg {Object} extraParams @hide
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders @hide
11385      */
11386     /**
11387      * @cfg {String} method (Optional) @hide
11388      */
11389     /**
11390      * @cfg {Number} timeout (Optional) @hide
11391      */
11392     /**
11393      * @cfg {Boolean} autoAbort (Optional) @hide
11394      */
11395
11396     /**
11397      * @cfg {Boolean} disableCaching (Optional) @hide
11398      */
11399
11400     /**
11401      * @property  disableCaching
11402      * True to add a unique cache-buster param to GET requests. (defaults to true)
11403      * @type Boolean
11404      */
11405     /**
11406      * @property  url
11407      * The default URL to be used for requests to the server. (defaults to undefined)
11408      * @type String
11409      */
11410     /**
11411      * @property  extraParams
11412      * An object containing properties which are used as
11413      * extra parameters to each request made by this object. (defaults to undefined)
11414      * @type Object
11415      */
11416     /**
11417      * @property  defaultHeaders
11418      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11419      * @type Object
11420      */
11421     /**
11422      * @property  method
11423      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11424      * @type String
11425      */
11426     /**
11427      * @property  timeout
11428      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11429      * @type Number
11430      */
11431
11432     /**
11433      * @property  autoAbort
11434      * Whether a new request should abort any pending requests. (defaults to false)
11435      * @type Boolean
11436      */
11437     autoAbort : false,
11438
11439     /**
11440      * Serialize the passed form into a url encoded string
11441      * @param {String/HTMLElement} form
11442      * @return {String}
11443      */
11444     serializeForm : function(form){
11445         return Roo.lib.Ajax.serializeForm(form);
11446     }
11447 });/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458  
11459 /**
11460  * @class Roo.UpdateManager
11461  * @extends Roo.util.Observable
11462  * Provides AJAX-style update for Element object.<br><br>
11463  * Usage:<br>
11464  * <pre><code>
11465  * // Get it from a Roo.Element object
11466  * var el = Roo.get("foo");
11467  * var mgr = el.getUpdateManager();
11468  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11469  * ...
11470  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11471  * <br>
11472  * // or directly (returns the same UpdateManager instance)
11473  * var mgr = new Roo.UpdateManager("myElementId");
11474  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11475  * mgr.on("update", myFcnNeedsToKnow);
11476  * <br>
11477    // short handed call directly from the element object
11478    Roo.get("foo").load({
11479         url: "bar.php",
11480         scripts:true,
11481         params: "for=bar",
11482         text: "Loading Foo..."
11483    });
11484  * </code></pre>
11485  * @constructor
11486  * Create new UpdateManager directly.
11487  * @param {String/HTMLElement/Roo.Element} el The element to update
11488  * @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).
11489  */
11490 Roo.UpdateManager = function(el, forceNew){
11491     el = Roo.get(el);
11492     if(!forceNew && el.updateManager){
11493         return el.updateManager;
11494     }
11495     /**
11496      * The Element object
11497      * @type Roo.Element
11498      */
11499     this.el = el;
11500     /**
11501      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11502      * @type String
11503      */
11504     this.defaultUrl = null;
11505
11506     this.addEvents({
11507         /**
11508          * @event beforeupdate
11509          * Fired before an update is made, return false from your handler and the update is cancelled.
11510          * @param {Roo.Element} el
11511          * @param {String/Object/Function} url
11512          * @param {String/Object} params
11513          */
11514         "beforeupdate": true,
11515         /**
11516          * @event update
11517          * Fired after successful update is made.
11518          * @param {Roo.Element} el
11519          * @param {Object} oResponseObject The response Object
11520          */
11521         "update": true,
11522         /**
11523          * @event failure
11524          * Fired on update failure.
11525          * @param {Roo.Element} el
11526          * @param {Object} oResponseObject The response Object
11527          */
11528         "failure": true
11529     });
11530     var d = Roo.UpdateManager.defaults;
11531     /**
11532      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11533      * @type String
11534      */
11535     this.sslBlankUrl = d.sslBlankUrl;
11536     /**
11537      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11538      * @type Boolean
11539      */
11540     this.disableCaching = d.disableCaching;
11541     /**
11542      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11543      * @type String
11544      */
11545     this.indicatorText = d.indicatorText;
11546     /**
11547      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11548      * @type String
11549      */
11550     this.showLoadIndicator = d.showLoadIndicator;
11551     /**
11552      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11553      * @type Number
11554      */
11555     this.timeout = d.timeout;
11556
11557     /**
11558      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11559      * @type Boolean
11560      */
11561     this.loadScripts = d.loadScripts;
11562
11563     /**
11564      * Transaction object of current executing transaction
11565      */
11566     this.transaction = null;
11567
11568     /**
11569      * @private
11570      */
11571     this.autoRefreshProcId = null;
11572     /**
11573      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11574      * @type Function
11575      */
11576     this.refreshDelegate = this.refresh.createDelegate(this);
11577     /**
11578      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11579      * @type Function
11580      */
11581     this.updateDelegate = this.update.createDelegate(this);
11582     /**
11583      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11584      * @type Function
11585      */
11586     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11587     /**
11588      * @private
11589      */
11590     this.successDelegate = this.processSuccess.createDelegate(this);
11591     /**
11592      * @private
11593      */
11594     this.failureDelegate = this.processFailure.createDelegate(this);
11595
11596     if(!this.renderer){
11597      /**
11598       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11599       */
11600     this.renderer = new Roo.UpdateManager.BasicRenderer();
11601     }
11602     
11603     Roo.UpdateManager.superclass.constructor.call(this);
11604 };
11605
11606 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11607     /**
11608      * Get the Element this UpdateManager is bound to
11609      * @return {Roo.Element} The element
11610      */
11611     getEl : function(){
11612         return this.el;
11613     },
11614     /**
11615      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11616      * @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:
11617 <pre><code>
11618 um.update({<br/>
11619     url: "your-url.php",<br/>
11620     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11621     callback: yourFunction,<br/>
11622     scope: yourObject, //(optional scope)  <br/>
11623     discardUrl: false, <br/>
11624     nocache: false,<br/>
11625     text: "Loading...",<br/>
11626     timeout: 30,<br/>
11627     scripts: false<br/>
11628 });
11629 </code></pre>
11630      * The only required property is url. The optional properties nocache, text and scripts
11631      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11632      * @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}
11633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11634      * @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.
11635      */
11636     update : function(url, params, callback, discardUrl){
11637         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11638             var method = this.method, cfg;
11639             if(typeof url == "object"){ // must be config object
11640                 cfg = url;
11641                 url = cfg.url;
11642                 params = params || cfg.params;
11643                 callback = callback || cfg.callback;
11644                 discardUrl = discardUrl || cfg.discardUrl;
11645                 if(callback && cfg.scope){
11646                     callback = callback.createDelegate(cfg.scope);
11647                 }
11648                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11649                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11650                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11651                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11652                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11653             }
11654             this.showLoading();
11655             if(!discardUrl){
11656                 this.defaultUrl = url;
11657             }
11658             if(typeof url == "function"){
11659                 url = url.call(this);
11660             }
11661
11662             method = method || (params ? "POST" : "GET");
11663             if(method == "GET"){
11664                 url = this.prepareUrl(url);
11665             }
11666
11667             var o = Roo.apply(cfg ||{}, {
11668                 url : url,
11669                 params: params,
11670                 success: this.successDelegate,
11671                 failure: this.failureDelegate,
11672                 callback: undefined,
11673                 timeout: (this.timeout*1000),
11674                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11675             });
11676
11677             this.transaction = Roo.Ajax.request(o);
11678         }
11679     },
11680
11681     /**
11682      * 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.
11683      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11684      * @param {String/HTMLElement} form The form Id or form element
11685      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11686      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11687      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11688      */
11689     formUpdate : function(form, url, reset, callback){
11690         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11691             if(typeof url == "function"){
11692                 url = url.call(this);
11693             }
11694             form = Roo.getDom(form);
11695             this.transaction = Roo.Ajax.request({
11696                 form: form,
11697                 url:url,
11698                 success: this.successDelegate,
11699                 failure: this.failureDelegate,
11700                 timeout: (this.timeout*1000),
11701                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11702             });
11703             this.showLoading.defer(1, this);
11704         }
11705     },
11706
11707     /**
11708      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11709      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11710      */
11711     refresh : function(callback){
11712         if(this.defaultUrl == null){
11713             return;
11714         }
11715         this.update(this.defaultUrl, null, callback, true);
11716     },
11717
11718     /**
11719      * Set this element to auto refresh.
11720      * @param {Number} interval How often to update (in seconds).
11721      * @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)
11722      * @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}
11723      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11724      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11725      */
11726     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11727         if(refreshNow){
11728             this.update(url || this.defaultUrl, params, callback, true);
11729         }
11730         if(this.autoRefreshProcId){
11731             clearInterval(this.autoRefreshProcId);
11732         }
11733         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11734     },
11735
11736     /**
11737      * Stop auto refresh on this element.
11738      */
11739      stopAutoRefresh : function(){
11740         if(this.autoRefreshProcId){
11741             clearInterval(this.autoRefreshProcId);
11742             delete this.autoRefreshProcId;
11743         }
11744     },
11745
11746     isAutoRefreshing : function(){
11747        return this.autoRefreshProcId ? true : false;
11748     },
11749     /**
11750      * Called to update the element to "Loading" state. Override to perform custom action.
11751      */
11752     showLoading : function(){
11753         if(this.showLoadIndicator){
11754             this.el.update(this.indicatorText);
11755         }
11756     },
11757
11758     /**
11759      * Adds unique parameter to query string if disableCaching = true
11760      * @private
11761      */
11762     prepareUrl : function(url){
11763         if(this.disableCaching){
11764             var append = "_dc=" + (new Date().getTime());
11765             if(url.indexOf("?") !== -1){
11766                 url += "&" + append;
11767             }else{
11768                 url += "?" + append;
11769             }
11770         }
11771         return url;
11772     },
11773
11774     /**
11775      * @private
11776      */
11777     processSuccess : function(response){
11778         this.transaction = null;
11779         if(response.argument.form && response.argument.reset){
11780             try{ // put in try/catch since some older FF releases had problems with this
11781                 response.argument.form.reset();
11782             }catch(e){}
11783         }
11784         if(this.loadScripts){
11785             this.renderer.render(this.el, response, this,
11786                 this.updateComplete.createDelegate(this, [response]));
11787         }else{
11788             this.renderer.render(this.el, response, this);
11789             this.updateComplete(response);
11790         }
11791     },
11792
11793     updateComplete : function(response){
11794         this.fireEvent("update", this.el, response);
11795         if(typeof response.argument.callback == "function"){
11796             response.argument.callback(this.el, true, response);
11797         }
11798     },
11799
11800     /**
11801      * @private
11802      */
11803     processFailure : function(response){
11804         this.transaction = null;
11805         this.fireEvent("failure", this.el, response);
11806         if(typeof response.argument.callback == "function"){
11807             response.argument.callback(this.el, false, response);
11808         }
11809     },
11810
11811     /**
11812      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11813      * @param {Object} renderer The object implementing the render() method
11814      */
11815     setRenderer : function(renderer){
11816         this.renderer = renderer;
11817     },
11818
11819     getRenderer : function(){
11820        return this.renderer;
11821     },
11822
11823     /**
11824      * Set the defaultUrl used for updates
11825      * @param {String/Function} defaultUrl The url or a function to call to get the url
11826      */
11827     setDefaultUrl : function(defaultUrl){
11828         this.defaultUrl = defaultUrl;
11829     },
11830
11831     /**
11832      * Aborts the executing transaction
11833      */
11834     abort : function(){
11835         if(this.transaction){
11836             Roo.Ajax.abort(this.transaction);
11837         }
11838     },
11839
11840     /**
11841      * Returns true if an update is in progress
11842      * @return {Boolean}
11843      */
11844     isUpdating : function(){
11845         if(this.transaction){
11846             return Roo.Ajax.isLoading(this.transaction);
11847         }
11848         return false;
11849     }
11850 });
11851
11852 /**
11853  * @class Roo.UpdateManager.defaults
11854  * @static (not really - but it helps the doc tool)
11855  * The defaults collection enables customizing the default properties of UpdateManager
11856  */
11857    Roo.UpdateManager.defaults = {
11858        /**
11859          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11860          * @type Number
11861          */
11862          timeout : 30,
11863
11864          /**
11865          * True to process scripts by default (Defaults to false).
11866          * @type Boolean
11867          */
11868         loadScripts : false,
11869
11870         /**
11871         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11872         * @type String
11873         */
11874         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11875         /**
11876          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11877          * @type Boolean
11878          */
11879         disableCaching : false,
11880         /**
11881          * Whether to show indicatorText when loading (Defaults to true).
11882          * @type Boolean
11883          */
11884         showLoadIndicator : true,
11885         /**
11886          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11887          * @type String
11888          */
11889         indicatorText : '<div class="loading-indicator">Loading...</div>'
11890    };
11891
11892 /**
11893  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11894  *Usage:
11895  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11896  * @param {String/HTMLElement/Roo.Element} el The element to update
11897  * @param {String} url The url
11898  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11899  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11900  * @static
11901  * @deprecated
11902  * @member Roo.UpdateManager
11903  */
11904 Roo.UpdateManager.updateElement = function(el, url, params, options){
11905     var um = Roo.get(el, true).getUpdateManager();
11906     Roo.apply(um, options);
11907     um.update(url, params, options ? options.callback : null);
11908 };
11909 // alias for backwards compat
11910 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11911 /**
11912  * @class Roo.UpdateManager.BasicRenderer
11913  * Default Content renderer. Updates the elements innerHTML with the responseText.
11914  */
11915 Roo.UpdateManager.BasicRenderer = function(){};
11916
11917 Roo.UpdateManager.BasicRenderer.prototype = {
11918     /**
11919      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11920      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11921      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11922      * @param {Roo.Element} el The element being rendered
11923      * @param {Object} response The YUI Connect response object
11924      * @param {UpdateManager} updateManager The calling update manager
11925      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11926      */
11927      render : function(el, response, updateManager, callback){
11928         el.update(response.responseText, updateManager.loadScripts, callback);
11929     }
11930 };
11931 /*
11932  * Based on:
11933  * Ext JS Library 1.1.1
11934  * Copyright(c) 2006-2007, Ext JS, LLC.
11935  *
11936  * Originally Released Under LGPL - original licence link has changed is not relivant.
11937  *
11938  * Fork - LGPL
11939  * <script type="text/javascript">
11940  */
11941
11942 /**
11943  * @class Roo.util.DelayedTask
11944  * Provides a convenient method of performing setTimeout where a new
11945  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11946  * You can use this class to buffer
11947  * the keypress events for a certain number of milliseconds, and perform only if they stop
11948  * for that amount of time.
11949  * @constructor The parameters to this constructor serve as defaults and are not required.
11950  * @param {Function} fn (optional) The default function to timeout
11951  * @param {Object} scope (optional) The default scope of that timeout
11952  * @param {Array} args (optional) The default Array of arguments
11953  */
11954 Roo.util.DelayedTask = function(fn, scope, args){
11955     var id = null, d, t;
11956
11957     var call = function(){
11958         var now = new Date().getTime();
11959         if(now - t >= d){
11960             clearInterval(id);
11961             id = null;
11962             fn.apply(scope, args || []);
11963         }
11964     };
11965     /**
11966      * Cancels any pending timeout and queues a new one
11967      * @param {Number} delay The milliseconds to delay
11968      * @param {Function} newFn (optional) Overrides function passed to constructor
11969      * @param {Object} newScope (optional) Overrides scope passed to constructor
11970      * @param {Array} newArgs (optional) Overrides args passed to constructor
11971      */
11972     this.delay = function(delay, newFn, newScope, newArgs){
11973         if(id && delay != d){
11974             this.cancel();
11975         }
11976         d = delay;
11977         t = new Date().getTime();
11978         fn = newFn || fn;
11979         scope = newScope || scope;
11980         args = newArgs || args;
11981         if(!id){
11982             id = setInterval(call, d);
11983         }
11984     };
11985
11986     /**
11987      * Cancel the last queued timeout
11988      */
11989     this.cancel = function(){
11990         if(id){
11991             clearInterval(id);
11992             id = null;
11993         }
11994     };
11995 };/*
11996  * Based on:
11997  * Ext JS Library 1.1.1
11998  * Copyright(c) 2006-2007, Ext JS, LLC.
11999  *
12000  * Originally Released Under LGPL - original licence link has changed is not relivant.
12001  *
12002  * Fork - LGPL
12003  * <script type="text/javascript">
12004  */
12005  
12006  
12007 Roo.util.TaskRunner = function(interval){
12008     interval = interval || 10;
12009     var tasks = [], removeQueue = [];
12010     var id = 0;
12011     var running = false;
12012
12013     var stopThread = function(){
12014         running = false;
12015         clearInterval(id);
12016         id = 0;
12017     };
12018
12019     var startThread = function(){
12020         if(!running){
12021             running = true;
12022             id = setInterval(runTasks, interval);
12023         }
12024     };
12025
12026     var removeTask = function(task){
12027         removeQueue.push(task);
12028         if(task.onStop){
12029             task.onStop();
12030         }
12031     };
12032
12033     var runTasks = function(){
12034         if(removeQueue.length > 0){
12035             for(var i = 0, len = removeQueue.length; i < len; i++){
12036                 tasks.remove(removeQueue[i]);
12037             }
12038             removeQueue = [];
12039             if(tasks.length < 1){
12040                 stopThread();
12041                 return;
12042             }
12043         }
12044         var now = new Date().getTime();
12045         for(var i = 0, len = tasks.length; i < len; ++i){
12046             var t = tasks[i];
12047             var itime = now - t.taskRunTime;
12048             if(t.interval <= itime){
12049                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12050                 t.taskRunTime = now;
12051                 if(rt === false || t.taskRunCount === t.repeat){
12052                     removeTask(t);
12053                     return;
12054                 }
12055             }
12056             if(t.duration && t.duration <= (now - t.taskStartTime)){
12057                 removeTask(t);
12058             }
12059         }
12060     };
12061
12062     /**
12063      * Queues a new task.
12064      * @param {Object} task
12065      */
12066     this.start = function(task){
12067         tasks.push(task);
12068         task.taskStartTime = new Date().getTime();
12069         task.taskRunTime = 0;
12070         task.taskRunCount = 0;
12071         startThread();
12072         return task;
12073     };
12074
12075     this.stop = function(task){
12076         removeTask(task);
12077         return task;
12078     };
12079
12080     this.stopAll = function(){
12081         stopThread();
12082         for(var i = 0, len = tasks.length; i < len; i++){
12083             if(tasks[i].onStop){
12084                 tasks[i].onStop();
12085             }
12086         }
12087         tasks = [];
12088         removeQueue = [];
12089     };
12090 };
12091
12092 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103  
12104 /**
12105  * @class Roo.util.MixedCollection
12106  * @extends Roo.util.Observable
12107  * A Collection class that maintains both numeric indexes and keys and exposes events.
12108  * @constructor
12109  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12110  * collection (defaults to false)
12111  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12112  * and return the key value for that item.  This is used when available to look up the key on items that
12113  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12114  * equivalent to providing an implementation for the {@link #getKey} method.
12115  */
12116 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12117     this.items = [];
12118     this.map = {};
12119     this.keys = [];
12120     this.length = 0;
12121     this.addEvents({
12122         /**
12123          * @event clear
12124          * Fires when the collection is cleared.
12125          */
12126         "clear" : true,
12127         /**
12128          * @event add
12129          * Fires when an item is added to the collection.
12130          * @param {Number} index The index at which the item was added.
12131          * @param {Object} o The item added.
12132          * @param {String} key The key associated with the added item.
12133          */
12134         "add" : true,
12135         /**
12136          * @event replace
12137          * Fires when an item is replaced in the collection.
12138          * @param {String} key he key associated with the new added.
12139          * @param {Object} old The item being replaced.
12140          * @param {Object} new The new item.
12141          */
12142         "replace" : true,
12143         /**
12144          * @event remove
12145          * Fires when an item is removed from the collection.
12146          * @param {Object} o The item being removed.
12147          * @param {String} key (optional) The key associated with the removed item.
12148          */
12149         "remove" : true,
12150         "sort" : true
12151     });
12152     this.allowFunctions = allowFunctions === true;
12153     if(keyFn){
12154         this.getKey = keyFn;
12155     }
12156     Roo.util.MixedCollection.superclass.constructor.call(this);
12157 };
12158
12159 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12160     allowFunctions : false,
12161     
12162 /**
12163  * Adds an item to the collection.
12164  * @param {String} key The key to associate with the item
12165  * @param {Object} o The item to add.
12166  * @return {Object} The item added.
12167  */
12168     add : function(key, o){
12169         if(arguments.length == 1){
12170             o = arguments[0];
12171             key = this.getKey(o);
12172         }
12173         if(typeof key == "undefined" || key === null){
12174             this.length++;
12175             this.items.push(o);
12176             this.keys.push(null);
12177         }else{
12178             var old = this.map[key];
12179             if(old){
12180                 return this.replace(key, o);
12181             }
12182             this.length++;
12183             this.items.push(o);
12184             this.map[key] = o;
12185             this.keys.push(key);
12186         }
12187         this.fireEvent("add", this.length-1, o, key);
12188         return o;
12189     },
12190    
12191 /**
12192   * MixedCollection has a generic way to fetch keys if you implement getKey.
12193 <pre><code>
12194 // normal way
12195 var mc = new Roo.util.MixedCollection();
12196 mc.add(someEl.dom.id, someEl);
12197 mc.add(otherEl.dom.id, otherEl);
12198 //and so on
12199
12200 // using getKey
12201 var mc = new Roo.util.MixedCollection();
12202 mc.getKey = function(el){
12203    return el.dom.id;
12204 };
12205 mc.add(someEl);
12206 mc.add(otherEl);
12207
12208 // or via the constructor
12209 var mc = new Roo.util.MixedCollection(false, function(el){
12210    return el.dom.id;
12211 });
12212 mc.add(someEl);
12213 mc.add(otherEl);
12214 </code></pre>
12215  * @param o {Object} The item for which to find the key.
12216  * @return {Object} The key for the passed item.
12217  */
12218     getKey : function(o){
12219          return o.id; 
12220     },
12221    
12222 /**
12223  * Replaces an item in the collection.
12224  * @param {String} key The key associated with the item to replace, or the item to replace.
12225  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12226  * @return {Object}  The new item.
12227  */
12228     replace : function(key, o){
12229         if(arguments.length == 1){
12230             o = arguments[0];
12231             key = this.getKey(o);
12232         }
12233         var old = this.item(key);
12234         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12235              return this.add(key, o);
12236         }
12237         var index = this.indexOfKey(key);
12238         this.items[index] = o;
12239         this.map[key] = o;
12240         this.fireEvent("replace", key, old, o);
12241         return o;
12242     },
12243    
12244 /**
12245  * Adds all elements of an Array or an Object to the collection.
12246  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12247  * an Array of values, each of which are added to the collection.
12248  */
12249     addAll : function(objs){
12250         if(arguments.length > 1 || objs instanceof Array){
12251             var args = arguments.length > 1 ? arguments : objs;
12252             for(var i = 0, len = args.length; i < len; i++){
12253                 this.add(args[i]);
12254             }
12255         }else{
12256             for(var key in objs){
12257                 if(this.allowFunctions || typeof objs[key] != "function"){
12258                     this.add(key, objs[key]);
12259                 }
12260             }
12261         }
12262     },
12263    
12264 /**
12265  * Executes the specified function once for every item in the collection, passing each
12266  * item as the first and only parameter. returning false from the function will stop the iteration.
12267  * @param {Function} fn The function to execute for each item.
12268  * @param {Object} scope (optional) The scope in which to execute the function.
12269  */
12270     each : function(fn, scope){
12271         var items = [].concat(this.items); // each safe for removal
12272         for(var i = 0, len = items.length; i < len; i++){
12273             if(fn.call(scope || items[i], items[i], i, len) === false){
12274                 break;
12275             }
12276         }
12277     },
12278    
12279 /**
12280  * Executes the specified function once for every key in the collection, passing each
12281  * key, and its associated item as the first two parameters.
12282  * @param {Function} fn The function to execute for each item.
12283  * @param {Object} scope (optional) The scope in which to execute the function.
12284  */
12285     eachKey : function(fn, scope){
12286         for(var i = 0, len = this.keys.length; i < len; i++){
12287             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12288         }
12289     },
12290    
12291 /**
12292  * Returns the first item in the collection which elicits a true return value from the
12293  * passed selection function.
12294  * @param {Function} fn The selection function to execute for each item.
12295  * @param {Object} scope (optional) The scope in which to execute the function.
12296  * @return {Object} The first item in the collection which returned true from the selection function.
12297  */
12298     find : function(fn, scope){
12299         for(var i = 0, len = this.items.length; i < len; i++){
12300             if(fn.call(scope || window, this.items[i], this.keys[i])){
12301                 return this.items[i];
12302             }
12303         }
12304         return null;
12305     },
12306    
12307 /**
12308  * Inserts an item at the specified index in the collection.
12309  * @param {Number} index The index to insert the item at.
12310  * @param {String} key The key to associate with the new item, or the item itself.
12311  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12312  * @return {Object} The item inserted.
12313  */
12314     insert : function(index, key, o){
12315         if(arguments.length == 2){
12316             o = arguments[1];
12317             key = this.getKey(o);
12318         }
12319         if(index >= this.length){
12320             return this.add(key, o);
12321         }
12322         this.length++;
12323         this.items.splice(index, 0, o);
12324         if(typeof key != "undefined" && key != null){
12325             this.map[key] = o;
12326         }
12327         this.keys.splice(index, 0, key);
12328         this.fireEvent("add", index, o, key);
12329         return o;
12330     },
12331    
12332 /**
12333  * Removed an item from the collection.
12334  * @param {Object} o The item to remove.
12335  * @return {Object} The item removed.
12336  */
12337     remove : function(o){
12338         return this.removeAt(this.indexOf(o));
12339     },
12340    
12341 /**
12342  * Remove an item from a specified index in the collection.
12343  * @param {Number} index The index within the collection of the item to remove.
12344  */
12345     removeAt : function(index){
12346         if(index < this.length && index >= 0){
12347             this.length--;
12348             var o = this.items[index];
12349             this.items.splice(index, 1);
12350             var key = this.keys[index];
12351             if(typeof key != "undefined"){
12352                 delete this.map[key];
12353             }
12354             this.keys.splice(index, 1);
12355             this.fireEvent("remove", o, key);
12356         }
12357     },
12358    
12359 /**
12360  * Removed an item associated with the passed key fom the collection.
12361  * @param {String} key The key of the item to remove.
12362  */
12363     removeKey : function(key){
12364         return this.removeAt(this.indexOfKey(key));
12365     },
12366    
12367 /**
12368  * Returns the number of items in the collection.
12369  * @return {Number} the number of items in the collection.
12370  */
12371     getCount : function(){
12372         return this.length; 
12373     },
12374    
12375 /**
12376  * Returns index within the collection of the passed Object.
12377  * @param {Object} o The item to find the index of.
12378  * @return {Number} index of the item.
12379  */
12380     indexOf : function(o){
12381         if(!this.items.indexOf){
12382             for(var i = 0, len = this.items.length; i < len; i++){
12383                 if(this.items[i] == o) return i;
12384             }
12385             return -1;
12386         }else{
12387             return this.items.indexOf(o);
12388         }
12389     },
12390    
12391 /**
12392  * Returns index within the collection of the passed key.
12393  * @param {String} key The key to find the index of.
12394  * @return {Number} index of the key.
12395  */
12396     indexOfKey : function(key){
12397         if(!this.keys.indexOf){
12398             for(var i = 0, len = this.keys.length; i < len; i++){
12399                 if(this.keys[i] == key) return i;
12400             }
12401             return -1;
12402         }else{
12403             return this.keys.indexOf(key);
12404         }
12405     },
12406    
12407 /**
12408  * Returns the item associated with the passed key OR index. Key has priority over index.
12409  * @param {String/Number} key The key or index of the item.
12410  * @return {Object} The item associated with the passed key.
12411  */
12412     item : function(key){
12413         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12414         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12415     },
12416     
12417 /**
12418  * Returns the item at the specified index.
12419  * @param {Number} index The index of the item.
12420  * @return {Object}
12421  */
12422     itemAt : function(index){
12423         return this.items[index];
12424     },
12425     
12426 /**
12427  * Returns the item associated with the passed key.
12428  * @param {String/Number} key The key of the item.
12429  * @return {Object} The item associated with the passed key.
12430  */
12431     key : function(key){
12432         return this.map[key];
12433     },
12434    
12435 /**
12436  * Returns true if the collection contains the passed Object as an item.
12437  * @param {Object} o  The Object to look for in the collection.
12438  * @return {Boolean} True if the collection contains the Object as an item.
12439  */
12440     contains : function(o){
12441         return this.indexOf(o) != -1;
12442     },
12443    
12444 /**
12445  * Returns true if the collection contains the passed Object as a key.
12446  * @param {String} key The key to look for in the collection.
12447  * @return {Boolean} True if the collection contains the Object as a key.
12448  */
12449     containsKey : function(key){
12450         return typeof this.map[key] != "undefined";
12451     },
12452    
12453 /**
12454  * Removes all items from the collection.
12455  */
12456     clear : function(){
12457         this.length = 0;
12458         this.items = [];
12459         this.keys = [];
12460         this.map = {};
12461         this.fireEvent("clear");
12462     },
12463    
12464 /**
12465  * Returns the first item in the collection.
12466  * @return {Object} the first item in the collection..
12467  */
12468     first : function(){
12469         return this.items[0]; 
12470     },
12471    
12472 /**
12473  * Returns the last item in the collection.
12474  * @return {Object} the last item in the collection..
12475  */
12476     last : function(){
12477         return this.items[this.length-1];   
12478     },
12479     
12480     _sort : function(property, dir, fn){
12481         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12482         fn = fn || function(a, b){
12483             return a-b;
12484         };
12485         var c = [], k = this.keys, items = this.items;
12486         for(var i = 0, len = items.length; i < len; i++){
12487             c[c.length] = {key: k[i], value: items[i], index: i};
12488         }
12489         c.sort(function(a, b){
12490             var v = fn(a[property], b[property]) * dsc;
12491             if(v == 0){
12492                 v = (a.index < b.index ? -1 : 1);
12493             }
12494             return v;
12495         });
12496         for(var i = 0, len = c.length; i < len; i++){
12497             items[i] = c[i].value;
12498             k[i] = c[i].key;
12499         }
12500         this.fireEvent("sort", this);
12501     },
12502     
12503     /**
12504      * Sorts this collection with the passed comparison function
12505      * @param {String} direction (optional) "ASC" or "DESC"
12506      * @param {Function} fn (optional) comparison function
12507      */
12508     sort : function(dir, fn){
12509         this._sort("value", dir, fn);
12510     },
12511     
12512     /**
12513      * Sorts this collection by keys
12514      * @param {String} direction (optional) "ASC" or "DESC"
12515      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12516      */
12517     keySort : function(dir, fn){
12518         this._sort("key", dir, fn || function(a, b){
12519             return String(a).toUpperCase()-String(b).toUpperCase();
12520         });
12521     },
12522     
12523     /**
12524      * Returns a range of items in this collection
12525      * @param {Number} startIndex (optional) defaults to 0
12526      * @param {Number} endIndex (optional) default to the last item
12527      * @return {Array} An array of items
12528      */
12529     getRange : function(start, end){
12530         var items = this.items;
12531         if(items.length < 1){
12532             return [];
12533         }
12534         start = start || 0;
12535         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12536         var r = [];
12537         if(start <= end){
12538             for(var i = start; i <= end; i++) {
12539                     r[r.length] = items[i];
12540             }
12541         }else{
12542             for(var i = start; i >= end; i--) {
12543                     r[r.length] = items[i];
12544             }
12545         }
12546         return r;
12547     },
12548         
12549     /**
12550      * Filter the <i>objects</i> in this collection by a specific property. 
12551      * Returns a new collection that has been filtered.
12552      * @param {String} property A property on your objects
12553      * @param {String/RegExp} value Either string that the property values 
12554      * should start with or a RegExp to test against the property
12555      * @return {MixedCollection} The new filtered collection
12556      */
12557     filter : function(property, value){
12558         if(!value.exec){ // not a regex
12559             value = String(value);
12560             if(value.length == 0){
12561                 return this.clone();
12562             }
12563             value = new RegExp("^" + Roo.escapeRe(value), "i");
12564         }
12565         return this.filterBy(function(o){
12566             return o && value.test(o[property]);
12567         });
12568         },
12569     
12570     /**
12571      * Filter by a function. * Returns a new collection that has been filtered.
12572      * The passed function will be called with each 
12573      * object in the collection. If the function returns true, the value is included 
12574      * otherwise it is filtered.
12575      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12576      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12577      * @return {MixedCollection} The new filtered collection
12578      */
12579     filterBy : function(fn, scope){
12580         var r = new Roo.util.MixedCollection();
12581         r.getKey = this.getKey;
12582         var k = this.keys, it = this.items;
12583         for(var i = 0, len = it.length; i < len; i++){
12584             if(fn.call(scope||this, it[i], k[i])){
12585                                 r.add(k[i], it[i]);
12586                         }
12587         }
12588         return r;
12589     },
12590     
12591     /**
12592      * Creates a duplicate of this collection
12593      * @return {MixedCollection}
12594      */
12595     clone : function(){
12596         var r = new Roo.util.MixedCollection();
12597         var k = this.keys, it = this.items;
12598         for(var i = 0, len = it.length; i < len; i++){
12599             r.add(k[i], it[i]);
12600         }
12601         r.getKey = this.getKey;
12602         return r;
12603     }
12604 });
12605 /**
12606  * Returns the item associated with the passed key or index.
12607  * @method
12608  * @param {String/Number} key The key or index of the item.
12609  * @return {Object} The item associated with the passed key.
12610  */
12611 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12612  * Based on:
12613  * Ext JS Library 1.1.1
12614  * Copyright(c) 2006-2007, Ext JS, LLC.
12615  *
12616  * Originally Released Under LGPL - original licence link has changed is not relivant.
12617  *
12618  * Fork - LGPL
12619  * <script type="text/javascript">
12620  */
12621 /**
12622  * @class Roo.util.JSON
12623  * Modified version of Douglas Crockford"s json.js that doesn"t
12624  * mess with the Object prototype 
12625  * http://www.json.org/js.html
12626  * @singleton
12627  */
12628 Roo.util.JSON = new (function(){
12629     var useHasOwn = {}.hasOwnProperty ? true : false;
12630     
12631     // crashes Safari in some instances
12632     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12633     
12634     var pad = function(n) {
12635         return n < 10 ? "0" + n : n;
12636     };
12637     
12638     var m = {
12639         "\b": '\\b',
12640         "\t": '\\t',
12641         "\n": '\\n',
12642         "\f": '\\f',
12643         "\r": '\\r',
12644         '"' : '\\"',
12645         "\\": '\\\\'
12646     };
12647
12648     var encodeString = function(s){
12649         if (/["\\\x00-\x1f]/.test(s)) {
12650             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12651                 var c = m[b];
12652                 if(c){
12653                     return c;
12654                 }
12655                 c = b.charCodeAt();
12656                 return "\\u00" +
12657                     Math.floor(c / 16).toString(16) +
12658                     (c % 16).toString(16);
12659             }) + '"';
12660         }
12661         return '"' + s + '"';
12662     };
12663     
12664     var encodeArray = function(o){
12665         var a = ["["], b, i, l = o.length, v;
12666             for (i = 0; i < l; i += 1) {
12667                 v = o[i];
12668                 switch (typeof v) {
12669                     case "undefined":
12670                     case "function":
12671                     case "unknown":
12672                         break;
12673                     default:
12674                         if (b) {
12675                             a.push(',');
12676                         }
12677                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12678                         b = true;
12679                 }
12680             }
12681             a.push("]");
12682             return a.join("");
12683     };
12684     
12685     var encodeDate = function(o){
12686         return '"' + o.getFullYear() + "-" +
12687                 pad(o.getMonth() + 1) + "-" +
12688                 pad(o.getDate()) + "T" +
12689                 pad(o.getHours()) + ":" +
12690                 pad(o.getMinutes()) + ":" +
12691                 pad(o.getSeconds()) + '"';
12692     };
12693     
12694     /**
12695      * Encodes an Object, Array or other value
12696      * @param {Mixed} o The variable to encode
12697      * @return {String} The JSON string
12698      */
12699     this.encode = function(o){
12700         if(typeof o == "undefined" || o === null){
12701             return "null";
12702         }else if(o instanceof Array){
12703             return encodeArray(o);
12704         }else if(o instanceof Date){
12705             return encodeDate(o);
12706         }else if(typeof o == "string"){
12707             return encodeString(o);
12708         }else if(typeof o == "number"){
12709             return isFinite(o) ? String(o) : "null";
12710         }else if(typeof o == "boolean"){
12711             return String(o);
12712         }else {
12713             var a = ["{"], b, i, v;
12714             for (i in o) {
12715                 if(!useHasOwn || o.hasOwnProperty(i)) {
12716                     v = o[i];
12717                     switch (typeof v) {
12718                     case "undefined":
12719                     case "function":
12720                     case "unknown":
12721                         break;
12722                     default:
12723                         if(b){
12724                             a.push(',');
12725                         }
12726                         a.push(this.encode(i), ":",
12727                                 v === null ? "null" : this.encode(v));
12728                         b = true;
12729                     }
12730                 }
12731             }
12732             a.push("}");
12733             return a.join("");
12734         }
12735     };
12736     
12737     /**
12738      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12739      * @param {String} json The JSON string
12740      * @return {Object} The resulting object
12741      */
12742     this.decode = function(json){
12743         /**
12744          * eval:var:json
12745          */
12746         return eval("(" + json + ')');
12747     };
12748 })();
12749 /** 
12750  * Shorthand for {@link Roo.util.JSON#encode}
12751  * @member Roo encode 
12752  * @method */
12753 Roo.encode = Roo.util.JSON.encode;
12754 /** 
12755  * Shorthand for {@link Roo.util.JSON#decode}
12756  * @member Roo decode 
12757  * @method */
12758 Roo.decode = Roo.util.JSON.decode;
12759 /*
12760  * Based on:
12761  * Ext JS Library 1.1.1
12762  * Copyright(c) 2006-2007, Ext JS, LLC.
12763  *
12764  * Originally Released Under LGPL - original licence link has changed is not relivant.
12765  *
12766  * Fork - LGPL
12767  * <script type="text/javascript">
12768  */
12769  
12770 /**
12771  * @class Roo.util.Format
12772  * Reusable data formatting functions
12773  * @singleton
12774  */
12775 Roo.util.Format = function(){
12776     var trimRe = /^\s+|\s+$/g;
12777     return {
12778         /**
12779          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12780          * @param {String} value The string to truncate
12781          * @param {Number} length The maximum length to allow before truncating
12782          * @return {String} The converted text
12783          */
12784         ellipsis : function(value, len){
12785             if(value && value.length > len){
12786                 return value.substr(0, len-3)+"...";
12787             }
12788             return value;
12789         },
12790
12791         /**
12792          * Checks a reference and converts it to empty string if it is undefined
12793          * @param {Mixed} value Reference to check
12794          * @return {Mixed} Empty string if converted, otherwise the original value
12795          */
12796         undef : function(value){
12797             return typeof value != "undefined" ? value : "";
12798         },
12799
12800         /**
12801          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12802          * @param {String} value The string to encode
12803          * @return {String} The encoded text
12804          */
12805         htmlEncode : function(value){
12806             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12807         },
12808
12809         /**
12810          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12811          * @param {String} value The string to decode
12812          * @return {String} The decoded text
12813          */
12814         htmlDecode : function(value){
12815             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12816         },
12817
12818         /**
12819          * Trims any whitespace from either side of a string
12820          * @param {String} value The text to trim
12821          * @return {String} The trimmed text
12822          */
12823         trim : function(value){
12824             return String(value).replace(trimRe, "");
12825         },
12826
12827         /**
12828          * Returns a substring from within an original string
12829          * @param {String} value The original text
12830          * @param {Number} start The start index of the substring
12831          * @param {Number} length The length of the substring
12832          * @return {String} The substring
12833          */
12834         substr : function(value, start, length){
12835             return String(value).substr(start, length);
12836         },
12837
12838         /**
12839          * Converts a string to all lower case letters
12840          * @param {String} value The text to convert
12841          * @return {String} The converted text
12842          */
12843         lowercase : function(value){
12844             return String(value).toLowerCase();
12845         },
12846
12847         /**
12848          * Converts a string to all upper case letters
12849          * @param {String} value The text to convert
12850          * @return {String} The converted text
12851          */
12852         uppercase : function(value){
12853             return String(value).toUpperCase();
12854         },
12855
12856         /**
12857          * Converts the first character only of a string to upper case
12858          * @param {String} value The text to convert
12859          * @return {String} The converted text
12860          */
12861         capitalize : function(value){
12862             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12863         },
12864
12865         // private
12866         call : function(value, fn){
12867             if(arguments.length > 2){
12868                 var args = Array.prototype.slice.call(arguments, 2);
12869                 args.unshift(value);
12870                  
12871                 return /** eval:var:value */  eval(fn).apply(window, args);
12872             }else{
12873                 /** eval:var:value */
12874                 return /** eval:var:value */ eval(fn).call(window, value);
12875             }
12876         },
12877
12878         /**
12879          * Format a number as US currency
12880          * @param {Number/String} value The numeric value to format
12881          * @return {String} The formatted currency string
12882          */
12883         usMoney : function(v){
12884             v = (Math.round((v-0)*100))/100;
12885             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12886             v = String(v);
12887             var ps = v.split('.');
12888             var whole = ps[0];
12889             var sub = ps[1] ? '.'+ ps[1] : '.00';
12890             var r = /(\d+)(\d{3})/;
12891             while (r.test(whole)) {
12892                 whole = whole.replace(r, '$1' + ',' + '$2');
12893             }
12894             return "$" + whole + sub ;
12895         },
12896
12897         /**
12898          * Parse a value into a formatted date using the specified format pattern.
12899          * @param {Mixed} value The value to format
12900          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12901          * @return {String} The formatted date string
12902          */
12903         date : function(v, format){
12904             if(!v){
12905                 return "";
12906             }
12907             if(!(v instanceof Date)){
12908                 v = new Date(Date.parse(v));
12909             }
12910             return v.dateFormat(format || "m/d/Y");
12911         },
12912
12913         /**
12914          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12915          * @param {String} format Any valid date format string
12916          * @return {Function} The date formatting function
12917          */
12918         dateRenderer : function(format){
12919             return function(v){
12920                 return Roo.util.Format.date(v, format);  
12921             };
12922         },
12923
12924         // private
12925         stripTagsRE : /<\/?[^>]+>/gi,
12926         
12927         /**
12928          * Strips all HTML tags
12929          * @param {Mixed} value The text from which to strip tags
12930          * @return {String} The stripped text
12931          */
12932         stripTags : function(v){
12933             return !v ? v : String(v).replace(this.stripTagsRE, "");
12934         }
12935     };
12936 }();/*
12937  * Based on:
12938  * Ext JS Library 1.1.1
12939  * Copyright(c) 2006-2007, Ext JS, LLC.
12940  *
12941  * Originally Released Under LGPL - original licence link has changed is not relivant.
12942  *
12943  * Fork - LGPL
12944  * <script type="text/javascript">
12945  */
12946
12947
12948  
12949
12950 /**
12951  * @class Roo.MasterTemplate
12952  * @extends Roo.Template
12953  * Provides a template that can have child templates. The syntax is:
12954 <pre><code>
12955 var t = new Roo.MasterTemplate(
12956         '&lt;select name="{name}"&gt;',
12957                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12958         '&lt;/select&gt;'
12959 );
12960 t.add('options', {value: 'foo', text: 'bar'});
12961 // or you can add multiple child elements in one shot
12962 t.addAll('options', [
12963     {value: 'foo', text: 'bar'},
12964     {value: 'foo2', text: 'bar2'},
12965     {value: 'foo3', text: 'bar3'}
12966 ]);
12967 // then append, applying the master template values
12968 t.append('my-form', {name: 'my-select'});
12969 </code></pre>
12970 * A name attribute for the child template is not required if you have only one child
12971 * template or you want to refer to them by index.
12972  */
12973 Roo.MasterTemplate = function(){
12974     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12975     this.originalHtml = this.html;
12976     var st = {};
12977     var m, re = this.subTemplateRe;
12978     re.lastIndex = 0;
12979     var subIndex = 0;
12980     while(m = re.exec(this.html)){
12981         var name = m[1], content = m[2];
12982         st[subIndex] = {
12983             name: name,
12984             index: subIndex,
12985             buffer: [],
12986             tpl : new Roo.Template(content)
12987         };
12988         if(name){
12989             st[name] = st[subIndex];
12990         }
12991         st[subIndex].tpl.compile();
12992         st[subIndex].tpl.call = this.call.createDelegate(this);
12993         subIndex++;
12994     }
12995     this.subCount = subIndex;
12996     this.subs = st;
12997 };
12998 Roo.extend(Roo.MasterTemplate, Roo.Template, {
12999     /**
13000     * The regular expression used to match sub templates
13001     * @type RegExp
13002     * @property
13003     */
13004     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13005
13006     /**
13007      * Applies the passed values to a child template.
13008      * @param {String/Number} name (optional) The name or index of the child template
13009      * @param {Array/Object} values The values to be applied to the template
13010      * @return {MasterTemplate} this
13011      */
13012      add : function(name, values){
13013         if(arguments.length == 1){
13014             values = arguments[0];
13015             name = 0;
13016         }
13017         var s = this.subs[name];
13018         s.buffer[s.buffer.length] = s.tpl.apply(values);
13019         return this;
13020     },
13021
13022     /**
13023      * Applies all the passed values to a child template.
13024      * @param {String/Number} name (optional) The name or index of the child template
13025      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13026      * @param {Boolean} reset (optional) True to reset the template first
13027      * @return {MasterTemplate} this
13028      */
13029     fill : function(name, values, reset){
13030         var a = arguments;
13031         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13032             values = a[0];
13033             name = 0;
13034             reset = a[1];
13035         }
13036         if(reset){
13037             this.reset();
13038         }
13039         for(var i = 0, len = values.length; i < len; i++){
13040             this.add(name, values[i]);
13041         }
13042         return this;
13043     },
13044
13045     /**
13046      * Resets the template for reuse
13047      * @return {MasterTemplate} this
13048      */
13049      reset : function(){
13050         var s = this.subs;
13051         for(var i = 0; i < this.subCount; i++){
13052             s[i].buffer = [];
13053         }
13054         return this;
13055     },
13056
13057     applyTemplate : function(values){
13058         var s = this.subs;
13059         var replaceIndex = -1;
13060         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13061             return s[++replaceIndex].buffer.join("");
13062         });
13063         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13064     },
13065
13066     apply : function(){
13067         return this.applyTemplate.apply(this, arguments);
13068     },
13069
13070     compile : function(){return this;}
13071 });
13072
13073 /**
13074  * Alias for fill().
13075  * @method
13076  */
13077 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13078  /**
13079  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13080  * var tpl = Roo.MasterTemplate.from('element-id');
13081  * @param {String/HTMLElement} el
13082  * @param {Object} config
13083  * @static
13084  */
13085 Roo.MasterTemplate.from = function(el, config){
13086     el = Roo.getDom(el);
13087     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13088 };/*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098
13099  
13100 /**
13101  * @class Roo.util.CSS
13102  * Utility class for manipulating CSS rules
13103  * @singleton
13104  */
13105 Roo.util.CSS = function(){
13106         var rules = null;
13107         var doc = document;
13108
13109     var camelRe = /(-[a-z])/gi;
13110     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13111
13112    return {
13113    /**
13114     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13115     * tag and appended to the HEAD of the document.
13116     * @param {String} cssText The text containing the css rules
13117     * @param {String} id An id to add to the stylesheet for later removal
13118     * @return {StyleSheet}
13119     */
13120    createStyleSheet : function(cssText, id){
13121        var ss;
13122        var head = doc.getElementsByTagName("head")[0];
13123        var rules = doc.createElement("style");
13124        rules.setAttribute("type", "text/css");
13125        if(id){
13126            rules.setAttribute("id", id);
13127        }
13128        if(Roo.isIE){
13129            head.appendChild(rules);
13130            ss = rules.styleSheet;
13131            ss.cssText = cssText;
13132        }else{
13133            try{
13134                 rules.appendChild(doc.createTextNode(cssText));
13135            }catch(e){
13136                rules.cssText = cssText; 
13137            }
13138            head.appendChild(rules);
13139            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13140        }
13141        this.cacheStyleSheet(ss);
13142        return ss;
13143    },
13144
13145    /**
13146     * Removes a style or link tag by id
13147     * @param {String} id The id of the tag
13148     */
13149    removeStyleSheet : function(id){
13150        var existing = doc.getElementById(id);
13151        if(existing){
13152            existing.parentNode.removeChild(existing);
13153        }
13154    },
13155
13156    /**
13157     * Dynamically swaps an existing stylesheet reference for a new one
13158     * @param {String} id The id of an existing link tag to remove
13159     * @param {String} url The href of the new stylesheet to include
13160     */
13161    swapStyleSheet : function(id, url){
13162        this.removeStyleSheet(id);
13163        var ss = doc.createElement("link");
13164        ss.setAttribute("rel", "stylesheet");
13165        ss.setAttribute("type", "text/css");
13166        ss.setAttribute("id", id);
13167        ss.setAttribute("href", url);
13168        doc.getElementsByTagName("head")[0].appendChild(ss);
13169    },
13170    
13171    /**
13172     * Refresh the rule cache if you have dynamically added stylesheets
13173     * @return {Object} An object (hash) of rules indexed by selector
13174     */
13175    refreshCache : function(){
13176        return this.getRules(true);
13177    },
13178
13179    // private
13180    cacheStyleSheet : function(ss){
13181        if(!rules){
13182            rules = {};
13183        }
13184        try{// try catch for cross domain access issue
13185            var ssRules = ss.cssRules || ss.rules;
13186            for(var j = ssRules.length-1; j >= 0; --j){
13187                rules[ssRules[j].selectorText] = ssRules[j];
13188            }
13189        }catch(e){}
13190    },
13191    
13192    /**
13193     * Gets all css rules for the document
13194     * @param {Boolean} refreshCache true to refresh the internal cache
13195     * @return {Object} An object (hash) of rules indexed by selector
13196     */
13197    getRules : function(refreshCache){
13198                 if(rules == null || refreshCache){
13199                         rules = {};
13200                         var ds = doc.styleSheets;
13201                         for(var i =0, len = ds.length; i < len; i++){
13202                             try{
13203                         this.cacheStyleSheet(ds[i]);
13204                     }catch(e){} 
13205                 }
13206                 }
13207                 return rules;
13208         },
13209         
13210         /**
13211     * Gets an an individual CSS rule by selector(s)
13212     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13213     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13214     * @return {CSSRule} The CSS rule or null if one is not found
13215     */
13216    getRule : function(selector, refreshCache){
13217                 var rs = this.getRules(refreshCache);
13218                 if(!(selector instanceof Array)){
13219                     return rs[selector];
13220                 }
13221                 for(var i = 0; i < selector.length; i++){
13222                         if(rs[selector[i]]){
13223                                 return rs[selector[i]];
13224                         }
13225                 }
13226                 return null;
13227         },
13228         
13229         
13230         /**
13231     * Updates a rule property
13232     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13233     * @param {String} property The css property
13234     * @param {String} value The new value for the property
13235     * @return {Boolean} true If a rule was found and updated
13236     */
13237    updateRule : function(selector, property, value){
13238                 if(!(selector instanceof Array)){
13239                         var rule = this.getRule(selector);
13240                         if(rule){
13241                                 rule.style[property.replace(camelRe, camelFn)] = value;
13242                                 return true;
13243                         }
13244                 }else{
13245                         for(var i = 0; i < selector.length; i++){
13246                                 if(this.updateRule(selector[i], property, value)){
13247                                         return true;
13248                                 }
13249                         }
13250                 }
13251                 return false;
13252         }
13253    };   
13254 }();/*
13255  * Based on:
13256  * Ext JS Library 1.1.1
13257  * Copyright(c) 2006-2007, Ext JS, LLC.
13258  *
13259  * Originally Released Under LGPL - original licence link has changed is not relivant.
13260  *
13261  * Fork - LGPL
13262  * <script type="text/javascript">
13263  */
13264
13265  
13266
13267 /**
13268  * @class Roo.util.ClickRepeater
13269  * @extends Roo.util.Observable
13270  * 
13271  * A wrapper class which can be applied to any element. Fires a "click" event while the
13272  * mouse is pressed. The interval between firings may be specified in the config but
13273  * defaults to 10 milliseconds.
13274  * 
13275  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13276  * 
13277  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13278  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13279  * Similar to an autorepeat key delay.
13280  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13281  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13282  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13283  *           "interval" and "delay" are ignored. "immediate" is honored.
13284  * @cfg {Boolean} preventDefault True to prevent the default click event
13285  * @cfg {Boolean} stopDefault True to stop the default click event
13286  * 
13287  * @history
13288  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13289  *     2007-02-02 jvs Renamed to ClickRepeater
13290  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13291  *
13292  *  @constructor
13293  * @param {String/HTMLElement/Element} el The element to listen on
13294  * @param {Object} config
13295  **/
13296 Roo.util.ClickRepeater = function(el, config)
13297 {
13298     this.el = Roo.get(el);
13299     this.el.unselectable();
13300
13301     Roo.apply(this, config);
13302
13303     this.addEvents({
13304     /**
13305      * @event mousedown
13306      * Fires when the mouse button is depressed.
13307      * @param {Roo.util.ClickRepeater} this
13308      */
13309         "mousedown" : true,
13310     /**
13311      * @event click
13312      * Fires on a specified interval during the time the element is pressed.
13313      * @param {Roo.util.ClickRepeater} this
13314      */
13315         "click" : true,
13316     /**
13317      * @event mouseup
13318      * Fires when the mouse key is released.
13319      * @param {Roo.util.ClickRepeater} this
13320      */
13321         "mouseup" : true
13322     });
13323
13324     this.el.on("mousedown", this.handleMouseDown, this);
13325     if(this.preventDefault || this.stopDefault){
13326         this.el.on("click", function(e){
13327             if(this.preventDefault){
13328                 e.preventDefault();
13329             }
13330             if(this.stopDefault){
13331                 e.stopEvent();
13332             }
13333         }, this);
13334     }
13335
13336     // allow inline handler
13337     if(this.handler){
13338         this.on("click", this.handler,  this.scope || this);
13339     }
13340
13341     Roo.util.ClickRepeater.superclass.constructor.call(this);
13342 };
13343
13344 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13345     interval : 20,
13346     delay: 250,
13347     preventDefault : true,
13348     stopDefault : false,
13349     timer : 0,
13350
13351     // private
13352     handleMouseDown : function(){
13353         clearTimeout(this.timer);
13354         this.el.blur();
13355         if(this.pressClass){
13356             this.el.addClass(this.pressClass);
13357         }
13358         this.mousedownTime = new Date();
13359
13360         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13361         this.el.on("mouseout", this.handleMouseOut, this);
13362
13363         this.fireEvent("mousedown", this);
13364         this.fireEvent("click", this);
13365         
13366         this.timer = this.click.defer(this.delay || this.interval, this);
13367     },
13368
13369     // private
13370     click : function(){
13371         this.fireEvent("click", this);
13372         this.timer = this.click.defer(this.getInterval(), this);
13373     },
13374
13375     // private
13376     getInterval: function(){
13377         if(!this.accelerate){
13378             return this.interval;
13379         }
13380         var pressTime = this.mousedownTime.getElapsed();
13381         if(pressTime < 500){
13382             return 400;
13383         }else if(pressTime < 1700){
13384             return 320;
13385         }else if(pressTime < 2600){
13386             return 250;
13387         }else if(pressTime < 3500){
13388             return 180;
13389         }else if(pressTime < 4400){
13390             return 140;
13391         }else if(pressTime < 5300){
13392             return 80;
13393         }else if(pressTime < 6200){
13394             return 50;
13395         }else{
13396             return 10;
13397         }
13398     },
13399
13400     // private
13401     handleMouseOut : function(){
13402         clearTimeout(this.timer);
13403         if(this.pressClass){
13404             this.el.removeClass(this.pressClass);
13405         }
13406         this.el.on("mouseover", this.handleMouseReturn, this);
13407     },
13408
13409     // private
13410     handleMouseReturn : function(){
13411         this.el.un("mouseover", this.handleMouseReturn);
13412         if(this.pressClass){
13413             this.el.addClass(this.pressClass);
13414         }
13415         this.click();
13416     },
13417
13418     // private
13419     handleMouseUp : function(){
13420         clearTimeout(this.timer);
13421         this.el.un("mouseover", this.handleMouseReturn);
13422         this.el.un("mouseout", this.handleMouseOut);
13423         Roo.get(document).un("mouseup", this.handleMouseUp);
13424         this.el.removeClass(this.pressClass);
13425         this.fireEvent("mouseup", this);
13426     }
13427 });/*
13428  * Based on:
13429  * Ext JS Library 1.1.1
13430  * Copyright(c) 2006-2007, Ext JS, LLC.
13431  *
13432  * Originally Released Under LGPL - original licence link has changed is not relivant.
13433  *
13434  * Fork - LGPL
13435  * <script type="text/javascript">
13436  */
13437
13438  
13439 /**
13440  * @class Roo.KeyNav
13441  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13442  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13443  * way to implement custom navigation schemes for any UI component.</p>
13444  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13445  * pageUp, pageDown, del, home, end.  Usage:</p>
13446  <pre><code>
13447 var nav = new Roo.KeyNav("my-element", {
13448     "left" : function(e){
13449         this.moveLeft(e.ctrlKey);
13450     },
13451     "right" : function(e){
13452         this.moveRight(e.ctrlKey);
13453     },
13454     "enter" : function(e){
13455         this.save();
13456     },
13457     scope : this
13458 });
13459 </code></pre>
13460  * @constructor
13461  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13462  * @param {Object} config The config
13463  */
13464 Roo.KeyNav = function(el, config){
13465     this.el = Roo.get(el);
13466     Roo.apply(this, config);
13467     if(!this.disabled){
13468         this.disabled = true;
13469         this.enable();
13470     }
13471 };
13472
13473 Roo.KeyNav.prototype = {
13474     /**
13475      * @cfg {Boolean} disabled
13476      * True to disable this KeyNav instance (defaults to false)
13477      */
13478     disabled : false,
13479     /**
13480      * @cfg {String} defaultEventAction
13481      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13482      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13483      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13484      */
13485     defaultEventAction: "stopEvent",
13486     /**
13487      * @cfg {Boolean} forceKeyDown
13488      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13489      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13490      * handle keydown instead of keypress.
13491      */
13492     forceKeyDown : false,
13493
13494     // private
13495     prepareEvent : function(e){
13496         var k = e.getKey();
13497         var h = this.keyToHandler[k];
13498         //if(h && this[h]){
13499         //    e.stopPropagation();
13500         //}
13501         if(Roo.isSafari && h && k >= 37 && k <= 40){
13502             e.stopEvent();
13503         }
13504     },
13505
13506     // private
13507     relay : function(e){
13508         var k = e.getKey();
13509         var h = this.keyToHandler[k];
13510         if(h && this[h]){
13511             if(this.doRelay(e, this[h], h) !== true){
13512                 e[this.defaultEventAction]();
13513             }
13514         }
13515     },
13516
13517     // private
13518     doRelay : function(e, h, hname){
13519         return h.call(this.scope || this, e);
13520     },
13521
13522     // possible handlers
13523     enter : false,
13524     left : false,
13525     right : false,
13526     up : false,
13527     down : false,
13528     tab : false,
13529     esc : false,
13530     pageUp : false,
13531     pageDown : false,
13532     del : false,
13533     home : false,
13534     end : false,
13535
13536     // quick lookup hash
13537     keyToHandler : {
13538         37 : "left",
13539         39 : "right",
13540         38 : "up",
13541         40 : "down",
13542         33 : "pageUp",
13543         34 : "pageDown",
13544         46 : "del",
13545         36 : "home",
13546         35 : "end",
13547         13 : "enter",
13548         27 : "esc",
13549         9  : "tab"
13550     },
13551
13552         /**
13553          * Enable this KeyNav
13554          */
13555         enable: function(){
13556                 if(this.disabled){
13557             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13558             // the EventObject will normalize Safari automatically
13559             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13560                 this.el.on("keydown", this.relay,  this);
13561             }else{
13562                 this.el.on("keydown", this.prepareEvent,  this);
13563                 this.el.on("keypress", this.relay,  this);
13564             }
13565                     this.disabled = false;
13566                 }
13567         },
13568
13569         /**
13570          * Disable this KeyNav
13571          */
13572         disable: function(){
13573                 if(!this.disabled){
13574                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13575                 this.el.un("keydown", this.relay);
13576             }else{
13577                 this.el.un("keydown", this.prepareEvent);
13578                 this.el.un("keypress", this.relay);
13579             }
13580                     this.disabled = true;
13581                 }
13582         }
13583 };/*
13584  * Based on:
13585  * Ext JS Library 1.1.1
13586  * Copyright(c) 2006-2007, Ext JS, LLC.
13587  *
13588  * Originally Released Under LGPL - original licence link has changed is not relivant.
13589  *
13590  * Fork - LGPL
13591  * <script type="text/javascript">
13592  */
13593
13594  
13595 /**
13596  * @class Roo.KeyMap
13597  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13598  * The constructor accepts the same config object as defined by {@link #addBinding}.
13599  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13600  * combination it will call the function with this signature (if the match is a multi-key
13601  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13602  * A KeyMap can also handle a string representation of keys.<br />
13603  * Usage:
13604  <pre><code>
13605 // map one key by key code
13606 var map = new Roo.KeyMap("my-element", {
13607     key: 13, // or Roo.EventObject.ENTER
13608     fn: myHandler,
13609     scope: myObject
13610 });
13611
13612 // map multiple keys to one action by string
13613 var map = new Roo.KeyMap("my-element", {
13614     key: "a\r\n\t",
13615     fn: myHandler,
13616     scope: myObject
13617 });
13618
13619 // map multiple keys to multiple actions by strings and array of codes
13620 var map = new Roo.KeyMap("my-element", [
13621     {
13622         key: [10,13],
13623         fn: function(){ alert("Return was pressed"); }
13624     }, {
13625         key: "abc",
13626         fn: function(){ alert('a, b or c was pressed'); }
13627     }, {
13628         key: "\t",
13629         ctrl:true,
13630         shift:true,
13631         fn: function(){ alert('Control + shift + tab was pressed.'); }
13632     }
13633 ]);
13634 </code></pre>
13635  * <b>Note: A KeyMap starts enabled</b>
13636  * @constructor
13637  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13638  * @param {Object} config The config (see {@link #addBinding})
13639  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13640  */
13641 Roo.KeyMap = function(el, config, eventName){
13642     this.el  = Roo.get(el);
13643     this.eventName = eventName || "keydown";
13644     this.bindings = [];
13645     if(config){
13646         this.addBinding(config);
13647     }
13648     this.enable();
13649 };
13650
13651 Roo.KeyMap.prototype = {
13652     /**
13653      * True to stop the event from bubbling and prevent the default browser action if the
13654      * key was handled by the KeyMap (defaults to false)
13655      * @type Boolean
13656      */
13657     stopEvent : false,
13658
13659     /**
13660      * Add a new binding to this KeyMap. The following config object properties are supported:
13661      * <pre>
13662 Property    Type             Description
13663 ----------  ---------------  ----------------------------------------------------------------------
13664 key         String/Array     A single keycode or an array of keycodes to handle
13665 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13666 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13667 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13668 fn          Function         The function to call when KeyMap finds the expected key combination
13669 scope       Object           The scope of the callback function
13670 </pre>
13671      *
13672      * Usage:
13673      * <pre><code>
13674 // Create a KeyMap
13675 var map = new Roo.KeyMap(document, {
13676     key: Roo.EventObject.ENTER,
13677     fn: handleKey,
13678     scope: this
13679 });
13680
13681 //Add a new binding to the existing KeyMap later
13682 map.addBinding({
13683     key: 'abc',
13684     shift: true,
13685     fn: handleKey,
13686     scope: this
13687 });
13688 </code></pre>
13689      * @param {Object/Array} config A single KeyMap config or an array of configs
13690      */
13691         addBinding : function(config){
13692         if(config instanceof Array){
13693             for(var i = 0, len = config.length; i < len; i++){
13694                 this.addBinding(config[i]);
13695             }
13696             return;
13697         }
13698         var keyCode = config.key,
13699             shift = config.shift, 
13700             ctrl = config.ctrl, 
13701             alt = config.alt,
13702             fn = config.fn,
13703             scope = config.scope;
13704         if(typeof keyCode == "string"){
13705             var ks = [];
13706             var keyString = keyCode.toUpperCase();
13707             for(var j = 0, len = keyString.length; j < len; j++){
13708                 ks.push(keyString.charCodeAt(j));
13709             }
13710             keyCode = ks;
13711         }
13712         var keyArray = keyCode instanceof Array;
13713         var handler = function(e){
13714             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13715                 var k = e.getKey();
13716                 if(keyArray){
13717                     for(var i = 0, len = keyCode.length; i < len; i++){
13718                         if(keyCode[i] == k){
13719                           if(this.stopEvent){
13720                               e.stopEvent();
13721                           }
13722                           fn.call(scope || window, k, e);
13723                           return;
13724                         }
13725                     }
13726                 }else{
13727                     if(k == keyCode){
13728                         if(this.stopEvent){
13729                            e.stopEvent();
13730                         }
13731                         fn.call(scope || window, k, e);
13732                     }
13733                 }
13734             }
13735         };
13736         this.bindings.push(handler);  
13737         },
13738
13739     /**
13740      * Shorthand for adding a single key listener
13741      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13742      * following options:
13743      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13744      * @param {Function} fn The function to call
13745      * @param {Object} scope (optional) The scope of the function
13746      */
13747     on : function(key, fn, scope){
13748         var keyCode, shift, ctrl, alt;
13749         if(typeof key == "object" && !(key instanceof Array)){
13750             keyCode = key.key;
13751             shift = key.shift;
13752             ctrl = key.ctrl;
13753             alt = key.alt;
13754         }else{
13755             keyCode = key;
13756         }
13757         this.addBinding({
13758             key: keyCode,
13759             shift: shift,
13760             ctrl: ctrl,
13761             alt: alt,
13762             fn: fn,
13763             scope: scope
13764         })
13765     },
13766
13767     // private
13768     handleKeyDown : function(e){
13769             if(this.enabled){ //just in case
13770             var b = this.bindings;
13771             for(var i = 0, len = b.length; i < len; i++){
13772                 b[i].call(this, e);
13773             }
13774             }
13775         },
13776         
13777         /**
13778          * Returns true if this KeyMap is enabled
13779          * @return {Boolean} 
13780          */
13781         isEnabled : function(){
13782             return this.enabled;  
13783         },
13784         
13785         /**
13786          * Enables this KeyMap
13787          */
13788         enable: function(){
13789                 if(!this.enabled){
13790                     this.el.on(this.eventName, this.handleKeyDown, this);
13791                     this.enabled = true;
13792                 }
13793         },
13794
13795         /**
13796          * Disable this KeyMap
13797          */
13798         disable: function(){
13799                 if(this.enabled){
13800                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13801                     this.enabled = false;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.util.TextMetrics
13818  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13819  * wide, in pixels, a given block of text will be.
13820  * @singleton
13821  */
13822 Roo.util.TextMetrics = function(){
13823     var shared;
13824     return {
13825         /**
13826          * Measures the size of the specified text
13827          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13828          * that can affect the size of the rendered text
13829          * @param {String} text The text to measure
13830          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13831          * in order to accurately measure the text height
13832          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13833          */
13834         measure : function(el, text, fixedWidth){
13835             if(!shared){
13836                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13837             }
13838             shared.bind(el);
13839             shared.setFixedWidth(fixedWidth || 'auto');
13840             return shared.getSize(text);
13841         },
13842
13843         /**
13844          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13845          * the overhead of multiple calls to initialize the style properties on each measurement.
13846          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13847          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13848          * in order to accurately measure the text height
13849          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13850          */
13851         createInstance : function(el, fixedWidth){
13852             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13853         }
13854     };
13855 }();
13856
13857 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13858     var ml = new Roo.Element(document.createElement('div'));
13859     document.body.appendChild(ml.dom);
13860     ml.position('absolute');
13861     ml.setLeftTop(-1000, -1000);
13862     ml.hide();
13863
13864     if(fixedWidth){
13865         ml.setWidth(fixedWidth);
13866     }
13867
13868     var instance = {
13869         /**
13870          * Returns the size of the specified text based on the internal element's style and width properties
13871          * @param {String} text The text to measure
13872          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13873          */
13874         getSize : function(text){
13875             ml.update(text);
13876             var s = ml.getSize();
13877             ml.update('');
13878             return s;
13879         },
13880
13881         /**
13882          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13883          * that can affect the size of the rendered text
13884          * @param {String/HTMLElement} el The element, dom node or id
13885          */
13886         bind : function(el){
13887             ml.setStyle(
13888                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13889             );
13890         },
13891
13892         /**
13893          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13894          * to set a fixed width in order to accurately measure the text height.
13895          * @param {Number} width The width to set on the element
13896          */
13897         setFixedWidth : function(width){
13898             ml.setWidth(width);
13899         },
13900
13901         /**
13902          * Returns the measured width of the specified text
13903          * @param {String} text The text to measure
13904          * @return {Number} width The width in pixels
13905          */
13906         getWidth : function(text){
13907             ml.dom.style.width = 'auto';
13908             return this.getSize(text).width;
13909         },
13910
13911         /**
13912          * Returns the measured height of the specified text.  For multiline text, be sure to call
13913          * {@link #setFixedWidth} if necessary.
13914          * @param {String} text The text to measure
13915          * @return {Number} height The height in pixels
13916          */
13917         getHeight : function(text){
13918             return this.getSize(text).height;
13919         }
13920     };
13921
13922     instance.bind(bindTo);
13923
13924     return instance;
13925 };
13926
13927 // backwards compat
13928 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13929  * Based on:
13930  * Ext JS Library 1.1.1
13931  * Copyright(c) 2006-2007, Ext JS, LLC.
13932  *
13933  * Originally Released Under LGPL - original licence link has changed is not relivant.
13934  *
13935  * Fork - LGPL
13936  * <script type="text/javascript">
13937  */
13938
13939 /**
13940  * @class Roo.state.Provider
13941  * Abstract base class for state provider implementations. This class provides methods
13942  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13943  * Provider interface.
13944  */
13945 Roo.state.Provider = function(){
13946     /**
13947      * @event statechange
13948      * Fires when a state change occurs.
13949      * @param {Provider} this This state provider
13950      * @param {String} key The state key which was changed
13951      * @param {String} value The encoded value for the state
13952      */
13953     this.addEvents({
13954         "statechange": true
13955     });
13956     this.state = {};
13957     Roo.state.Provider.superclass.constructor.call(this);
13958 };
13959 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13960     /**
13961      * Returns the current value for a key
13962      * @param {String} name The key name
13963      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13964      * @return {Mixed} The state data
13965      */
13966     get : function(name, defaultValue){
13967         return typeof this.state[name] == "undefined" ?
13968             defaultValue : this.state[name];
13969     },
13970     
13971     /**
13972      * Clears a value from the state
13973      * @param {String} name The key name
13974      */
13975     clear : function(name){
13976         delete this.state[name];
13977         this.fireEvent("statechange", this, name, null);
13978     },
13979     
13980     /**
13981      * Sets the value for a key
13982      * @param {String} name The key name
13983      * @param {Mixed} value The value to set
13984      */
13985     set : function(name, value){
13986         this.state[name] = value;
13987         this.fireEvent("statechange", this, name, value);
13988     },
13989     
13990     /**
13991      * Decodes a string previously encoded with {@link #encodeValue}.
13992      * @param {String} value The value to decode
13993      * @return {Mixed} The decoded value
13994      */
13995     decodeValue : function(cookie){
13996         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13997         var matches = re.exec(unescape(cookie));
13998         if(!matches || !matches[1]) return; // non state cookie
13999         var type = matches[1];
14000         var v = matches[2];
14001         switch(type){
14002             case "n":
14003                 return parseFloat(v);
14004             case "d":
14005                 return new Date(Date.parse(v));
14006             case "b":
14007                 return (v == "1");
14008             case "a":
14009                 var all = [];
14010                 var values = v.split("^");
14011                 for(var i = 0, len = values.length; i < len; i++){
14012                     all.push(this.decodeValue(values[i]));
14013                 }
14014                 return all;
14015            case "o":
14016                 var all = {};
14017                 var values = v.split("^");
14018                 for(var i = 0, len = values.length; i < len; i++){
14019                     var kv = values[i].split("=");
14020                     all[kv[0]] = this.decodeValue(kv[1]);
14021                 }
14022                 return all;
14023            default:
14024                 return v;
14025         }
14026     },
14027     
14028     /**
14029      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14030      * @param {Mixed} value The value to encode
14031      * @return {String} The encoded value
14032      */
14033     encodeValue : function(v){
14034         var enc;
14035         if(typeof v == "number"){
14036             enc = "n:" + v;
14037         }else if(typeof v == "boolean"){
14038             enc = "b:" + (v ? "1" : "0");
14039         }else if(v instanceof Date){
14040             enc = "d:" + v.toGMTString();
14041         }else if(v instanceof Array){
14042             var flat = "";
14043             for(var i = 0, len = v.length; i < len; i++){
14044                 flat += this.encodeValue(v[i]);
14045                 if(i != len-1) flat += "^";
14046             }
14047             enc = "a:" + flat;
14048         }else if(typeof v == "object"){
14049             var flat = "";
14050             for(var key in v){
14051                 if(typeof v[key] != "function"){
14052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14053                 }
14054             }
14055             enc = "o:" + flat.substring(0, flat.length-1);
14056         }else{
14057             enc = "s:" + v;
14058         }
14059         return escape(enc);        
14060     }
14061 });
14062
14063 /*
14064  * Based on:
14065  * Ext JS Library 1.1.1
14066  * Copyright(c) 2006-2007, Ext JS, LLC.
14067  *
14068  * Originally Released Under LGPL - original licence link has changed is not relivant.
14069  *
14070  * Fork - LGPL
14071  * <script type="text/javascript">
14072  */
14073 /**
14074  * @class Roo.state.Manager
14075  * This is the global state manager. By default all components that are "state aware" check this class
14076  * for state information if you don't pass them a custom state provider. In order for this class
14077  * to be useful, it must be initialized with a provider when your application initializes.
14078  <pre><code>
14079 // in your initialization function
14080 init : function(){
14081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14082    ...
14083    // supposed you have a {@link Roo.BorderLayout}
14084    var layout = new Roo.BorderLayout(...);
14085    layout.restoreState();
14086    // or a {Roo.BasicDialog}
14087    var dialog = new Roo.BasicDialog(...);
14088    dialog.restoreState();
14089  </code></pre>
14090  * @singleton
14091  */
14092 Roo.state.Manager = function(){
14093     var provider = new Roo.state.Provider();
14094     
14095     return {
14096         /**
14097          * Configures the default state provider for your application
14098          * @param {Provider} stateProvider The state provider to set
14099          */
14100         setProvider : function(stateProvider){
14101             provider = stateProvider;
14102         },
14103         
14104         /**
14105          * Returns the current value for a key
14106          * @param {String} name The key name
14107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14108          * @return {Mixed} The state data
14109          */
14110         get : function(key, defaultValue){
14111             return provider.get(key, defaultValue);
14112         },
14113         
14114         /**
14115          * Sets the value for a key
14116          * @param {String} name The key name
14117          * @param {Mixed} value The state data
14118          */
14119          set : function(key, value){
14120             provider.set(key, value);
14121         },
14122         
14123         /**
14124          * Clears a value from the state
14125          * @param {String} name The key name
14126          */
14127         clear : function(key){
14128             provider.clear(key);
14129         },
14130         
14131         /**
14132          * Gets the currently configured state provider
14133          * @return {Provider} The state provider
14134          */
14135         getProvider : function(){
14136             return provider;
14137         }
14138     };
14139 }();
14140 /*
14141  * Based on:
14142  * Ext JS Library 1.1.1
14143  * Copyright(c) 2006-2007, Ext JS, LLC.
14144  *
14145  * Originally Released Under LGPL - original licence link has changed is not relivant.
14146  *
14147  * Fork - LGPL
14148  * <script type="text/javascript">
14149  */
14150 /**
14151  * @class Roo.state.CookieProvider
14152  * @extends Roo.state.Provider
14153  * The default Provider implementation which saves state via cookies.
14154  * <br />Usage:
14155  <pre><code>
14156    var cp = new Roo.state.CookieProvider({
14157        path: "/cgi-bin/",
14158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14159        domain: "roojs.com"
14160    })
14161    Roo.state.Manager.setProvider(cp);
14162  </code></pre>
14163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14168  * domain the page is running on including the 'www' like 'www.roojs.com')
14169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14170  * @constructor
14171  * Create a new CookieProvider
14172  * @param {Object} config The configuration object
14173  */
14174 Roo.state.CookieProvider = function(config){
14175     Roo.state.CookieProvider.superclass.constructor.call(this);
14176     this.path = "/";
14177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14178     this.domain = null;
14179     this.secure = false;
14180     Roo.apply(this, config);
14181     this.state = this.readCookies();
14182 };
14183
14184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14185     // private
14186     set : function(name, value){
14187         if(typeof value == "undefined" || value === null){
14188             this.clear(name);
14189             return;
14190         }
14191         this.setCookie(name, value);
14192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14193     },
14194
14195     // private
14196     clear : function(name){
14197         this.clearCookie(name);
14198         Roo.state.CookieProvider.superclass.clear.call(this, name);
14199     },
14200
14201     // private
14202     readCookies : function(){
14203         var cookies = {};
14204         var c = document.cookie + ";";
14205         var re = /\s?(.*?)=(.*?);/g;
14206         var matches;
14207         while((matches = re.exec(c)) != null){
14208             var name = matches[1];
14209             var value = matches[2];
14210             if(name && name.substring(0,3) == "ys-"){
14211                 cookies[name.substr(3)] = this.decodeValue(value);
14212             }
14213         }
14214         return cookies;
14215     },
14216
14217     // private
14218     setCookie : function(name, value){
14219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14221            ((this.path == null) ? "" : ("; path=" + this.path)) +
14222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14223            ((this.secure == true) ? "; secure" : "");
14224     },
14225
14226     // private
14227     clearCookie : function(name){
14228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14229            ((this.path == null) ? "" : ("; path=" + this.path)) +
14230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14231            ((this.secure == true) ? "; secure" : "");
14232     }
14233 });/*
14234  * Based on:
14235  * Ext JS Library 1.1.1
14236  * Copyright(c) 2006-2007, Ext JS, LLC.
14237  *
14238  * Originally Released Under LGPL - original licence link has changed is not relivant.
14239  *
14240  * Fork - LGPL
14241  * <script type="text/javascript">
14242  */
14243
14244
14245
14246 /*
14247  * These classes are derivatives of the similarly named classes in the YUI Library.
14248  * The original license:
14249  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14250  * Code licensed under the BSD License:
14251  * http://developer.yahoo.net/yui/license.txt
14252  */
14253
14254 (function() {
14255
14256 var Event=Roo.EventManager;
14257 var Dom=Roo.lib.Dom;
14258
14259 /**
14260  * @class Roo.dd.DragDrop
14261  * Defines the interface and base operation of items that that can be
14262  * dragged or can be drop targets.  It was designed to be extended, overriding
14263  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14264  * Up to three html elements can be associated with a DragDrop instance:
14265  * <ul>
14266  * <li>linked element: the element that is passed into the constructor.
14267  * This is the element which defines the boundaries for interaction with
14268  * other DragDrop objects.</li>
14269  * <li>handle element(s): The drag operation only occurs if the element that
14270  * was clicked matches a handle element.  By default this is the linked
14271  * element, but there are times that you will want only a portion of the
14272  * linked element to initiate the drag operation, and the setHandleElId()
14273  * method provides a way to define this.</li>
14274  * <li>drag element: this represents the element that would be moved along
14275  * with the cursor during a drag operation.  By default, this is the linked
14276  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14277  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14278  * </li>
14279  * </ul>
14280  * This class should not be instantiated until the onload event to ensure that
14281  * the associated elements are available.
14282  * The following would define a DragDrop obj that would interact with any
14283  * other DragDrop obj in the "group1" group:
14284  * <pre>
14285  *  dd = new Roo.dd.DragDrop("div1", "group1");
14286  * </pre>
14287  * Since none of the event handlers have been implemented, nothing would
14288  * actually happen if you were to run the code above.  Normally you would
14289  * override this class or one of the default implementations, but you can
14290  * also override the methods you want on an instance of the class...
14291  * <pre>
14292  *  dd.onDragDrop = function(e, id) {
14293  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14294  *  }
14295  * </pre>
14296  * @constructor
14297  * @param {String} id of the element that is linked to this instance
14298  * @param {String} sGroup the group of related DragDrop objects
14299  * @param {object} config an object containing configurable attributes
14300  *                Valid properties for DragDrop:
14301  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14302  */
14303 Roo.dd.DragDrop = function(id, sGroup, config) {
14304     if (id) {
14305         this.init(id, sGroup, config);
14306     }
14307 };
14308
14309 Roo.dd.DragDrop.prototype = {
14310
14311     /**
14312      * The id of the element associated with this object.  This is what we
14313      * refer to as the "linked element" because the size and position of
14314      * this element is used to determine when the drag and drop objects have
14315      * interacted.
14316      * @property id
14317      * @type String
14318      */
14319     id: null,
14320
14321     /**
14322      * Configuration attributes passed into the constructor
14323      * @property config
14324      * @type object
14325      */
14326     config: null,
14327
14328     /**
14329      * The id of the element that will be dragged.  By default this is same
14330      * as the linked element , but could be changed to another element. Ex:
14331      * Roo.dd.DDProxy
14332      * @property dragElId
14333      * @type String
14334      * @private
14335      */
14336     dragElId: null,
14337
14338     /**
14339      * the id of the element that initiates the drag operation.  By default
14340      * this is the linked element, but could be changed to be a child of this
14341      * element.  This lets us do things like only starting the drag when the
14342      * header element within the linked html element is clicked.
14343      * @property handleElId
14344      * @type String
14345      * @private
14346      */
14347     handleElId: null,
14348
14349     /**
14350      * An associative array of HTML tags that will be ignored if clicked.
14351      * @property invalidHandleTypes
14352      * @type {string: string}
14353      */
14354     invalidHandleTypes: null,
14355
14356     /**
14357      * An associative array of ids for elements that will be ignored if clicked
14358      * @property invalidHandleIds
14359      * @type {string: string}
14360      */
14361     invalidHandleIds: null,
14362
14363     /**
14364      * An indexted array of css class names for elements that will be ignored
14365      * if clicked.
14366      * @property invalidHandleClasses
14367      * @type string[]
14368      */
14369     invalidHandleClasses: null,
14370
14371     /**
14372      * The linked element's absolute X position at the time the drag was
14373      * started
14374      * @property startPageX
14375      * @type int
14376      * @private
14377      */
14378     startPageX: 0,
14379
14380     /**
14381      * The linked element's absolute X position at the time the drag was
14382      * started
14383      * @property startPageY
14384      * @type int
14385      * @private
14386      */
14387     startPageY: 0,
14388
14389     /**
14390      * The group defines a logical collection of DragDrop objects that are
14391      * related.  Instances only get events when interacting with other
14392      * DragDrop object in the same group.  This lets us define multiple
14393      * groups using a single DragDrop subclass if we want.
14394      * @property groups
14395      * @type {string: string}
14396      */
14397     groups: null,
14398
14399     /**
14400      * Individual drag/drop instances can be locked.  This will prevent
14401      * onmousedown start drag.
14402      * @property locked
14403      * @type boolean
14404      * @private
14405      */
14406     locked: false,
14407
14408     /**
14409      * Lock this instance
14410      * @method lock
14411      */
14412     lock: function() { this.locked = true; },
14413
14414     /**
14415      * Unlock this instace
14416      * @method unlock
14417      */
14418     unlock: function() { this.locked = false; },
14419
14420     /**
14421      * By default, all insances can be a drop target.  This can be disabled by
14422      * setting isTarget to false.
14423      * @method isTarget
14424      * @type boolean
14425      */
14426     isTarget: true,
14427
14428     /**
14429      * The padding configured for this drag and drop object for calculating
14430      * the drop zone intersection with this object.
14431      * @method padding
14432      * @type int[]
14433      */
14434     padding: null,
14435
14436     /**
14437      * Cached reference to the linked element
14438      * @property _domRef
14439      * @private
14440      */
14441     _domRef: null,
14442
14443     /**
14444      * Internal typeof flag
14445      * @property __ygDragDrop
14446      * @private
14447      */
14448     __ygDragDrop: true,
14449
14450     /**
14451      * Set to true when horizontal contraints are applied
14452      * @property constrainX
14453      * @type boolean
14454      * @private
14455      */
14456     constrainX: false,
14457
14458     /**
14459      * Set to true when vertical contraints are applied
14460      * @property constrainY
14461      * @type boolean
14462      * @private
14463      */
14464     constrainY: false,
14465
14466     /**
14467      * The left constraint
14468      * @property minX
14469      * @type int
14470      * @private
14471      */
14472     minX: 0,
14473
14474     /**
14475      * The right constraint
14476      * @property maxX
14477      * @type int
14478      * @private
14479      */
14480     maxX: 0,
14481
14482     /**
14483      * The up constraint
14484      * @property minY
14485      * @type int
14486      * @type int
14487      * @private
14488      */
14489     minY: 0,
14490
14491     /**
14492      * The down constraint
14493      * @property maxY
14494      * @type int
14495      * @private
14496      */
14497     maxY: 0,
14498
14499     /**
14500      * Maintain offsets when we resetconstraints.  Set to true when you want
14501      * the position of the element relative to its parent to stay the same
14502      * when the page changes
14503      *
14504      * @property maintainOffset
14505      * @type boolean
14506      */
14507     maintainOffset: false,
14508
14509     /**
14510      * Array of pixel locations the element will snap to if we specified a
14511      * horizontal graduation/interval.  This array is generated automatically
14512      * when you define a tick interval.
14513      * @property xTicks
14514      * @type int[]
14515      */
14516     xTicks: null,
14517
14518     /**
14519      * Array of pixel locations the element will snap to if we specified a
14520      * vertical graduation/interval.  This array is generated automatically
14521      * when you define a tick interval.
14522      * @property yTicks
14523      * @type int[]
14524      */
14525     yTicks: null,
14526
14527     /**
14528      * By default the drag and drop instance will only respond to the primary
14529      * button click (left button for a right-handed mouse).  Set to true to
14530      * allow drag and drop to start with any mouse click that is propogated
14531      * by the browser
14532      * @property primaryButtonOnly
14533      * @type boolean
14534      */
14535     primaryButtonOnly: true,
14536
14537     /**
14538      * The availabe property is false until the linked dom element is accessible.
14539      * @property available
14540      * @type boolean
14541      */
14542     available: false,
14543
14544     /**
14545      * By default, drags can only be initiated if the mousedown occurs in the
14546      * region the linked element is.  This is done in part to work around a
14547      * bug in some browsers that mis-report the mousedown if the previous
14548      * mouseup happened outside of the window.  This property is set to true
14549      * if outer handles are defined.
14550      *
14551      * @property hasOuterHandles
14552      * @type boolean
14553      * @default false
14554      */
14555     hasOuterHandles: false,
14556
14557     /**
14558      * Code that executes immediately before the startDrag event
14559      * @method b4StartDrag
14560      * @private
14561      */
14562     b4StartDrag: function(x, y) { },
14563
14564     /**
14565      * Abstract method called after a drag/drop object is clicked
14566      * and the drag or mousedown time thresholds have beeen met.
14567      * @method startDrag
14568      * @param {int} X click location
14569      * @param {int} Y click location
14570      */
14571     startDrag: function(x, y) { /* override this */ },
14572
14573     /**
14574      * Code that executes immediately before the onDrag event
14575      * @method b4Drag
14576      * @private
14577      */
14578     b4Drag: function(e) { },
14579
14580     /**
14581      * Abstract method called during the onMouseMove event while dragging an
14582      * object.
14583      * @method onDrag
14584      * @param {Event} e the mousemove event
14585      */
14586     onDrag: function(e) { /* override this */ },
14587
14588     /**
14589      * Abstract method called when this element fist begins hovering over
14590      * another DragDrop obj
14591      * @method onDragEnter
14592      * @param {Event} e the mousemove event
14593      * @param {String|DragDrop[]} id In POINT mode, the element
14594      * id this is hovering over.  In INTERSECT mode, an array of one or more
14595      * dragdrop items being hovered over.
14596      */
14597     onDragEnter: function(e, id) { /* override this */ },
14598
14599     /**
14600      * Code that executes immediately before the onDragOver event
14601      * @method b4DragOver
14602      * @private
14603      */
14604     b4DragOver: function(e) { },
14605
14606     /**
14607      * Abstract method called when this element is hovering over another
14608      * DragDrop obj
14609      * @method onDragOver
14610      * @param {Event} e the mousemove event
14611      * @param {String|DragDrop[]} id In POINT mode, the element
14612      * id this is hovering over.  In INTERSECT mode, an array of dd items
14613      * being hovered over.
14614      */
14615     onDragOver: function(e, id) { /* override this */ },
14616
14617     /**
14618      * Code that executes immediately before the onDragOut event
14619      * @method b4DragOut
14620      * @private
14621      */
14622     b4DragOut: function(e) { },
14623
14624     /**
14625      * Abstract method called when we are no longer hovering over an element
14626      * @method onDragOut
14627      * @param {Event} e the mousemove event
14628      * @param {String|DragDrop[]} id In POINT mode, the element
14629      * id this was hovering over.  In INTERSECT mode, an array of dd items
14630      * that the mouse is no longer over.
14631      */
14632     onDragOut: function(e, id) { /* override this */ },
14633
14634     /**
14635      * Code that executes immediately before the onDragDrop event
14636      * @method b4DragDrop
14637      * @private
14638      */
14639     b4DragDrop: function(e) { },
14640
14641     /**
14642      * Abstract method called when this item is dropped on another DragDrop
14643      * obj
14644      * @method onDragDrop
14645      * @param {Event} e the mouseup event
14646      * @param {String|DragDrop[]} id In POINT mode, the element
14647      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14648      * was dropped on.
14649      */
14650     onDragDrop: function(e, id) { /* override this */ },
14651
14652     /**
14653      * Abstract method called when this item is dropped on an area with no
14654      * drop target
14655      * @method onInvalidDrop
14656      * @param {Event} e the mouseup event
14657      */
14658     onInvalidDrop: function(e) { /* override this */ },
14659
14660     /**
14661      * Code that executes immediately before the endDrag event
14662      * @method b4EndDrag
14663      * @private
14664      */
14665     b4EndDrag: function(e) { },
14666
14667     /**
14668      * Fired when we are done dragging the object
14669      * @method endDrag
14670      * @param {Event} e the mouseup event
14671      */
14672     endDrag: function(e) { /* override this */ },
14673
14674     /**
14675      * Code executed immediately before the onMouseDown event
14676      * @method b4MouseDown
14677      * @param {Event} e the mousedown event
14678      * @private
14679      */
14680     b4MouseDown: function(e) {  },
14681
14682     /**
14683      * Event handler that fires when a drag/drop obj gets a mousedown
14684      * @method onMouseDown
14685      * @param {Event} e the mousedown event
14686      */
14687     onMouseDown: function(e) { /* override this */ },
14688
14689     /**
14690      * Event handler that fires when a drag/drop obj gets a mouseup
14691      * @method onMouseUp
14692      * @param {Event} e the mouseup event
14693      */
14694     onMouseUp: function(e) { /* override this */ },
14695
14696     /**
14697      * Override the onAvailable method to do what is needed after the initial
14698      * position was determined.
14699      * @method onAvailable
14700      */
14701     onAvailable: function () {
14702     },
14703
14704     /*
14705      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14706      * @type Object
14707      */
14708     defaultPadding : {left:0, right:0, top:0, bottom:0},
14709
14710     /*
14711      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14712  *
14713  * Usage:
14714  <pre><code>
14715  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14716                 { dragElId: "existingProxyDiv" });
14717  dd.startDrag = function(){
14718      this.constrainTo("parent-id");
14719  };
14720  </code></pre>
14721  * Or you can initalize it using the {@link Roo.Element} object:
14722  <pre><code>
14723  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14724      startDrag : function(){
14725          this.constrainTo("parent-id");
14726      }
14727  });
14728  </code></pre>
14729      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14730      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14731      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14732      * an object containing the sides to pad. For example: {right:10, bottom:10}
14733      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14734      */
14735     constrainTo : function(constrainTo, pad, inContent){
14736         if(typeof pad == "number"){
14737             pad = {left: pad, right:pad, top:pad, bottom:pad};
14738         }
14739         pad = pad || this.defaultPadding;
14740         var b = Roo.get(this.getEl()).getBox();
14741         var ce = Roo.get(constrainTo);
14742         var s = ce.getScroll();
14743         var c, cd = ce.dom;
14744         if(cd == document.body){
14745             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14746         }else{
14747             xy = ce.getXY();
14748             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14749         }
14750
14751
14752         var topSpace = b.y - c.y;
14753         var leftSpace = b.x - c.x;
14754
14755         this.resetConstraints();
14756         this.setXConstraint(leftSpace - (pad.left||0), // left
14757                 c.width - leftSpace - b.width - (pad.right||0) //right
14758         );
14759         this.setYConstraint(topSpace - (pad.top||0), //top
14760                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14761         );
14762     },
14763
14764     /**
14765      * Returns a reference to the linked element
14766      * @method getEl
14767      * @return {HTMLElement} the html element
14768      */
14769     getEl: function() {
14770         if (!this._domRef) {
14771             this._domRef = Roo.getDom(this.id);
14772         }
14773
14774         return this._domRef;
14775     },
14776
14777     /**
14778      * Returns a reference to the actual element to drag.  By default this is
14779      * the same as the html element, but it can be assigned to another
14780      * element. An example of this can be found in Roo.dd.DDProxy
14781      * @method getDragEl
14782      * @return {HTMLElement} the html element
14783      */
14784     getDragEl: function() {
14785         return Roo.getDom(this.dragElId);
14786     },
14787
14788     /**
14789      * Sets up the DragDrop object.  Must be called in the constructor of any
14790      * Roo.dd.DragDrop subclass
14791      * @method init
14792      * @param id the id of the linked element
14793      * @param {String} sGroup the group of related items
14794      * @param {object} config configuration attributes
14795      */
14796     init: function(id, sGroup, config) {
14797         this.initTarget(id, sGroup, config);
14798         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14799         // Event.on(this.id, "selectstart", Event.preventDefault);
14800     },
14801
14802     /**
14803      * Initializes Targeting functionality only... the object does not
14804      * get a mousedown handler.
14805      * @method initTarget
14806      * @param id the id of the linked element
14807      * @param {String} sGroup the group of related items
14808      * @param {object} config configuration attributes
14809      */
14810     initTarget: function(id, sGroup, config) {
14811
14812         // configuration attributes
14813         this.config = config || {};
14814
14815         // create a local reference to the drag and drop manager
14816         this.DDM = Roo.dd.DDM;
14817         // initialize the groups array
14818         this.groups = {};
14819
14820         // assume that we have an element reference instead of an id if the
14821         // parameter is not a string
14822         if (typeof id !== "string") {
14823             id = Roo.id(id);
14824         }
14825
14826         // set the id
14827         this.id = id;
14828
14829         // add to an interaction group
14830         this.addToGroup((sGroup) ? sGroup : "default");
14831
14832         // We don't want to register this as the handle with the manager
14833         // so we just set the id rather than calling the setter.
14834         this.handleElId = id;
14835
14836         // the linked element is the element that gets dragged by default
14837         this.setDragElId(id);
14838
14839         // by default, clicked anchors will not start drag operations.
14840         this.invalidHandleTypes = { A: "A" };
14841         this.invalidHandleIds = {};
14842         this.invalidHandleClasses = [];
14843
14844         this.applyConfig();
14845
14846         this.handleOnAvailable();
14847     },
14848
14849     /**
14850      * Applies the configuration parameters that were passed into the constructor.
14851      * This is supposed to happen at each level through the inheritance chain.  So
14852      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14853      * DragDrop in order to get all of the parameters that are available in
14854      * each object.
14855      * @method applyConfig
14856      */
14857     applyConfig: function() {
14858
14859         // configurable properties:
14860         //    padding, isTarget, maintainOffset, primaryButtonOnly
14861         this.padding           = this.config.padding || [0, 0, 0, 0];
14862         this.isTarget          = (this.config.isTarget !== false);
14863         this.maintainOffset    = (this.config.maintainOffset);
14864         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14865
14866     },
14867
14868     /**
14869      * Executed when the linked element is available
14870      * @method handleOnAvailable
14871      * @private
14872      */
14873     handleOnAvailable: function() {
14874         this.available = true;
14875         this.resetConstraints();
14876         this.onAvailable();
14877     },
14878
14879      /**
14880      * Configures the padding for the target zone in px.  Effectively expands
14881      * (or reduces) the virtual object size for targeting calculations.
14882      * Supports css-style shorthand; if only one parameter is passed, all sides
14883      * will have that padding, and if only two are passed, the top and bottom
14884      * will have the first param, the left and right the second.
14885      * @method setPadding
14886      * @param {int} iTop    Top pad
14887      * @param {int} iRight  Right pad
14888      * @param {int} iBot    Bot pad
14889      * @param {int} iLeft   Left pad
14890      */
14891     setPadding: function(iTop, iRight, iBot, iLeft) {
14892         // this.padding = [iLeft, iRight, iTop, iBot];
14893         if (!iRight && 0 !== iRight) {
14894             this.padding = [iTop, iTop, iTop, iTop];
14895         } else if (!iBot && 0 !== iBot) {
14896             this.padding = [iTop, iRight, iTop, iRight];
14897         } else {
14898             this.padding = [iTop, iRight, iBot, iLeft];
14899         }
14900     },
14901
14902     /**
14903      * Stores the initial placement of the linked element.
14904      * @method setInitialPosition
14905      * @param {int} diffX   the X offset, default 0
14906      * @param {int} diffY   the Y offset, default 0
14907      */
14908     setInitPosition: function(diffX, diffY) {
14909         var el = this.getEl();
14910
14911         if (!this.DDM.verifyEl(el)) {
14912             return;
14913         }
14914
14915         var dx = diffX || 0;
14916         var dy = diffY || 0;
14917
14918         var p = Dom.getXY( el );
14919
14920         this.initPageX = p[0] - dx;
14921         this.initPageY = p[1] - dy;
14922
14923         this.lastPageX = p[0];
14924         this.lastPageY = p[1];
14925
14926
14927         this.setStartPosition(p);
14928     },
14929
14930     /**
14931      * Sets the start position of the element.  This is set when the obj
14932      * is initialized, the reset when a drag is started.
14933      * @method setStartPosition
14934      * @param pos current position (from previous lookup)
14935      * @private
14936      */
14937     setStartPosition: function(pos) {
14938         var p = pos || Dom.getXY( this.getEl() );
14939         this.deltaSetXY = null;
14940
14941         this.startPageX = p[0];
14942         this.startPageY = p[1];
14943     },
14944
14945     /**
14946      * Add this instance to a group of related drag/drop objects.  All
14947      * instances belong to at least one group, and can belong to as many
14948      * groups as needed.
14949      * @method addToGroup
14950      * @param sGroup {string} the name of the group
14951      */
14952     addToGroup: function(sGroup) {
14953         this.groups[sGroup] = true;
14954         this.DDM.regDragDrop(this, sGroup);
14955     },
14956
14957     /**
14958      * Remove's this instance from the supplied interaction group
14959      * @method removeFromGroup
14960      * @param {string}  sGroup  The group to drop
14961      */
14962     removeFromGroup: function(sGroup) {
14963         if (this.groups[sGroup]) {
14964             delete this.groups[sGroup];
14965         }
14966
14967         this.DDM.removeDDFromGroup(this, sGroup);
14968     },
14969
14970     /**
14971      * Allows you to specify that an element other than the linked element
14972      * will be moved with the cursor during a drag
14973      * @method setDragElId
14974      * @param id {string} the id of the element that will be used to initiate the drag
14975      */
14976     setDragElId: function(id) {
14977         this.dragElId = id;
14978     },
14979
14980     /**
14981      * Allows you to specify a child of the linked element that should be
14982      * used to initiate the drag operation.  An example of this would be if
14983      * you have a content div with text and links.  Clicking anywhere in the
14984      * content area would normally start the drag operation.  Use this method
14985      * to specify that an element inside of the content div is the element
14986      * that starts the drag operation.
14987      * @method setHandleElId
14988      * @param id {string} the id of the element that will be used to
14989      * initiate the drag.
14990      */
14991     setHandleElId: function(id) {
14992         if (typeof id !== "string") {
14993             id = Roo.id(id);
14994         }
14995         this.handleElId = id;
14996         this.DDM.regHandle(this.id, id);
14997     },
14998
14999     /**
15000      * Allows you to set an element outside of the linked element as a drag
15001      * handle
15002      * @method setOuterHandleElId
15003      * @param id the id of the element that will be used to initiate the drag
15004      */
15005     setOuterHandleElId: function(id) {
15006         if (typeof id !== "string") {
15007             id = Roo.id(id);
15008         }
15009         Event.on(id, "mousedown",
15010                 this.handleMouseDown, this);
15011         this.setHandleElId(id);
15012
15013         this.hasOuterHandles = true;
15014     },
15015
15016     /**
15017      * Remove all drag and drop hooks for this element
15018      * @method unreg
15019      */
15020     unreg: function() {
15021         Event.un(this.id, "mousedown",
15022                 this.handleMouseDown);
15023         this._domRef = null;
15024         this.DDM._remove(this);
15025     },
15026
15027     destroy : function(){
15028         this.unreg();
15029     },
15030
15031     /**
15032      * Returns true if this instance is locked, or the drag drop mgr is locked
15033      * (meaning that all drag/drop is disabled on the page.)
15034      * @method isLocked
15035      * @return {boolean} true if this obj or all drag/drop is locked, else
15036      * false
15037      */
15038     isLocked: function() {
15039         return (this.DDM.isLocked() || this.locked);
15040     },
15041
15042     /**
15043      * Fired when this object is clicked
15044      * @method handleMouseDown
15045      * @param {Event} e
15046      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15047      * @private
15048      */
15049     handleMouseDown: function(e, oDD){
15050         if (this.primaryButtonOnly && e.button != 0) {
15051             return;
15052         }
15053
15054         if (this.isLocked()) {
15055             return;
15056         }
15057
15058         this.DDM.refreshCache(this.groups);
15059
15060         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15061         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15062         } else {
15063             if (this.clickValidator(e)) {
15064
15065                 // set the initial element position
15066                 this.setStartPosition();
15067
15068
15069                 this.b4MouseDown(e);
15070                 this.onMouseDown(e);
15071
15072                 this.DDM.handleMouseDown(e, this);
15073
15074                 this.DDM.stopEvent(e);
15075             } else {
15076
15077
15078             }
15079         }
15080     },
15081
15082     clickValidator: function(e) {
15083         var target = e.getTarget();
15084         return ( this.isValidHandleChild(target) &&
15085                     (this.id == this.handleElId ||
15086                         this.DDM.handleWasClicked(target, this.id)) );
15087     },
15088
15089     /**
15090      * Allows you to specify a tag name that should not start a drag operation
15091      * when clicked.  This is designed to facilitate embedding links within a
15092      * drag handle that do something other than start the drag.
15093      * @method addInvalidHandleType
15094      * @param {string} tagName the type of element to exclude
15095      */
15096     addInvalidHandleType: function(tagName) {
15097         var type = tagName.toUpperCase();
15098         this.invalidHandleTypes[type] = type;
15099     },
15100
15101     /**
15102      * Lets you to specify an element id for a child of a drag handle
15103      * that should not initiate a drag
15104      * @method addInvalidHandleId
15105      * @param {string} id the element id of the element you wish to ignore
15106      */
15107     addInvalidHandleId: function(id) {
15108         if (typeof id !== "string") {
15109             id = Roo.id(id);
15110         }
15111         this.invalidHandleIds[id] = id;
15112     },
15113
15114     /**
15115      * Lets you specify a css class of elements that will not initiate a drag
15116      * @method addInvalidHandleClass
15117      * @param {string} cssClass the class of the elements you wish to ignore
15118      */
15119     addInvalidHandleClass: function(cssClass) {
15120         this.invalidHandleClasses.push(cssClass);
15121     },
15122
15123     /**
15124      * Unsets an excluded tag name set by addInvalidHandleType
15125      * @method removeInvalidHandleType
15126      * @param {string} tagName the type of element to unexclude
15127      */
15128     removeInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         // this.invalidHandleTypes[type] = null;
15131         delete this.invalidHandleTypes[type];
15132     },
15133
15134     /**
15135      * Unsets an invalid handle id
15136      * @method removeInvalidHandleId
15137      * @param {string} id the id of the element to re-enable
15138      */
15139     removeInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         delete this.invalidHandleIds[id];
15144     },
15145
15146     /**
15147      * Unsets an invalid css class
15148      * @method removeInvalidHandleClass
15149      * @param {string} cssClass the class of the element(s) you wish to
15150      * re-enable
15151      */
15152     removeInvalidHandleClass: function(cssClass) {
15153         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15154             if (this.invalidHandleClasses[i] == cssClass) {
15155                 delete this.invalidHandleClasses[i];
15156             }
15157         }
15158     },
15159
15160     /**
15161      * Checks the tag exclusion list to see if this click should be ignored
15162      * @method isValidHandleChild
15163      * @param {HTMLElement} node the HTMLElement to evaluate
15164      * @return {boolean} true if this is a valid tag type, false if not
15165      */
15166     isValidHandleChild: function(node) {
15167
15168         var valid = true;
15169         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15170         var nodeName;
15171         try {
15172             nodeName = node.nodeName.toUpperCase();
15173         } catch(e) {
15174             nodeName = node.nodeName;
15175         }
15176         valid = valid && !this.invalidHandleTypes[nodeName];
15177         valid = valid && !this.invalidHandleIds[node.id];
15178
15179         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15180             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15181         }
15182
15183
15184         return valid;
15185
15186     },
15187
15188     /**
15189      * Create the array of horizontal tick marks if an interval was specified
15190      * in setXConstraint().
15191      * @method setXTicks
15192      * @private
15193      */
15194     setXTicks: function(iStartX, iTickSize) {
15195         this.xTicks = [];
15196         this.xTickSize = iTickSize;
15197
15198         var tickMap = {};
15199
15200         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15201             if (!tickMap[i]) {
15202                 this.xTicks[this.xTicks.length] = i;
15203                 tickMap[i] = true;
15204             }
15205         }
15206
15207         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15208             if (!tickMap[i]) {
15209                 this.xTicks[this.xTicks.length] = i;
15210                 tickMap[i] = true;
15211             }
15212         }
15213
15214         this.xTicks.sort(this.DDM.numericSort) ;
15215     },
15216
15217     /**
15218      * Create the array of vertical tick marks if an interval was specified in
15219      * setYConstraint().
15220      * @method setYTicks
15221      * @private
15222      */
15223     setYTicks: function(iStartY, iTickSize) {
15224         this.yTicks = [];
15225         this.yTickSize = iTickSize;
15226
15227         var tickMap = {};
15228
15229         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15230             if (!tickMap[i]) {
15231                 this.yTicks[this.yTicks.length] = i;
15232                 tickMap[i] = true;
15233             }
15234         }
15235
15236         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.yTicks[this.yTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         this.yTicks.sort(this.DDM.numericSort) ;
15244     },
15245
15246     /**
15247      * By default, the element can be dragged any place on the screen.  Use
15248      * this method to limit the horizontal travel of the element.  Pass in
15249      * 0,0 for the parameters if you want to lock the drag to the y axis.
15250      * @method setXConstraint
15251      * @param {int} iLeft the number of pixels the element can move to the left
15252      * @param {int} iRight the number of pixels the element can move to the
15253      * right
15254      * @param {int} iTickSize optional parameter for specifying that the
15255      * element
15256      * should move iTickSize pixels at a time.
15257      */
15258     setXConstraint: function(iLeft, iRight, iTickSize) {
15259         this.leftConstraint = iLeft;
15260         this.rightConstraint = iRight;
15261
15262         this.minX = this.initPageX - iLeft;
15263         this.maxX = this.initPageX + iRight;
15264         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15265
15266         this.constrainX = true;
15267     },
15268
15269     /**
15270      * Clears any constraints applied to this instance.  Also clears ticks
15271      * since they can't exist independent of a constraint at this time.
15272      * @method clearConstraints
15273      */
15274     clearConstraints: function() {
15275         this.constrainX = false;
15276         this.constrainY = false;
15277         this.clearTicks();
15278     },
15279
15280     /**
15281      * Clears any tick interval defined for this instance
15282      * @method clearTicks
15283      */
15284     clearTicks: function() {
15285         this.xTicks = null;
15286         this.yTicks = null;
15287         this.xTickSize = 0;
15288         this.yTickSize = 0;
15289     },
15290
15291     /**
15292      * By default, the element can be dragged any place on the screen.  Set
15293      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15294      * parameters if you want to lock the drag to the x axis.
15295      * @method setYConstraint
15296      * @param {int} iUp the number of pixels the element can move up
15297      * @param {int} iDown the number of pixels the element can move down
15298      * @param {int} iTickSize optional parameter for specifying that the
15299      * element should move iTickSize pixels at a time.
15300      */
15301     setYConstraint: function(iUp, iDown, iTickSize) {
15302         this.topConstraint = iUp;
15303         this.bottomConstraint = iDown;
15304
15305         this.minY = this.initPageY - iUp;
15306         this.maxY = this.initPageY + iDown;
15307         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15308
15309         this.constrainY = true;
15310
15311     },
15312
15313     /**
15314      * resetConstraints must be called if you manually reposition a dd element.
15315      * @method resetConstraints
15316      * @param {boolean} maintainOffset
15317      */
15318     resetConstraints: function() {
15319
15320
15321         // Maintain offsets if necessary
15322         if (this.initPageX || this.initPageX === 0) {
15323             // figure out how much this thing has moved
15324             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15325             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15326
15327             this.setInitPosition(dx, dy);
15328
15329         // This is the first time we have detected the element's position
15330         } else {
15331             this.setInitPosition();
15332         }
15333
15334         if (this.constrainX) {
15335             this.setXConstraint( this.leftConstraint,
15336                                  this.rightConstraint,
15337                                  this.xTickSize        );
15338         }
15339
15340         if (this.constrainY) {
15341             this.setYConstraint( this.topConstraint,
15342                                  this.bottomConstraint,
15343                                  this.yTickSize         );
15344         }
15345     },
15346
15347     /**
15348      * Normally the drag element is moved pixel by pixel, but we can specify
15349      * that it move a number of pixels at a time.  This method resolves the
15350      * location when we have it set up like this.
15351      * @method getTick
15352      * @param {int} val where we want to place the object
15353      * @param {int[]} tickArray sorted array of valid points
15354      * @return {int} the closest tick
15355      * @private
15356      */
15357     getTick: function(val, tickArray) {
15358
15359         if (!tickArray) {
15360             // If tick interval is not defined, it is effectively 1 pixel,
15361             // so we return the value passed to us.
15362             return val;
15363         } else if (tickArray[0] >= val) {
15364             // The value is lower than the first tick, so we return the first
15365             // tick.
15366             return tickArray[0];
15367         } else {
15368             for (var i=0, len=tickArray.length; i<len; ++i) {
15369                 var next = i + 1;
15370                 if (tickArray[next] && tickArray[next] >= val) {
15371                     var diff1 = val - tickArray[i];
15372                     var diff2 = tickArray[next] - val;
15373                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15374                 }
15375             }
15376
15377             // The value is larger than the last tick, so we return the last
15378             // tick.
15379             return tickArray[tickArray.length - 1];
15380         }
15381     },
15382
15383     /**
15384      * toString method
15385      * @method toString
15386      * @return {string} string representation of the dd obj
15387      */
15388     toString: function() {
15389         return ("DragDrop " + this.id);
15390     }
15391
15392 };
15393
15394 })();
15395 /*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406
15407 /**
15408  * The drag and drop utility provides a framework for building drag and drop
15409  * applications.  In addition to enabling drag and drop for specific elements,
15410  * the drag and drop elements are tracked by the manager class, and the
15411  * interactions between the various elements are tracked during the drag and
15412  * the implementing code is notified about these important moments.
15413  */
15414
15415 // Only load the library once.  Rewriting the manager class would orphan
15416 // existing drag and drop instances.
15417 if (!Roo.dd.DragDropMgr) {
15418
15419 /**
15420  * @class Roo.dd.DragDropMgr
15421  * DragDropMgr is a singleton that tracks the element interaction for
15422  * all DragDrop items in the window.  Generally, you will not call
15423  * this class directly, but it does have helper methods that could
15424  * be useful in your DragDrop implementations.
15425  * @singleton
15426  */
15427 Roo.dd.DragDropMgr = function() {
15428
15429     var Event = Roo.EventManager;
15430
15431     return {
15432
15433         /**
15434          * Two dimensional Array of registered DragDrop objects.  The first
15435          * dimension is the DragDrop item group, the second the DragDrop
15436          * object.
15437          * @property ids
15438          * @type {string: string}
15439          * @private
15440          * @static
15441          */
15442         ids: {},
15443
15444         /**
15445          * Array of element ids defined as drag handles.  Used to determine
15446          * if the element that generated the mousedown event is actually the
15447          * handle and not the html element itself.
15448          * @property handleIds
15449          * @type {string: string}
15450          * @private
15451          * @static
15452          */
15453         handleIds: {},
15454
15455         /**
15456          * the DragDrop object that is currently being dragged
15457          * @property dragCurrent
15458          * @type DragDrop
15459          * @private
15460          * @static
15461          **/
15462         dragCurrent: null,
15463
15464         /**
15465          * the DragDrop object(s) that are being hovered over
15466          * @property dragOvers
15467          * @type Array
15468          * @private
15469          * @static
15470          */
15471         dragOvers: {},
15472
15473         /**
15474          * the X distance between the cursor and the object being dragged
15475          * @property deltaX
15476          * @type int
15477          * @private
15478          * @static
15479          */
15480         deltaX: 0,
15481
15482         /**
15483          * the Y distance between the cursor and the object being dragged
15484          * @property deltaY
15485          * @type int
15486          * @private
15487          * @static
15488          */
15489         deltaY: 0,
15490
15491         /**
15492          * Flag to determine if we should prevent the default behavior of the
15493          * events we define. By default this is true, but this can be set to
15494          * false if you need the default behavior (not recommended)
15495          * @property preventDefault
15496          * @type boolean
15497          * @static
15498          */
15499         preventDefault: true,
15500
15501         /**
15502          * Flag to determine if we should stop the propagation of the events
15503          * we generate. This is true by default but you may want to set it to
15504          * false if the html element contains other features that require the
15505          * mouse click.
15506          * @property stopPropagation
15507          * @type boolean
15508          * @static
15509          */
15510         stopPropagation: true,
15511
15512         /**
15513          * Internal flag that is set to true when drag and drop has been
15514          * intialized
15515          * @property initialized
15516          * @private
15517          * @static
15518          */
15519         initalized: false,
15520
15521         /**
15522          * All drag and drop can be disabled.
15523          * @property locked
15524          * @private
15525          * @static
15526          */
15527         locked: false,
15528
15529         /**
15530          * Called the first time an element is registered.
15531          * @method init
15532          * @private
15533          * @static
15534          */
15535         init: function() {
15536             this.initialized = true;
15537         },
15538
15539         /**
15540          * In point mode, drag and drop interaction is defined by the
15541          * location of the cursor during the drag/drop
15542          * @property POINT
15543          * @type int
15544          * @static
15545          */
15546         POINT: 0,
15547
15548         /**
15549          * In intersect mode, drag and drop interactio nis defined by the
15550          * overlap of two or more drag and drop objects.
15551          * @property INTERSECT
15552          * @type int
15553          * @static
15554          */
15555         INTERSECT: 1,
15556
15557         /**
15558          * The current drag and drop mode.  Default: POINT
15559          * @property mode
15560          * @type int
15561          * @static
15562          */
15563         mode: 0,
15564
15565         /**
15566          * Runs method on all drag and drop objects
15567          * @method _execOnAll
15568          * @private
15569          * @static
15570          */
15571         _execOnAll: function(sMethod, args) {
15572             for (var i in this.ids) {
15573                 for (var j in this.ids[i]) {
15574                     var oDD = this.ids[i][j];
15575                     if (! this.isTypeOfDD(oDD)) {
15576                         continue;
15577                     }
15578                     oDD[sMethod].apply(oDD, args);
15579                 }
15580             }
15581         },
15582
15583         /**
15584          * Drag and drop initialization.  Sets up the global event handlers
15585          * @method _onLoad
15586          * @private
15587          * @static
15588          */
15589         _onLoad: function() {
15590
15591             this.init();
15592
15593
15594             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15595             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15596             Event.on(window,   "unload",    this._onUnload, this, true);
15597             Event.on(window,   "resize",    this._onResize, this, true);
15598             // Event.on(window,   "mouseout",    this._test);
15599
15600         },
15601
15602         /**
15603          * Reset constraints on all drag and drop objs
15604          * @method _onResize
15605          * @private
15606          * @static
15607          */
15608         _onResize: function(e) {
15609             this._execOnAll("resetConstraints", []);
15610         },
15611
15612         /**
15613          * Lock all drag and drop functionality
15614          * @method lock
15615          * @static
15616          */
15617         lock: function() { this.locked = true; },
15618
15619         /**
15620          * Unlock all drag and drop functionality
15621          * @method unlock
15622          * @static
15623          */
15624         unlock: function() { this.locked = false; },
15625
15626         /**
15627          * Is drag and drop locked?
15628          * @method isLocked
15629          * @return {boolean} True if drag and drop is locked, false otherwise.
15630          * @static
15631          */
15632         isLocked: function() { return this.locked; },
15633
15634         /**
15635          * Location cache that is set for all drag drop objects when a drag is
15636          * initiated, cleared when the drag is finished.
15637          * @property locationCache
15638          * @private
15639          * @static
15640          */
15641         locationCache: {},
15642
15643         /**
15644          * Set useCache to false if you want to force object the lookup of each
15645          * drag and drop linked element constantly during a drag.
15646          * @property useCache
15647          * @type boolean
15648          * @static
15649          */
15650         useCache: true,
15651
15652         /**
15653          * The number of pixels that the mouse needs to move after the
15654          * mousedown before the drag is initiated.  Default=3;
15655          * @property clickPixelThresh
15656          * @type int
15657          * @static
15658          */
15659         clickPixelThresh: 3,
15660
15661         /**
15662          * The number of milliseconds after the mousedown event to initiate the
15663          * drag if we don't get a mouseup event. Default=1000
15664          * @property clickTimeThresh
15665          * @type int
15666          * @static
15667          */
15668         clickTimeThresh: 350,
15669
15670         /**
15671          * Flag that indicates that either the drag pixel threshold or the
15672          * mousdown time threshold has been met
15673          * @property dragThreshMet
15674          * @type boolean
15675          * @private
15676          * @static
15677          */
15678         dragThreshMet: false,
15679
15680         /**
15681          * Timeout used for the click time threshold
15682          * @property clickTimeout
15683          * @type Object
15684          * @private
15685          * @static
15686          */
15687         clickTimeout: null,
15688
15689         /**
15690          * The X position of the mousedown event stored for later use when a
15691          * drag threshold is met.
15692          * @property startX
15693          * @type int
15694          * @private
15695          * @static
15696          */
15697         startX: 0,
15698
15699         /**
15700          * The Y position of the mousedown event stored for later use when a
15701          * drag threshold is met.
15702          * @property startY
15703          * @type int
15704          * @private
15705          * @static
15706          */
15707         startY: 0,
15708
15709         /**
15710          * Each DragDrop instance must be registered with the DragDropMgr.
15711          * This is executed in DragDrop.init()
15712          * @method regDragDrop
15713          * @param {DragDrop} oDD the DragDrop object to register
15714          * @param {String} sGroup the name of the group this element belongs to
15715          * @static
15716          */
15717         regDragDrop: function(oDD, sGroup) {
15718             if (!this.initialized) { this.init(); }
15719
15720             if (!this.ids[sGroup]) {
15721                 this.ids[sGroup] = {};
15722             }
15723             this.ids[sGroup][oDD.id] = oDD;
15724         },
15725
15726         /**
15727          * Removes the supplied dd instance from the supplied group. Executed
15728          * by DragDrop.removeFromGroup, so don't call this function directly.
15729          * @method removeDDFromGroup
15730          * @private
15731          * @static
15732          */
15733         removeDDFromGroup: function(oDD, sGroup) {
15734             if (!this.ids[sGroup]) {
15735                 this.ids[sGroup] = {};
15736             }
15737
15738             var obj = this.ids[sGroup];
15739             if (obj && obj[oDD.id]) {
15740                 delete obj[oDD.id];
15741             }
15742         },
15743
15744         /**
15745          * Unregisters a drag and drop item.  This is executed in
15746          * DragDrop.unreg, use that method instead of calling this directly.
15747          * @method _remove
15748          * @private
15749          * @static
15750          */
15751         _remove: function(oDD) {
15752             for (var g in oDD.groups) {
15753                 if (g && this.ids[g][oDD.id]) {
15754                     delete this.ids[g][oDD.id];
15755                 }
15756             }
15757             delete this.handleIds[oDD.id];
15758         },
15759
15760         /**
15761          * Each DragDrop handle element must be registered.  This is done
15762          * automatically when executing DragDrop.setHandleElId()
15763          * @method regHandle
15764          * @param {String} sDDId the DragDrop id this element is a handle for
15765          * @param {String} sHandleId the id of the element that is the drag
15766          * handle
15767          * @static
15768          */
15769         regHandle: function(sDDId, sHandleId) {
15770             if (!this.handleIds[sDDId]) {
15771                 this.handleIds[sDDId] = {};
15772             }
15773             this.handleIds[sDDId][sHandleId] = sHandleId;
15774         },
15775
15776         /**
15777          * Utility function to determine if a given element has been
15778          * registered as a drag drop item.
15779          * @method isDragDrop
15780          * @param {String} id the element id to check
15781          * @return {boolean} true if this element is a DragDrop item,
15782          * false otherwise
15783          * @static
15784          */
15785         isDragDrop: function(id) {
15786             return ( this.getDDById(id) ) ? true : false;
15787         },
15788
15789         /**
15790          * Returns the drag and drop instances that are in all groups the
15791          * passed in instance belongs to.
15792          * @method getRelated
15793          * @param {DragDrop} p_oDD the obj to get related data for
15794          * @param {boolean} bTargetsOnly if true, only return targetable objs
15795          * @return {DragDrop[]} the related instances
15796          * @static
15797          */
15798         getRelated: function(p_oDD, bTargetsOnly) {
15799             var oDDs = [];
15800             for (var i in p_oDD.groups) {
15801                 for (j in this.ids[i]) {
15802                     var dd = this.ids[i][j];
15803                     if (! this.isTypeOfDD(dd)) {
15804                         continue;
15805                     }
15806                     if (!bTargetsOnly || dd.isTarget) {
15807                         oDDs[oDDs.length] = dd;
15808                     }
15809                 }
15810             }
15811
15812             return oDDs;
15813         },
15814
15815         /**
15816          * Returns true if the specified dd target is a legal target for
15817          * the specifice drag obj
15818          * @method isLegalTarget
15819          * @param {DragDrop} the drag obj
15820          * @param {DragDrop} the target
15821          * @return {boolean} true if the target is a legal target for the
15822          * dd obj
15823          * @static
15824          */
15825         isLegalTarget: function (oDD, oTargetDD) {
15826             var targets = this.getRelated(oDD, true);
15827             for (var i=0, len=targets.length;i<len;++i) {
15828                 if (targets[i].id == oTargetDD.id) {
15829                     return true;
15830                 }
15831             }
15832
15833             return false;
15834         },
15835
15836         /**
15837          * My goal is to be able to transparently determine if an object is
15838          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15839          * returns "object", oDD.constructor.toString() always returns
15840          * "DragDrop" and not the name of the subclass.  So for now it just
15841          * evaluates a well-known variable in DragDrop.
15842          * @method isTypeOfDD
15843          * @param {Object} the object to evaluate
15844          * @return {boolean} true if typeof oDD = DragDrop
15845          * @static
15846          */
15847         isTypeOfDD: function (oDD) {
15848             return (oDD && oDD.__ygDragDrop);
15849         },
15850
15851         /**
15852          * Utility function to determine if a given element has been
15853          * registered as a drag drop handle for the given Drag Drop object.
15854          * @method isHandle
15855          * @param {String} id the element id to check
15856          * @return {boolean} true if this element is a DragDrop handle, false
15857          * otherwise
15858          * @static
15859          */
15860         isHandle: function(sDDId, sHandleId) {
15861             return ( this.handleIds[sDDId] &&
15862                             this.handleIds[sDDId][sHandleId] );
15863         },
15864
15865         /**
15866          * Returns the DragDrop instance for a given id
15867          * @method getDDById
15868          * @param {String} id the id of the DragDrop object
15869          * @return {DragDrop} the drag drop object, null if it is not found
15870          * @static
15871          */
15872         getDDById: function(id) {
15873             for (var i in this.ids) {
15874                 if (this.ids[i][id]) {
15875                     return this.ids[i][id];
15876                 }
15877             }
15878             return null;
15879         },
15880
15881         /**
15882          * Fired after a registered DragDrop object gets the mousedown event.
15883          * Sets up the events required to track the object being dragged
15884          * @method handleMouseDown
15885          * @param {Event} e the event
15886          * @param oDD the DragDrop object being dragged
15887          * @private
15888          * @static
15889          */
15890         handleMouseDown: function(e, oDD) {
15891             if(Roo.QuickTips){
15892                 Roo.QuickTips.disable();
15893             }
15894             this.currentTarget = e.getTarget();
15895
15896             this.dragCurrent = oDD;
15897
15898             var el = oDD.getEl();
15899
15900             // track start position
15901             this.startX = e.getPageX();
15902             this.startY = e.getPageY();
15903
15904             this.deltaX = this.startX - el.offsetLeft;
15905             this.deltaY = this.startY - el.offsetTop;
15906
15907             this.dragThreshMet = false;
15908
15909             this.clickTimeout = setTimeout(
15910                     function() {
15911                         var DDM = Roo.dd.DDM;
15912                         DDM.startDrag(DDM.startX, DDM.startY);
15913                     },
15914                     this.clickTimeThresh );
15915         },
15916
15917         /**
15918          * Fired when either the drag pixel threshol or the mousedown hold
15919          * time threshold has been met.
15920          * @method startDrag
15921          * @param x {int} the X position of the original mousedown
15922          * @param y {int} the Y position of the original mousedown
15923          * @static
15924          */
15925         startDrag: function(x, y) {
15926             clearTimeout(this.clickTimeout);
15927             if (this.dragCurrent) {
15928                 this.dragCurrent.b4StartDrag(x, y);
15929                 this.dragCurrent.startDrag(x, y);
15930             }
15931             this.dragThreshMet = true;
15932         },
15933
15934         /**
15935          * Internal function to handle the mouseup event.  Will be invoked
15936          * from the context of the document.
15937          * @method handleMouseUp
15938          * @param {Event} e the event
15939          * @private
15940          * @static
15941          */
15942         handleMouseUp: function(e) {
15943
15944             if(Roo.QuickTips){
15945                 Roo.QuickTips.enable();
15946             }
15947             if (! this.dragCurrent) {
15948                 return;
15949             }
15950
15951             clearTimeout(this.clickTimeout);
15952
15953             if (this.dragThreshMet) {
15954                 this.fireEvents(e, true);
15955             } else {
15956             }
15957
15958             this.stopDrag(e);
15959
15960             this.stopEvent(e);
15961         },
15962
15963         /**
15964          * Utility to stop event propagation and event default, if these
15965          * features are turned on.
15966          * @method stopEvent
15967          * @param {Event} e the event as returned by this.getEvent()
15968          * @static
15969          */
15970         stopEvent: function(e){
15971             if(this.stopPropagation) {
15972                 e.stopPropagation();
15973             }
15974
15975             if (this.preventDefault) {
15976                 e.preventDefault();
15977             }
15978         },
15979
15980         /**
15981          * Internal function to clean up event handlers after the drag
15982          * operation is complete
15983          * @method stopDrag
15984          * @param {Event} e the event
15985          * @private
15986          * @static
15987          */
15988         stopDrag: function(e) {
15989             // Fire the drag end event for the item that was dragged
15990             if (this.dragCurrent) {
15991                 if (this.dragThreshMet) {
15992                     this.dragCurrent.b4EndDrag(e);
15993                     this.dragCurrent.endDrag(e);
15994                 }
15995
15996                 this.dragCurrent.onMouseUp(e);
15997             }
15998
15999             this.dragCurrent = null;
16000             this.dragOvers = {};
16001         },
16002
16003         /**
16004          * Internal function to handle the mousemove event.  Will be invoked
16005          * from the context of the html element.
16006          *
16007          * @TODO figure out what we can do about mouse events lost when the
16008          * user drags objects beyond the window boundary.  Currently we can
16009          * detect this in internet explorer by verifying that the mouse is
16010          * down during the mousemove event.  Firefox doesn't give us the
16011          * button state on the mousemove event.
16012          * @method handleMouseMove
16013          * @param {Event} e the event
16014          * @private
16015          * @static
16016          */
16017         handleMouseMove: function(e) {
16018             if (! this.dragCurrent) {
16019                 return true;
16020             }
16021
16022             // var button = e.which || e.button;
16023
16024             // check for IE mouseup outside of page boundary
16025             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16026                 this.stopEvent(e);
16027                 return this.handleMouseUp(e);
16028             }
16029
16030             if (!this.dragThreshMet) {
16031                 var diffX = Math.abs(this.startX - e.getPageX());
16032                 var diffY = Math.abs(this.startY - e.getPageY());
16033                 if (diffX > this.clickPixelThresh ||
16034                             diffY > this.clickPixelThresh) {
16035                     this.startDrag(this.startX, this.startY);
16036                 }
16037             }
16038
16039             if (this.dragThreshMet) {
16040                 this.dragCurrent.b4Drag(e);
16041                 this.dragCurrent.onDrag(e);
16042                 if(!this.dragCurrent.moveOnly){
16043                     this.fireEvents(e, false);
16044                 }
16045             }
16046
16047             this.stopEvent(e);
16048
16049             return true;
16050         },
16051
16052         /**
16053          * Iterates over all of the DragDrop elements to find ones we are
16054          * hovering over or dropping on
16055          * @method fireEvents
16056          * @param {Event} e the event
16057          * @param {boolean} isDrop is this a drop op or a mouseover op?
16058          * @private
16059          * @static
16060          */
16061         fireEvents: function(e, isDrop) {
16062             var dc = this.dragCurrent;
16063
16064             // If the user did the mouse up outside of the window, we could
16065             // get here even though we have ended the drag.
16066             if (!dc || dc.isLocked()) {
16067                 return;
16068             }
16069
16070             var pt = e.getPoint();
16071
16072             // cache the previous dragOver array
16073             var oldOvers = [];
16074
16075             var outEvts   = [];
16076             var overEvts  = [];
16077             var dropEvts  = [];
16078             var enterEvts = [];
16079
16080             // Check to see if the object(s) we were hovering over is no longer
16081             // being hovered over so we can fire the onDragOut event
16082             for (var i in this.dragOvers) {
16083
16084                 var ddo = this.dragOvers[i];
16085
16086                 if (! this.isTypeOfDD(ddo)) {
16087                     continue;
16088                 }
16089
16090                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16091                     outEvts.push( ddo );
16092                 }
16093
16094                 oldOvers[i] = true;
16095                 delete this.dragOvers[i];
16096             }
16097
16098             for (var sGroup in dc.groups) {
16099
16100                 if ("string" != typeof sGroup) {
16101                     continue;
16102                 }
16103
16104                 for (i in this.ids[sGroup]) {
16105                     var oDD = this.ids[sGroup][i];
16106                     if (! this.isTypeOfDD(oDD)) {
16107                         continue;
16108                     }
16109
16110                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16111                         if (this.isOverTarget(pt, oDD, this.mode)) {
16112                             // look for drop interactions
16113                             if (isDrop) {
16114                                 dropEvts.push( oDD );
16115                             // look for drag enter and drag over interactions
16116                             } else {
16117
16118                                 // initial drag over: dragEnter fires
16119                                 if (!oldOvers[oDD.id]) {
16120                                     enterEvts.push( oDD );
16121                                 // subsequent drag overs: dragOver fires
16122                                 } else {
16123                                     overEvts.push( oDD );
16124                                 }
16125
16126                                 this.dragOvers[oDD.id] = oDD;
16127                             }
16128                         }
16129                     }
16130                 }
16131             }
16132
16133             if (this.mode) {
16134                 if (outEvts.length) {
16135                     dc.b4DragOut(e, outEvts);
16136                     dc.onDragOut(e, outEvts);
16137                 }
16138
16139                 if (enterEvts.length) {
16140                     dc.onDragEnter(e, enterEvts);
16141                 }
16142
16143                 if (overEvts.length) {
16144                     dc.b4DragOver(e, overEvts);
16145                     dc.onDragOver(e, overEvts);
16146                 }
16147
16148                 if (dropEvts.length) {
16149                     dc.b4DragDrop(e, dropEvts);
16150                     dc.onDragDrop(e, dropEvts);
16151                 }
16152
16153             } else {
16154                 // fire dragout events
16155                 var len = 0;
16156                 for (i=0, len=outEvts.length; i<len; ++i) {
16157                     dc.b4DragOut(e, outEvts[i].id);
16158                     dc.onDragOut(e, outEvts[i].id);
16159                 }
16160
16161                 // fire enter events
16162                 for (i=0,len=enterEvts.length; i<len; ++i) {
16163                     // dc.b4DragEnter(e, oDD.id);
16164                     dc.onDragEnter(e, enterEvts[i].id);
16165                 }
16166
16167                 // fire over events
16168                 for (i=0,len=overEvts.length; i<len; ++i) {
16169                     dc.b4DragOver(e, overEvts[i].id);
16170                     dc.onDragOver(e, overEvts[i].id);
16171                 }
16172
16173                 // fire drop events
16174                 for (i=0, len=dropEvts.length; i<len; ++i) {
16175                     dc.b4DragDrop(e, dropEvts[i].id);
16176                     dc.onDragDrop(e, dropEvts[i].id);
16177                 }
16178
16179             }
16180
16181             // notify about a drop that did not find a target
16182             if (isDrop && !dropEvts.length) {
16183                 dc.onInvalidDrop(e);
16184             }
16185
16186         },
16187
16188         /**
16189          * Helper function for getting the best match from the list of drag
16190          * and drop objects returned by the drag and drop events when we are
16191          * in INTERSECT mode.  It returns either the first object that the
16192          * cursor is over, or the object that has the greatest overlap with
16193          * the dragged element.
16194          * @method getBestMatch
16195          * @param  {DragDrop[]} dds The array of drag and drop objects
16196          * targeted
16197          * @return {DragDrop}       The best single match
16198          * @static
16199          */
16200         getBestMatch: function(dds) {
16201             var winner = null;
16202             // Return null if the input is not what we expect
16203             //if (!dds || !dds.length || dds.length == 0) {
16204                // winner = null;
16205             // If there is only one item, it wins
16206             //} else if (dds.length == 1) {
16207
16208             var len = dds.length;
16209
16210             if (len == 1) {
16211                 winner = dds[0];
16212             } else {
16213                 // Loop through the targeted items
16214                 for (var i=0; i<len; ++i) {
16215                     var dd = dds[i];
16216                     // If the cursor is over the object, it wins.  If the
16217                     // cursor is over multiple matches, the first one we come
16218                     // to wins.
16219                     if (dd.cursorIsOver) {
16220                         winner = dd;
16221                         break;
16222                     // Otherwise the object with the most overlap wins
16223                     } else {
16224                         if (!winner ||
16225                             winner.overlap.getArea() < dd.overlap.getArea()) {
16226                             winner = dd;
16227                         }
16228                     }
16229                 }
16230             }
16231
16232             return winner;
16233         },
16234
16235         /**
16236          * Refreshes the cache of the top-left and bottom-right points of the
16237          * drag and drop objects in the specified group(s).  This is in the
16238          * format that is stored in the drag and drop instance, so typical
16239          * usage is:
16240          * <code>
16241          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16242          * </code>
16243          * Alternatively:
16244          * <code>
16245          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16246          * </code>
16247          * @TODO this really should be an indexed array.  Alternatively this
16248          * method could accept both.
16249          * @method refreshCache
16250          * @param {Object} groups an associative array of groups to refresh
16251          * @static
16252          */
16253         refreshCache: function(groups) {
16254             for (var sGroup in groups) {
16255                 if ("string" != typeof sGroup) {
16256                     continue;
16257                 }
16258                 for (var i in this.ids[sGroup]) {
16259                     var oDD = this.ids[sGroup][i];
16260
16261                     if (this.isTypeOfDD(oDD)) {
16262                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16263                         var loc = this.getLocation(oDD);
16264                         if (loc) {
16265                             this.locationCache[oDD.id] = loc;
16266                         } else {
16267                             delete this.locationCache[oDD.id];
16268                             // this will unregister the drag and drop object if
16269                             // the element is not in a usable state
16270                             // oDD.unreg();
16271                         }
16272                     }
16273                 }
16274             }
16275         },
16276
16277         /**
16278          * This checks to make sure an element exists and is in the DOM.  The
16279          * main purpose is to handle cases where innerHTML is used to remove
16280          * drag and drop objects from the DOM.  IE provides an 'unspecified
16281          * error' when trying to access the offsetParent of such an element
16282          * @method verifyEl
16283          * @param {HTMLElement} el the element to check
16284          * @return {boolean} true if the element looks usable
16285          * @static
16286          */
16287         verifyEl: function(el) {
16288             if (el) {
16289                 var parent;
16290                 if(Roo.isIE){
16291                     try{
16292                         parent = el.offsetParent;
16293                     }catch(e){}
16294                 }else{
16295                     parent = el.offsetParent;
16296                 }
16297                 if (parent) {
16298                     return true;
16299                 }
16300             }
16301
16302             return false;
16303         },
16304
16305         /**
16306          * Returns a Region object containing the drag and drop element's position
16307          * and size, including the padding configured for it
16308          * @method getLocation
16309          * @param {DragDrop} oDD the drag and drop object to get the
16310          *                       location for
16311          * @return {Roo.lib.Region} a Region object representing the total area
16312          *                             the element occupies, including any padding
16313          *                             the instance is configured for.
16314          * @static
16315          */
16316         getLocation: function(oDD) {
16317             if (! this.isTypeOfDD(oDD)) {
16318                 return null;
16319             }
16320
16321             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16322
16323             try {
16324                 pos= Roo.lib.Dom.getXY(el);
16325             } catch (e) { }
16326
16327             if (!pos) {
16328                 return null;
16329             }
16330
16331             x1 = pos[0];
16332             x2 = x1 + el.offsetWidth;
16333             y1 = pos[1];
16334             y2 = y1 + el.offsetHeight;
16335
16336             t = y1 - oDD.padding[0];
16337             r = x2 + oDD.padding[1];
16338             b = y2 + oDD.padding[2];
16339             l = x1 - oDD.padding[3];
16340
16341             return new Roo.lib.Region( t, r, b, l );
16342         },
16343
16344         /**
16345          * Checks the cursor location to see if it over the target
16346          * @method isOverTarget
16347          * @param {Roo.lib.Point} pt The point to evaluate
16348          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16349          * @return {boolean} true if the mouse is over the target
16350          * @private
16351          * @static
16352          */
16353         isOverTarget: function(pt, oTarget, intersect) {
16354             // use cache if available
16355             var loc = this.locationCache[oTarget.id];
16356             if (!loc || !this.useCache) {
16357                 loc = this.getLocation(oTarget);
16358                 this.locationCache[oTarget.id] = loc;
16359
16360             }
16361
16362             if (!loc) {
16363                 return false;
16364             }
16365
16366             oTarget.cursorIsOver = loc.contains( pt );
16367
16368             // DragDrop is using this as a sanity check for the initial mousedown
16369             // in this case we are done.  In POINT mode, if the drag obj has no
16370             // contraints, we are also done. Otherwise we need to evaluate the
16371             // location of the target as related to the actual location of the
16372             // dragged element.
16373             var dc = this.dragCurrent;
16374             if (!dc || !dc.getTargetCoord ||
16375                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16376                 return oTarget.cursorIsOver;
16377             }
16378
16379             oTarget.overlap = null;
16380
16381             // Get the current location of the drag element, this is the
16382             // location of the mouse event less the delta that represents
16383             // where the original mousedown happened on the element.  We
16384             // need to consider constraints and ticks as well.
16385             var pos = dc.getTargetCoord(pt.x, pt.y);
16386
16387             var el = dc.getDragEl();
16388             var curRegion = new Roo.lib.Region( pos.y,
16389                                                    pos.x + el.offsetWidth,
16390                                                    pos.y + el.offsetHeight,
16391                                                    pos.x );
16392
16393             var overlap = curRegion.intersect(loc);
16394
16395             if (overlap) {
16396                 oTarget.overlap = overlap;
16397                 return (intersect) ? true : oTarget.cursorIsOver;
16398             } else {
16399                 return false;
16400             }
16401         },
16402
16403         /**
16404          * unload event handler
16405          * @method _onUnload
16406          * @private
16407          * @static
16408          */
16409         _onUnload: function(e, me) {
16410             Roo.dd.DragDropMgr.unregAll();
16411         },
16412
16413         /**
16414          * Cleans up the drag and drop events and objects.
16415          * @method unregAll
16416          * @private
16417          * @static
16418          */
16419         unregAll: function() {
16420
16421             if (this.dragCurrent) {
16422                 this.stopDrag();
16423                 this.dragCurrent = null;
16424             }
16425
16426             this._execOnAll("unreg", []);
16427
16428             for (i in this.elementCache) {
16429                 delete this.elementCache[i];
16430             }
16431
16432             this.elementCache = {};
16433             this.ids = {};
16434         },
16435
16436         /**
16437          * A cache of DOM elements
16438          * @property elementCache
16439          * @private
16440          * @static
16441          */
16442         elementCache: {},
16443
16444         /**
16445          * Get the wrapper for the DOM element specified
16446          * @method getElWrapper
16447          * @param {String} id the id of the element to get
16448          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16449          * @private
16450          * @deprecated This wrapper isn't that useful
16451          * @static
16452          */
16453         getElWrapper: function(id) {
16454             var oWrapper = this.elementCache[id];
16455             if (!oWrapper || !oWrapper.el) {
16456                 oWrapper = this.elementCache[id] =
16457                     new this.ElementWrapper(Roo.getDom(id));
16458             }
16459             return oWrapper;
16460         },
16461
16462         /**
16463          * Returns the actual DOM element
16464          * @method getElement
16465          * @param {String} id the id of the elment to get
16466          * @return {Object} The element
16467          * @deprecated use Roo.getDom instead
16468          * @static
16469          */
16470         getElement: function(id) {
16471             return Roo.getDom(id);
16472         },
16473
16474         /**
16475          * Returns the style property for the DOM element (i.e.,
16476          * document.getElById(id).style)
16477          * @method getCss
16478          * @param {String} id the id of the elment to get
16479          * @return {Object} The style property of the element
16480          * @deprecated use Roo.getDom instead
16481          * @static
16482          */
16483         getCss: function(id) {
16484             var el = Roo.getDom(id);
16485             return (el) ? el.style : null;
16486         },
16487
16488         /**
16489          * Inner class for cached elements
16490          * @class DragDropMgr.ElementWrapper
16491          * @for DragDropMgr
16492          * @private
16493          * @deprecated
16494          */
16495         ElementWrapper: function(el) {
16496                 /**
16497                  * The element
16498                  * @property el
16499                  */
16500                 this.el = el || null;
16501                 /**
16502                  * The element id
16503                  * @property id
16504                  */
16505                 this.id = this.el && el.id;
16506                 /**
16507                  * A reference to the style property
16508                  * @property css
16509                  */
16510                 this.css = this.el && el.style;
16511             },
16512
16513         /**
16514          * Returns the X position of an html element
16515          * @method getPosX
16516          * @param el the element for which to get the position
16517          * @return {int} the X coordinate
16518          * @for DragDropMgr
16519          * @deprecated use Roo.lib.Dom.getX instead
16520          * @static
16521          */
16522         getPosX: function(el) {
16523             return Roo.lib.Dom.getX(el);
16524         },
16525
16526         /**
16527          * Returns the Y position of an html element
16528          * @method getPosY
16529          * @param el the element for which to get the position
16530          * @return {int} the Y coordinate
16531          * @deprecated use Roo.lib.Dom.getY instead
16532          * @static
16533          */
16534         getPosY: function(el) {
16535             return Roo.lib.Dom.getY(el);
16536         },
16537
16538         /**
16539          * Swap two nodes.  In IE, we use the native method, for others we
16540          * emulate the IE behavior
16541          * @method swapNode
16542          * @param n1 the first node to swap
16543          * @param n2 the other node to swap
16544          * @static
16545          */
16546         swapNode: function(n1, n2) {
16547             if (n1.swapNode) {
16548                 n1.swapNode(n2);
16549             } else {
16550                 var p = n2.parentNode;
16551                 var s = n2.nextSibling;
16552
16553                 if (s == n1) {
16554                     p.insertBefore(n1, n2);
16555                 } else if (n2 == n1.nextSibling) {
16556                     p.insertBefore(n2, n1);
16557                 } else {
16558                     n1.parentNode.replaceChild(n2, n1);
16559                     p.insertBefore(n1, s);
16560                 }
16561             }
16562         },
16563
16564         /**
16565          * Returns the current scroll position
16566          * @method getScroll
16567          * @private
16568          * @static
16569          */
16570         getScroll: function () {
16571             var t, l, dde=document.documentElement, db=document.body;
16572             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16573                 t = dde.scrollTop;
16574                 l = dde.scrollLeft;
16575             } else if (db) {
16576                 t = db.scrollTop;
16577                 l = db.scrollLeft;
16578             } else {
16579
16580             }
16581             return { top: t, left: l };
16582         },
16583
16584         /**
16585          * Returns the specified element style property
16586          * @method getStyle
16587          * @param {HTMLElement} el          the element
16588          * @param {string}      styleProp   the style property
16589          * @return {string} The value of the style property
16590          * @deprecated use Roo.lib.Dom.getStyle
16591          * @static
16592          */
16593         getStyle: function(el, styleProp) {
16594             return Roo.fly(el).getStyle(styleProp);
16595         },
16596
16597         /**
16598          * Gets the scrollTop
16599          * @method getScrollTop
16600          * @return {int} the document's scrollTop
16601          * @static
16602          */
16603         getScrollTop: function () { return this.getScroll().top; },
16604
16605         /**
16606          * Gets the scrollLeft
16607          * @method getScrollLeft
16608          * @return {int} the document's scrollTop
16609          * @static
16610          */
16611         getScrollLeft: function () { return this.getScroll().left; },
16612
16613         /**
16614          * Sets the x/y position of an element to the location of the
16615          * target element.
16616          * @method moveToEl
16617          * @param {HTMLElement} moveEl      The element to move
16618          * @param {HTMLElement} targetEl    The position reference element
16619          * @static
16620          */
16621         moveToEl: function (moveEl, targetEl) {
16622             var aCoord = Roo.lib.Dom.getXY(targetEl);
16623             Roo.lib.Dom.setXY(moveEl, aCoord);
16624         },
16625
16626         /**
16627          * Numeric array sort function
16628          * @method numericSort
16629          * @static
16630          */
16631         numericSort: function(a, b) { return (a - b); },
16632
16633         /**
16634          * Internal counter
16635          * @property _timeoutCount
16636          * @private
16637          * @static
16638          */
16639         _timeoutCount: 0,
16640
16641         /**
16642          * Trying to make the load order less important.  Without this we get
16643          * an error if this file is loaded before the Event Utility.
16644          * @method _addListeners
16645          * @private
16646          * @static
16647          */
16648         _addListeners: function() {
16649             var DDM = Roo.dd.DDM;
16650             if ( Roo.lib.Event && document ) {
16651                 DDM._onLoad();
16652             } else {
16653                 if (DDM._timeoutCount > 2000) {
16654                 } else {
16655                     setTimeout(DDM._addListeners, 10);
16656                     if (document && document.body) {
16657                         DDM._timeoutCount += 1;
16658                     }
16659                 }
16660             }
16661         },
16662
16663         /**
16664          * Recursively searches the immediate parent and all child nodes for
16665          * the handle element in order to determine wheter or not it was
16666          * clicked.
16667          * @method handleWasClicked
16668          * @param node the html element to inspect
16669          * @static
16670          */
16671         handleWasClicked: function(node, id) {
16672             if (this.isHandle(id, node.id)) {
16673                 return true;
16674             } else {
16675                 // check to see if this is a text node child of the one we want
16676                 var p = node.parentNode;
16677
16678                 while (p) {
16679                     if (this.isHandle(id, p.id)) {
16680                         return true;
16681                     } else {
16682                         p = p.parentNode;
16683                     }
16684                 }
16685             }
16686
16687             return false;
16688         }
16689
16690     };
16691
16692 }();
16693
16694 // shorter alias, save a few bytes
16695 Roo.dd.DDM = Roo.dd.DragDropMgr;
16696 Roo.dd.DDM._addListeners();
16697
16698 }