roojs-core-debug.js
[roojs1] / roojs-core-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
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                 if (Roo.debug) 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  */
1107 Date.createParser = function(format) {
1108     var funcName = "parse" + Date.parseFunctions.count++;
1109     var regexNum = Date.parseRegexes.length;
1110     var currentGroup = 1;
1111     Date.parseFunctions[format] = funcName;
1112
1113     var code = "Date." + funcName + " = function(input){\n"
1114         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1115         + "var d = new Date();\n"
1116         + "y = d.getFullYear();\n"
1117         + "m = d.getMonth();\n"
1118         + "d = d.getDate();\n"
1119         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1120         + "if (results && results.length > 0) {";
1121     var regex = "";
1122
1123     var special = false;
1124     var ch = '';
1125     for (var i = 0; i < format.length; ++i) {
1126         ch = format.charAt(i);
1127         if (!special && ch == "\\") {
1128             special = true;
1129         }
1130         else if (special) {
1131             special = false;
1132             regex += String.escape(ch);
1133         }
1134         else {
1135             var obj = Date.formatCodeToRegex(ch, currentGroup);
1136             currentGroup += obj.g;
1137             regex += obj.s;
1138             if (obj.g && obj.c) {
1139                 code += obj.c;
1140             }
1141         }
1142     }
1143
1144     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1145         + "{v = new Date(y, m, d, h, i, s);}\n"
1146         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1147         + "{v = new Date(y, m, d, h, i);}\n"
1148         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1149         + "{v = new Date(y, m, d, h);}\n"
1150         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1151         + "{v = new Date(y, m, d);}\n"
1152         + "else if (y >= 0 && m >= 0)\n"
1153         + "{v = new Date(y, m);}\n"
1154         + "else if (y >= 0)\n"
1155         + "{v = new Date(y);}\n"
1156         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1157         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1158         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1159         + ";}";
1160
1161     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1162     /** eval:var:zzzzzzzzzzzzz */
1163     eval(code);
1164 };
1165
1166 // private
1167 Date.formatCodeToRegex = function(character, currentGroup) {
1168     switch (character) {
1169     case "D":
1170         return {g:0,
1171         c:null,
1172         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1173     case "j":
1174         return {g:1,
1175             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1176             s:"(\\d{1,2})"}; // day of month without leading zeroes
1177     case "d":
1178         return {g:1,
1179             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1180             s:"(\\d{2})"}; // day of month with leading zeroes
1181     case "l":
1182         return {g:0,
1183             c:null,
1184             s:"(?:" + Date.dayNames.join("|") + ")"};
1185     case "S":
1186         return {g:0,
1187             c:null,
1188             s:"(?:st|nd|rd|th)"};
1189     case "w":
1190         return {g:0,
1191             c:null,
1192             s:"\\d"};
1193     case "z":
1194         return {g:0,
1195             c:null,
1196             s:"(?:\\d{1,3})"};
1197     case "W":
1198         return {g:0,
1199             c:null,
1200             s:"(?:\\d{2})"};
1201     case "F":
1202         return {g:1,
1203             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1204             s:"(" + Date.monthNames.join("|") + ")"};
1205     case "M":
1206         return {g:1,
1207             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1208             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1209     case "n":
1210         return {g:1,
1211             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1212             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1213     case "m":
1214         return {g:1,
1215             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1216             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1217     case "t":
1218         return {g:0,
1219             c:null,
1220             s:"\\d{1,2}"};
1221     case "L":
1222         return {g:0,
1223             c:null,
1224             s:"(?:1|0)"};
1225     case "Y":
1226         return {g:1,
1227             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1228             s:"(\\d{4})"};
1229     case "y":
1230         return {g:1,
1231             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1232                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1233             s:"(\\d{1,2})"};
1234     case "a":
1235         return {g:1,
1236             c:"if (results[" + currentGroup + "] == 'am') {\n"
1237                 + "if (h == 12) { h = 0; }\n"
1238                 + "} else { if (h < 12) { h += 12; }}",
1239             s:"(am|pm)"};
1240     case "A":
1241         return {g:1,
1242             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1243                 + "if (h == 12) { h = 0; }\n"
1244                 + "} else { if (h < 12) { h += 12; }}",
1245             s:"(AM|PM)"};
1246     case "g":
1247     case "G":
1248         return {g:1,
1249             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1250             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1251     case "h":
1252     case "H":
1253         return {g:1,
1254             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1255             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1256     case "i":
1257         return {g:1,
1258             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1259             s:"(\\d{2})"};
1260     case "s":
1261         return {g:1,
1262             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1263             s:"(\\d{2})"};
1264     case "O":
1265         return {g:1,
1266             c:[
1267                 "o = results[", currentGroup, "];\n",
1268                 "var sn = o.substring(0,1);\n", // get + / - sign
1269                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1270                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1271                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1272                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1273             ].join(""),
1274             s:"([+\-]\\d{4})"};
1275     case "T":
1276         return {g:0,
1277             c:null,
1278             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1279     case "Z":
1280         return {g:1,
1281             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1282                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1283             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1284     default:
1285         return {g:0,
1286             c:null,
1287             s:String.escape(character)};
1288     }
1289 };
1290
1291 /**
1292  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1293  * @return {String} The abbreviated timezone name (e.g. 'CST')
1294  */
1295 Date.prototype.getTimezone = function() {
1296     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1297 };
1298
1299 /**
1300  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1301  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1302  */
1303 Date.prototype.getGMTOffset = function() {
1304     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1305         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1306         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1307 };
1308
1309 /**
1310  * Get the numeric day number of the year, adjusted for leap year.
1311  * @return {Number} 0 through 364 (365 in leap years)
1312  */
1313 Date.prototype.getDayOfYear = function() {
1314     var num = 0;
1315     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1316     for (var i = 0; i < this.getMonth(); ++i) {
1317         num += Date.daysInMonth[i];
1318     }
1319     return num + this.getDate() - 1;
1320 };
1321
1322 /**
1323  * Get the string representation of the numeric week number of the year
1324  * (equivalent to the format specifier 'W').
1325  * @return {String} '00' through '52'
1326  */
1327 Date.prototype.getWeekOfYear = function() {
1328     // Skip to Thursday of this week
1329     var now = this.getDayOfYear() + (4 - this.getDay());
1330     // Find the first Thursday of the year
1331     var jan1 = new Date(this.getFullYear(), 0, 1);
1332     var then = (7 - jan1.getDay() + 4);
1333     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1334 };
1335
1336 /**
1337  * Whether or not the current date is in a leap year.
1338  * @return {Boolean} True if the current date is in a leap year, else false
1339  */
1340 Date.prototype.isLeapYear = function() {
1341     var year = this.getFullYear();
1342     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1343 };
1344
1345 /**
1346  * Get the first day of the current month, adjusted for leap year.  The returned value
1347  * is the numeric day index within the week (0-6) which can be used in conjunction with
1348  * the {@link #monthNames} array to retrieve the textual day name.
1349  * Example:
1350  *<pre><code>
1351 var dt = new Date('1/10/2007');
1352 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1353 </code></pre>
1354  * @return {Number} The day number (0-6)
1355  */
1356 Date.prototype.getFirstDayOfMonth = function() {
1357     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1358     return (day < 0) ? (day + 7) : day;
1359 };
1360
1361 /**
1362  * Get the last day of the current month, adjusted for leap year.  The returned value
1363  * is the numeric day index within the week (0-6) which can be used in conjunction with
1364  * the {@link #monthNames} array to retrieve the textual day name.
1365  * Example:
1366  *<pre><code>
1367 var dt = new Date('1/10/2007');
1368 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1369 </code></pre>
1370  * @return {Number} The day number (0-6)
1371  */
1372 Date.prototype.getLastDayOfMonth = function() {
1373     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1374     return (day < 0) ? (day + 7) : day;
1375 };
1376
1377
1378 /**
1379  * Get the first date of this date's month
1380  * @return {Date}
1381  */
1382 Date.prototype.getFirstDateOfMonth = function() {
1383     return new Date(this.getFullYear(), this.getMonth(), 1);
1384 };
1385
1386 /**
1387  * Get the last date of this date's month
1388  * @return {Date}
1389  */
1390 Date.prototype.getLastDateOfMonth = function() {
1391     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1392 };
1393 /**
1394  * Get the number of days in the current month, adjusted for leap year.
1395  * @return {Number} The number of days in the month
1396  */
1397 Date.prototype.getDaysInMonth = function() {
1398     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1399     return Date.daysInMonth[this.getMonth()];
1400 };
1401
1402 /**
1403  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1404  * @return {String} 'st, 'nd', 'rd' or 'th'
1405  */
1406 Date.prototype.getSuffix = function() {
1407     switch (this.getDate()) {
1408         case 1:
1409         case 21:
1410         case 31:
1411             return "st";
1412         case 2:
1413         case 22:
1414             return "nd";
1415         case 3:
1416         case 23:
1417             return "rd";
1418         default:
1419             return "th";
1420     }
1421 };
1422
1423 // private
1424 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1425
1426 /**
1427  * An array of textual month names.
1428  * Override these values for international dates, for example...
1429  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1430  * @type Array
1431  * @static
1432  */
1433 Date.monthNames =
1434    ["January",
1435     "February",
1436     "March",
1437     "April",
1438     "May",
1439     "June",
1440     "July",
1441     "August",
1442     "September",
1443     "October",
1444     "November",
1445     "December"];
1446
1447 /**
1448  * An array of textual day names.
1449  * Override these values for international dates, for example...
1450  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1451  * @type Array
1452  * @static
1453  */
1454 Date.dayNames =
1455    ["Sunday",
1456     "Monday",
1457     "Tuesday",
1458     "Wednesday",
1459     "Thursday",
1460     "Friday",
1461     "Saturday"];
1462
1463 // private
1464 Date.y2kYear = 50;
1465 // private
1466 Date.monthNumbers = {
1467     Jan:0,
1468     Feb:1,
1469     Mar:2,
1470     Apr:3,
1471     May:4,
1472     Jun:5,
1473     Jul:6,
1474     Aug:7,
1475     Sep:8,
1476     Oct:9,
1477     Nov:10,
1478     Dec:11};
1479
1480 /**
1481  * Creates and returns a new Date instance with the exact same date value as the called instance.
1482  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1483  * variable will also be changed.  When the intention is to create a new variable that will not
1484  * modify the original instance, you should create a clone.
1485  *
1486  * Example of correctly cloning a date:
1487  * <pre><code>
1488 //wrong way:
1489 var orig = new Date('10/1/2006');
1490 var copy = orig;
1491 copy.setDate(5);
1492 document.write(orig);  //returns 'Thu Oct 05 2006'!
1493
1494 //correct way:
1495 var orig = new Date('10/1/2006');
1496 var copy = orig.clone();
1497 copy.setDate(5);
1498 document.write(orig);  //returns 'Thu Oct 01 2006'
1499 </code></pre>
1500  * @return {Date} The new Date instance
1501  */
1502 Date.prototype.clone = function() {
1503         return new Date(this.getTime());
1504 };
1505
1506 /**
1507  * Clears any time information from this date
1508  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1509  @return {Date} this or the clone
1510  */
1511 Date.prototype.clearTime = function(clone){
1512     if(clone){
1513         return this.clone().clearTime();
1514     }
1515     this.setHours(0);
1516     this.setMinutes(0);
1517     this.setSeconds(0);
1518     this.setMilliseconds(0);
1519     return this;
1520 };
1521
1522 // private
1523 // safari setMonth is broken
1524 if(Roo.isSafari){
1525     Date.brokenSetMonth = Date.prototype.setMonth;
1526         Date.prototype.setMonth = function(num){
1527                 if(num <= -1){
1528                         var n = Math.ceil(-num);
1529                         var back_year = Math.ceil(n/12);
1530                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1531                         this.setFullYear(this.getFullYear() - back_year);
1532                         return Date.brokenSetMonth.call(this, month);
1533                 } else {
1534                         return Date.brokenSetMonth.apply(this, arguments);
1535                 }
1536         };
1537 }
1538
1539 /** Date interval constant 
1540 * @static 
1541 * @type String */
1542 Date.MILLI = "ms";
1543 /** Date interval constant 
1544 * @static 
1545 * @type String */
1546 Date.SECOND = "s";
1547 /** Date interval constant 
1548 * @static 
1549 * @type String */
1550 Date.MINUTE = "mi";
1551 /** Date interval constant 
1552 * @static 
1553 * @type String */
1554 Date.HOUR = "h";
1555 /** Date interval constant 
1556 * @static 
1557 * @type String */
1558 Date.DAY = "d";
1559 /** Date interval constant 
1560 * @static 
1561 * @type String */
1562 Date.MONTH = "mo";
1563 /** Date interval constant 
1564 * @static 
1565 * @type String */
1566 Date.YEAR = "y";
1567
1568 /**
1569  * Provides a convenient method of performing basic date arithmetic.  This method
1570  * does not modify the Date instance being called - it creates and returns
1571  * a new Date instance containing the resulting date value.
1572  *
1573  * Examples:
1574  * <pre><code>
1575 //Basic usage:
1576 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1577 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1578
1579 //Negative values will subtract correctly:
1580 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1581 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1582
1583 //You can even chain several calls together in one line!
1584 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1585 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1586  </code></pre>
1587  *
1588  * @param {String} interval   A valid date interval enum value
1589  * @param {Number} value      The amount to add to the current date
1590  * @return {Date} The new Date instance
1591  */
1592 Date.prototype.add = function(interval, value){
1593   var d = this.clone();
1594   if (!interval || value === 0) return d;
1595   switch(interval.toLowerCase()){
1596     case Date.MILLI:
1597       d.setMilliseconds(this.getMilliseconds() + value);
1598       break;
1599     case Date.SECOND:
1600       d.setSeconds(this.getSeconds() + value);
1601       break;
1602     case Date.MINUTE:
1603       d.setMinutes(this.getMinutes() + value);
1604       break;
1605     case Date.HOUR:
1606       d.setHours(this.getHours() + value);
1607       break;
1608     case Date.DAY:
1609       d.setDate(this.getDate() + value);
1610       break;
1611     case Date.MONTH:
1612       var day = this.getDate();
1613       if(day > 28){
1614           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1615       }
1616       d.setDate(day);
1617       d.setMonth(this.getMonth() + value);
1618       break;
1619     case Date.YEAR:
1620       d.setFullYear(this.getFullYear() + value);
1621       break;
1622   }
1623   return d;
1624 };/*
1625  * Based on:
1626  * Ext JS Library 1.1.1
1627  * Copyright(c) 2006-2007, Ext JS, LLC.
1628  *
1629  * Originally Released Under LGPL - original licence link has changed is not relivant.
1630  *
1631  * Fork - LGPL
1632  * <script type="text/javascript">
1633  */
1634
1635 Roo.lib.Dom = {
1636     getViewWidth : function(full) {
1637         return full ? this.getDocumentWidth() : this.getViewportWidth();
1638     },
1639
1640     getViewHeight : function(full) {
1641         return full ? this.getDocumentHeight() : this.getViewportHeight();
1642     },
1643
1644     getDocumentHeight: function() {
1645         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1646         return Math.max(scrollHeight, this.getViewportHeight());
1647     },
1648
1649     getDocumentWidth: function() {
1650         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1651         return Math.max(scrollWidth, this.getViewportWidth());
1652     },
1653
1654     getViewportHeight: function() {
1655         var height = self.innerHeight;
1656         var mode = document.compatMode;
1657
1658         if ((mode || Roo.isIE) && !Roo.isOpera) {
1659             height = (mode == "CSS1Compat") ?
1660                      document.documentElement.clientHeight :
1661                      document.body.clientHeight;
1662         }
1663
1664         return height;
1665     },
1666
1667     getViewportWidth: function() {
1668         var width = self.innerWidth;
1669         var mode = document.compatMode;
1670
1671         if (mode || Roo.isIE) {
1672             width = (mode == "CSS1Compat") ?
1673                     document.documentElement.clientWidth :
1674                     document.body.clientWidth;
1675         }
1676         return width;
1677     },
1678
1679     isAncestor : function(p, c) {
1680         p = Roo.getDom(p);
1681         c = Roo.getDom(c);
1682         if (!p || !c) {
1683             return false;
1684         }
1685
1686         if (p.contains && !Roo.isSafari) {
1687             return p.contains(c);
1688         } else if (p.compareDocumentPosition) {
1689             return !!(p.compareDocumentPosition(c) & 16);
1690         } else {
1691             var parent = c.parentNode;
1692             while (parent) {
1693                 if (parent == p) {
1694                     return true;
1695                 }
1696                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1697                     return false;
1698                 }
1699                 parent = parent.parentNode;
1700             }
1701             return false;
1702         }
1703     },
1704
1705     getRegion : function(el) {
1706         return Roo.lib.Region.getRegion(el);
1707     },
1708
1709     getY : function(el) {
1710         return this.getXY(el)[1];
1711     },
1712
1713     getX : function(el) {
1714         return this.getXY(el)[0];
1715     },
1716
1717     getXY : function(el) {
1718         var p, pe, b, scroll, bd = document.body;
1719         el = Roo.getDom(el);
1720         var fly = Roo.lib.AnimBase.fly;
1721         if (el.getBoundingClientRect) {
1722             b = el.getBoundingClientRect();
1723             scroll = fly(document).getScroll();
1724             return [b.left + scroll.left, b.top + scroll.top];
1725         }
1726         var x = 0, y = 0;
1727
1728         p = el;
1729
1730         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1731
1732         while (p) {
1733
1734             x += p.offsetLeft;
1735             y += p.offsetTop;
1736
1737             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1738                 hasAbsolute = true;
1739             }
1740
1741             if (Roo.isGecko) {
1742                 pe = fly(p);
1743
1744                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1745                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1746
1747
1748                 x += bl;
1749                 y += bt;
1750
1751
1752                 if (p != el && pe.getStyle('overflow') != 'visible') {
1753                     x += bl;
1754                     y += bt;
1755                 }
1756             }
1757             p = p.offsetParent;
1758         }
1759
1760         if (Roo.isSafari && hasAbsolute) {
1761             x -= bd.offsetLeft;
1762             y -= bd.offsetTop;
1763         }
1764
1765         if (Roo.isGecko && !hasAbsolute) {
1766             var dbd = fly(bd);
1767             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1768             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1769         }
1770
1771         p = el.parentNode;
1772         while (p && p != bd) {
1773             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1774                 x -= p.scrollLeft;
1775                 y -= p.scrollTop;
1776             }
1777             p = p.parentNode;
1778         }
1779         return [x, y];
1780     },
1781  
1782   
1783
1784
1785     setXY : function(el, xy) {
1786         el = Roo.fly(el, '_setXY');
1787         el.position();
1788         var pts = el.translatePoints(xy);
1789         if (xy[0] !== false) {
1790             el.dom.style.left = pts.left + "px";
1791         }
1792         if (xy[1] !== false) {
1793             el.dom.style.top = pts.top + "px";
1794         }
1795     },
1796
1797     setX : function(el, x) {
1798         this.setXY(el, [x, false]);
1799     },
1800
1801     setY : function(el, y) {
1802         this.setXY(el, [false, y]);
1803     }
1804 };
1805 /*
1806  * Portions of this file are based on pieces of Yahoo User Interface Library
1807  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1808  * YUI licensed under the BSD License:
1809  * http://developer.yahoo.net/yui/license.txt
1810  * <script type="text/javascript">
1811  *
1812  */
1813
1814 Roo.lib.Event = function() {
1815     var loadComplete = false;
1816     var listeners = [];
1817     var unloadListeners = [];
1818     var retryCount = 0;
1819     var onAvailStack = [];
1820     var counter = 0;
1821     var lastError = null;
1822
1823     return {
1824         POLL_RETRYS: 200,
1825         POLL_INTERVAL: 20,
1826         EL: 0,
1827         TYPE: 1,
1828         FN: 2,
1829         WFN: 3,
1830         OBJ: 3,
1831         ADJ_SCOPE: 4,
1832         _interval: null,
1833
1834         startInterval: function() {
1835             if (!this._interval) {
1836                 var self = this;
1837                 var callback = function() {
1838                     self._tryPreloadAttach();
1839                 };
1840                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1841
1842             }
1843         },
1844
1845         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1846             onAvailStack.push({ id:         p_id,
1847                 fn:         p_fn,
1848                 obj:        p_obj,
1849                 override:   p_override,
1850                 checkReady: false    });
1851
1852             retryCount = this.POLL_RETRYS;
1853             this.startInterval();
1854         },
1855
1856
1857         addListener: function(el, eventName, fn) {
1858             el = Roo.getDom(el);
1859             if (!el || !fn) {
1860                 return false;
1861             }
1862
1863             if ("unload" == eventName) {
1864                 unloadListeners[unloadListeners.length] =
1865                 [el, eventName, fn];
1866                 return true;
1867             }
1868
1869             var wrappedFn = function(e) {
1870                 return fn(Roo.lib.Event.getEvent(e));
1871             };
1872
1873             var li = [el, eventName, fn, wrappedFn];
1874
1875             var index = listeners.length;
1876             listeners[index] = li;
1877
1878             this.doAdd(el, eventName, wrappedFn, false);
1879             return true;
1880
1881         },
1882
1883
1884         removeListener: function(el, eventName, fn) {
1885             var i, len;
1886
1887             el = Roo.getDom(el);
1888
1889             if(!fn) {
1890                 return this.purgeElement(el, false, eventName);
1891             }
1892
1893
1894             if ("unload" == eventName) {
1895
1896                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1897                     var li = unloadListeners[i];
1898                     if (li &&
1899                         li[0] == el &&
1900                         li[1] == eventName &&
1901                         li[2] == fn) {
1902                         unloadListeners.splice(i, 1);
1903                         return true;
1904                     }
1905                 }
1906
1907                 return false;
1908             }
1909
1910             var cacheItem = null;
1911
1912
1913             var index = arguments[3];
1914
1915             if ("undefined" == typeof index) {
1916                 index = this._getCacheIndex(el, eventName, fn);
1917             }
1918
1919             if (index >= 0) {
1920                 cacheItem = listeners[index];
1921             }
1922
1923             if (!el || !cacheItem) {
1924                 return false;
1925             }
1926
1927             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1928
1929             delete listeners[index][this.WFN];
1930             delete listeners[index][this.FN];
1931             listeners.splice(index, 1);
1932
1933             return true;
1934
1935         },
1936
1937
1938         getTarget: function(ev, resolveTextNode) {
1939             ev = ev.browserEvent || ev;
1940             var t = ev.target || ev.srcElement;
1941             return this.resolveTextNode(t);
1942         },
1943
1944
1945         resolveTextNode: function(node) {
1946             if (Roo.isSafari && node && 3 == node.nodeType) {
1947                 return node.parentNode;
1948             } else {
1949                 return node;
1950             }
1951         },
1952
1953
1954         getPageX: function(ev) {
1955             ev = ev.browserEvent || ev;
1956             var x = ev.pageX;
1957             if (!x && 0 !== x) {
1958                 x = ev.clientX || 0;
1959
1960                 if (Roo.isIE) {
1961                     x += this.getScroll()[1];
1962                 }
1963             }
1964
1965             return x;
1966         },
1967
1968
1969         getPageY: function(ev) {
1970             ev = ev.browserEvent || ev;
1971             var y = ev.pageY;
1972             if (!y && 0 !== y) {
1973                 y = ev.clientY || 0;
1974
1975                 if (Roo.isIE) {
1976                     y += this.getScroll()[0];
1977                 }
1978             }
1979
1980
1981             return y;
1982         },
1983
1984
1985         getXY: function(ev) {
1986             ev = ev.browserEvent || ev;
1987             return [this.getPageX(ev), this.getPageY(ev)];
1988         },
1989
1990
1991         getRelatedTarget: function(ev) {
1992             ev = ev.browserEvent || ev;
1993             var t = ev.relatedTarget;
1994             if (!t) {
1995                 if (ev.type == "mouseout") {
1996                     t = ev.toElement;
1997                 } else if (ev.type == "mouseover") {
1998                     t = ev.fromElement;
1999                 }
2000             }
2001
2002             return this.resolveTextNode(t);
2003         },
2004
2005
2006         getTime: function(ev) {
2007             ev = ev.browserEvent || ev;
2008             if (!ev.time) {
2009                 var t = new Date().getTime();
2010                 try {
2011                     ev.time = t;
2012                 } catch(ex) {
2013                     this.lastError = ex;
2014                     return t;
2015                 }
2016             }
2017
2018             return ev.time;
2019         },
2020
2021
2022         stopEvent: function(ev) {
2023             this.stopPropagation(ev);
2024             this.preventDefault(ev);
2025         },
2026
2027
2028         stopPropagation: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             if (ev.stopPropagation) {
2031                 ev.stopPropagation();
2032             } else {
2033                 ev.cancelBubble = true;
2034             }
2035         },
2036
2037
2038         preventDefault: function(ev) {
2039             ev = ev.browserEvent || ev;
2040             if(ev.preventDefault) {
2041                 ev.preventDefault();
2042             } else {
2043                 ev.returnValue = false;
2044             }
2045         },
2046
2047
2048         getEvent: function(e) {
2049             var ev = e || window.event;
2050             if (!ev) {
2051                 var c = this.getEvent.caller;
2052                 while (c) {
2053                     ev = c.arguments[0];
2054                     if (ev && Event == ev.constructor) {
2055                         break;
2056                     }
2057                     c = c.caller;
2058                 }
2059             }
2060             return ev;
2061         },
2062
2063
2064         getCharCode: function(ev) {
2065             ev = ev.browserEvent || ev;
2066             return ev.charCode || ev.keyCode || 0;
2067         },
2068
2069
2070         _getCacheIndex: function(el, eventName, fn) {
2071             for (var i = 0,len = listeners.length; i < len; ++i) {
2072                 var li = listeners[i];
2073                 if (li &&
2074                     li[this.FN] == fn &&
2075                     li[this.EL] == el &&
2076                     li[this.TYPE] == eventName) {
2077                     return i;
2078                 }
2079             }
2080
2081             return -1;
2082         },
2083
2084
2085         elCache: {},
2086
2087
2088         getEl: function(id) {
2089             return document.getElementById(id);
2090         },
2091
2092
2093         clearCache: function() {
2094         },
2095
2096
2097         _load: function(e) {
2098             loadComplete = true;
2099             var EU = Roo.lib.Event;
2100
2101
2102             if (Roo.isIE) {
2103                 EU.doRemove(window, "load", EU._load);
2104             }
2105         },
2106
2107
2108         _tryPreloadAttach: function() {
2109
2110             if (this.locked) {
2111                 return false;
2112             }
2113
2114             this.locked = true;
2115
2116
2117             var tryAgain = !loadComplete;
2118             if (!tryAgain) {
2119                 tryAgain = (retryCount > 0);
2120             }
2121
2122
2123             var notAvail = [];
2124             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2125                 var item = onAvailStack[i];
2126                 if (item) {
2127                     var el = this.getEl(item.id);
2128
2129                     if (el) {
2130                         if (!item.checkReady ||
2131                             loadComplete ||
2132                             el.nextSibling ||
2133                             (document && document.body)) {
2134
2135                             var scope = el;
2136                             if (item.override) {
2137                                 if (item.override === true) {
2138                                     scope = item.obj;
2139                                 } else {
2140                                     scope = item.override;
2141                                 }
2142                             }
2143                             item.fn.call(scope, item.obj);
2144                             onAvailStack[i] = null;
2145                         }
2146                     } else {
2147                         notAvail.push(item);
2148                     }
2149                 }
2150             }
2151
2152             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2153
2154             if (tryAgain) {
2155
2156                 this.startInterval();
2157             } else {
2158                 clearInterval(this._interval);
2159                 this._interval = null;
2160             }
2161
2162             this.locked = false;
2163
2164             return true;
2165
2166         },
2167
2168
2169         purgeElement: function(el, recurse, eventName) {
2170             var elListeners = this.getListeners(el, eventName);
2171             if (elListeners) {
2172                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2173                     var l = elListeners[i];
2174                     this.removeListener(el, l.type, l.fn);
2175                 }
2176             }
2177
2178             if (recurse && el && el.childNodes) {
2179                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2180                     this.purgeElement(el.childNodes[i], recurse, eventName);
2181                 }
2182             }
2183         },
2184
2185
2186         getListeners: function(el, eventName) {
2187             var results = [], searchLists;
2188             if (!eventName) {
2189                 searchLists = [listeners, unloadListeners];
2190             } else if (eventName == "unload") {
2191                 searchLists = [unloadListeners];
2192             } else {
2193                 searchLists = [listeners];
2194             }
2195
2196             for (var j = 0; j < searchLists.length; ++j) {
2197                 var searchList = searchLists[j];
2198                 if (searchList && searchList.length > 0) {
2199                     for (var i = 0,len = searchList.length; i < len; ++i) {
2200                         var l = searchList[i];
2201                         if (l && l[this.EL] === el &&
2202                             (!eventName || eventName === l[this.TYPE])) {
2203                             results.push({
2204                                 type:   l[this.TYPE],
2205                                 fn:     l[this.FN],
2206                                 obj:    l[this.OBJ],
2207                                 adjust: l[this.ADJ_SCOPE],
2208                                 index:  i
2209                             });
2210                         }
2211                     }
2212                 }
2213             }
2214
2215             return (results.length) ? results : null;
2216         },
2217
2218
2219         _unload: function(e) {
2220
2221             var EU = Roo.lib.Event, i, j, l, len, index;
2222
2223             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2224                 l = unloadListeners[i];
2225                 if (l) {
2226                     var scope = window;
2227                     if (l[EU.ADJ_SCOPE]) {
2228                         if (l[EU.ADJ_SCOPE] === true) {
2229                             scope = l[EU.OBJ];
2230                         } else {
2231                             scope = l[EU.ADJ_SCOPE];
2232                         }
2233                     }
2234                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2235                     unloadListeners[i] = null;
2236                     l = null;
2237                     scope = null;
2238                 }
2239             }
2240
2241             unloadListeners = null;
2242
2243             if (listeners && listeners.length > 0) {
2244                 j = listeners.length;
2245                 while (j) {
2246                     index = j - 1;
2247                     l = listeners[index];
2248                     if (l) {
2249                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2250                                 l[EU.FN], index);
2251                     }
2252                     j = j - 1;
2253                 }
2254                 l = null;
2255
2256                 EU.clearCache();
2257             }
2258
2259             EU.doRemove(window, "unload", EU._unload);
2260
2261         },
2262
2263
2264         getScroll: function() {
2265             var dd = document.documentElement, db = document.body;
2266             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2267                 return [dd.scrollTop, dd.scrollLeft];
2268             } else if (db) {
2269                 return [db.scrollTop, db.scrollLeft];
2270             } else {
2271                 return [0, 0];
2272             }
2273         },
2274
2275
2276         doAdd: function () {
2277             if (window.addEventListener) {
2278                 return function(el, eventName, fn, capture) {
2279                     el.addEventListener(eventName, fn, (capture));
2280                 };
2281             } else if (window.attachEvent) {
2282                 return function(el, eventName, fn, capture) {
2283                     el.attachEvent("on" + eventName, fn);
2284                 };
2285             } else {
2286                 return function() {
2287                 };
2288             }
2289         }(),
2290
2291
2292         doRemove: function() {
2293             if (window.removeEventListener) {
2294                 return function (el, eventName, fn, capture) {
2295                     el.removeEventListener(eventName, fn, (capture));
2296                 };
2297             } else if (window.detachEvent) {
2298                 return function (el, eventName, fn) {
2299                     el.detachEvent("on" + eventName, fn);
2300                 };
2301             } else {
2302                 return function() {
2303                 };
2304             }
2305         }()
2306     };
2307     
2308 }();
2309 (function() {     
2310    
2311     var E = Roo.lib.Event;
2312     E.on = E.addListener;
2313     E.un = E.removeListener;
2314
2315     if (document && document.body) {
2316         E._load();
2317     } else {
2318         E.doAdd(window, "load", E._load);
2319     }
2320     E.doAdd(window, "unload", E._unload);
2321     E._tryPreloadAttach();
2322 })();
2323
2324 /*
2325  * Portions of this file are based on pieces of Yahoo User Interface Library
2326  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2327  * YUI licensed under the BSD License:
2328  * http://developer.yahoo.net/yui/license.txt
2329  * <script type="text/javascript">
2330  *
2331  */
2332
2333 (function() {
2334     
2335     Roo.lib.Ajax = {
2336         request : function(method, uri, cb, data, options) {
2337             if(options){
2338                 var hs = options.headers;
2339                 if(hs){
2340                     for(var h in hs){
2341                         if(hs.hasOwnProperty(h)){
2342                             this.initHeader(h, hs[h], false);
2343                         }
2344                     }
2345                 }
2346                 if(options.xmlData){
2347                     this.initHeader('Content-Type', 'text/xml', false);
2348                     method = 'POST';
2349                     data = options.xmlData;
2350                 }
2351             }
2352
2353             return this.asyncRequest(method, uri, cb, data);
2354         },
2355
2356         serializeForm : function(form) {
2357             if(typeof form == 'string') {
2358                 form = (document.getElementById(form) || document.forms[form]);
2359             }
2360
2361             var el, name, val, disabled, data = '', hasSubmit = false;
2362             for (var i = 0; i < form.elements.length; i++) {
2363                 el = form.elements[i];
2364                 disabled = form.elements[i].disabled;
2365                 name = form.elements[i].name;
2366                 val = form.elements[i].value;
2367
2368                 if (!disabled && name){
2369                     switch (el.type)
2370                             {
2371                         case 'select-one':
2372                         case 'select-multiple':
2373                             for (var j = 0; j < el.options.length; j++) {
2374                                 if (el.options[j].selected) {
2375                                     if (Roo.isIE) {
2376                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2377                                     }
2378                                     else {
2379                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2380                                     }
2381                                 }
2382                             }
2383                             break;
2384                         case 'radio':
2385                         case 'checkbox':
2386                             if (el.checked) {
2387                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2388                             }
2389                             break;
2390                         case 'file':
2391
2392                         case undefined:
2393
2394                         case 'reset':
2395
2396                         case 'button':
2397
2398                             break;
2399                         case 'submit':
2400                             if(hasSubmit == false) {
2401                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2402                                 hasSubmit = true;
2403                             }
2404                             break;
2405                         default:
2406                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2407                             break;
2408                     }
2409                 }
2410             }
2411             data = data.substr(0, data.length - 1);
2412             return data;
2413         },
2414
2415         headers:{},
2416
2417         hasHeaders:false,
2418
2419         useDefaultHeader:true,
2420
2421         defaultPostHeader:'application/x-www-form-urlencoded',
2422
2423         useDefaultXhrHeader:true,
2424
2425         defaultXhrHeader:'XMLHttpRequest',
2426
2427         hasDefaultHeaders:true,
2428
2429         defaultHeaders:{},
2430
2431         poll:{},
2432
2433         timeout:{},
2434
2435         pollInterval:50,
2436
2437         transactionId:0,
2438
2439         setProgId:function(id)
2440         {
2441             this.activeX.unshift(id);
2442         },
2443
2444         setDefaultPostHeader:function(b)
2445         {
2446             this.useDefaultHeader = b;
2447         },
2448
2449         setDefaultXhrHeader:function(b)
2450         {
2451             this.useDefaultXhrHeader = b;
2452         },
2453
2454         setPollingInterval:function(i)
2455         {
2456             if (typeof i == 'number' && isFinite(i)) {
2457                 this.pollInterval = i;
2458             }
2459         },
2460
2461         createXhrObject:function(transactionId)
2462         {
2463             var obj,http;
2464             try
2465             {
2466
2467                 http = new XMLHttpRequest();
2468
2469                 obj = { conn:http, tId:transactionId };
2470             }
2471             catch(e)
2472             {
2473                 for (var i = 0; i < this.activeX.length; ++i) {
2474                     try
2475                     {
2476
2477                         http = new ActiveXObject(this.activeX[i]);
2478
2479                         obj = { conn:http, tId:transactionId };
2480                         break;
2481                     }
2482                     catch(e) {
2483                     }
2484                 }
2485             }
2486             finally
2487             {
2488                 return obj;
2489             }
2490         },
2491
2492         getConnectionObject:function()
2493         {
2494             var o;
2495             var tId = this.transactionId;
2496
2497             try
2498             {
2499                 o = this.createXhrObject(tId);
2500                 if (o) {
2501                     this.transactionId++;
2502                 }
2503             }
2504             catch(e) {
2505             }
2506             finally
2507             {
2508                 return o;
2509             }
2510         },
2511
2512         asyncRequest:function(method, uri, callback, postData)
2513         {
2514             var o = this.getConnectionObject();
2515
2516             if (!o) {
2517                 return null;
2518             }
2519             else {
2520                 o.conn.open(method, uri, true);
2521
2522                 if (this.useDefaultXhrHeader) {
2523                     if (!this.defaultHeaders['X-Requested-With']) {
2524                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2525                     }
2526                 }
2527
2528                 if(postData && this.useDefaultHeader){
2529                     this.initHeader('Content-Type', this.defaultPostHeader);
2530                 }
2531
2532                  if (this.hasDefaultHeaders || this.hasHeaders) {
2533                     this.setHeader(o);
2534                 }
2535
2536                 this.handleReadyState(o, callback);
2537                 o.conn.send(postData || null);
2538
2539                 return o;
2540             }
2541         },
2542
2543         handleReadyState:function(o, callback)
2544         {
2545             var oConn = this;
2546
2547             if (callback && callback.timeout) {
2548                 this.timeout[o.tId] = window.setTimeout(function() {
2549                     oConn.abort(o, callback, true);
2550                 }, callback.timeout);
2551             }
2552
2553             this.poll[o.tId] = window.setInterval(
2554                     function() {
2555                         if (o.conn && o.conn.readyState == 4) {
2556                             window.clearInterval(oConn.poll[o.tId]);
2557                             delete oConn.poll[o.tId];
2558
2559                             if(callback && callback.timeout) {
2560                                 window.clearTimeout(oConn.timeout[o.tId]);
2561                                 delete oConn.timeout[o.tId];
2562                             }
2563
2564                             oConn.handleTransactionResponse(o, callback);
2565                         }
2566                     }
2567                     , this.pollInterval);
2568         },
2569
2570         handleTransactionResponse:function(o, callback, isAbort)
2571         {
2572
2573             if (!callback) {
2574                 this.releaseObject(o);
2575                 return;
2576             }
2577
2578             var httpStatus, responseObject;
2579
2580             try
2581             {
2582                 if (o.conn.status !== undefined && o.conn.status != 0) {
2583                     httpStatus = o.conn.status;
2584                 }
2585                 else {
2586                     httpStatus = 13030;
2587                 }
2588             }
2589             catch(e) {
2590
2591
2592                 httpStatus = 13030;
2593             }
2594
2595             if (httpStatus >= 200 && httpStatus < 300) {
2596                 responseObject = this.createResponseObject(o, callback.argument);
2597                 if (callback.success) {
2598                     if (!callback.scope) {
2599                         callback.success(responseObject);
2600                     }
2601                     else {
2602
2603
2604                         callback.success.apply(callback.scope, [responseObject]);
2605                     }
2606                 }
2607             }
2608             else {
2609                 switch (httpStatus) {
2610
2611                     case 12002:
2612                     case 12029:
2613                     case 12030:
2614                     case 12031:
2615                     case 12152:
2616                     case 13030:
2617                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2618                         if (callback.failure) {
2619                             if (!callback.scope) {
2620                                 callback.failure(responseObject);
2621                             }
2622                             else {
2623                                 callback.failure.apply(callback.scope, [responseObject]);
2624                             }
2625                         }
2626                         break;
2627                     default:
2628                         responseObject = this.createResponseObject(o, callback.argument);
2629                         if (callback.failure) {
2630                             if (!callback.scope) {
2631                                 callback.failure(responseObject);
2632                             }
2633                             else {
2634                                 callback.failure.apply(callback.scope, [responseObject]);
2635                             }
2636                         }
2637                 }
2638             }
2639
2640             this.releaseObject(o);
2641             responseObject = null;
2642         },
2643
2644         createResponseObject:function(o, callbackArg)
2645         {
2646             var obj = {};
2647             var headerObj = {};
2648
2649             try
2650             {
2651                 var headerStr = o.conn.getAllResponseHeaders();
2652                 var header = headerStr.split('\n');
2653                 for (var i = 0; i < header.length; i++) {
2654                     var delimitPos = header[i].indexOf(':');
2655                     if (delimitPos != -1) {
2656                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2657                     }
2658                 }
2659             }
2660             catch(e) {
2661             }
2662
2663             obj.tId = o.tId;
2664             obj.status = o.conn.status;
2665             obj.statusText = o.conn.statusText;
2666             obj.getResponseHeader = headerObj;
2667             obj.getAllResponseHeaders = headerStr;
2668             obj.responseText = o.conn.responseText;
2669             obj.responseXML = o.conn.responseXML;
2670
2671             if (typeof callbackArg !== undefined) {
2672                 obj.argument = callbackArg;
2673             }
2674
2675             return obj;
2676         },
2677
2678         createExceptionObject:function(tId, callbackArg, isAbort)
2679         {
2680             var COMM_CODE = 0;
2681             var COMM_ERROR = 'communication failure';
2682             var ABORT_CODE = -1;
2683             var ABORT_ERROR = 'transaction aborted';
2684
2685             var obj = {};
2686
2687             obj.tId = tId;
2688             if (isAbort) {
2689                 obj.status = ABORT_CODE;
2690                 obj.statusText = ABORT_ERROR;
2691             }
2692             else {
2693                 obj.status = COMM_CODE;
2694                 obj.statusText = COMM_ERROR;
2695             }
2696
2697             if (callbackArg) {
2698                 obj.argument = callbackArg;
2699             }
2700
2701             return obj;
2702         },
2703
2704         initHeader:function(label, value, isDefault)
2705         {
2706             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2707
2708             if (headerObj[label] === undefined) {
2709                 headerObj[label] = value;
2710             }
2711             else {
2712
2713
2714                 headerObj[label] = value + "," + headerObj[label];
2715             }
2716
2717             if (isDefault) {
2718                 this.hasDefaultHeaders = true;
2719             }
2720             else {
2721                 this.hasHeaders = true;
2722             }
2723         },
2724
2725
2726         setHeader:function(o)
2727         {
2728             if (this.hasDefaultHeaders) {
2729                 for (var prop in this.defaultHeaders) {
2730                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2731                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2732                     }
2733                 }
2734             }
2735
2736             if (this.hasHeaders) {
2737                 for (var prop in this.headers) {
2738                     if (this.headers.hasOwnProperty(prop)) {
2739                         o.conn.setRequestHeader(prop, this.headers[prop]);
2740                     }
2741                 }
2742                 this.headers = {};
2743                 this.hasHeaders = false;
2744             }
2745         },
2746
2747         resetDefaultHeaders:function() {
2748             delete this.defaultHeaders;
2749             this.defaultHeaders = {};
2750             this.hasDefaultHeaders = false;
2751         },
2752
2753         abort:function(o, callback, isTimeout)
2754         {
2755             if(this.isCallInProgress(o)) {
2756                 o.conn.abort();
2757                 window.clearInterval(this.poll[o.tId]);
2758                 delete this.poll[o.tId];
2759                 if (isTimeout) {
2760                     delete this.timeout[o.tId];
2761                 }
2762
2763                 this.handleTransactionResponse(o, callback, true);
2764
2765                 return true;
2766             }
2767             else {
2768                 return false;
2769             }
2770         },
2771
2772
2773         isCallInProgress:function(o)
2774         {
2775             if (o && o.conn) {
2776                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2777             }
2778             else {
2779
2780                 return false;
2781             }
2782         },
2783
2784
2785         releaseObject:function(o)
2786         {
2787
2788             o.conn = null;
2789
2790             o = null;
2791         },
2792
2793         activeX:[
2794         'MSXML2.XMLHTTP.3.0',
2795         'MSXML2.XMLHTTP',
2796         'Microsoft.XMLHTTP'
2797         ]
2798
2799
2800     };
2801 })();/*
2802  * Portions of this file are based on pieces of Yahoo User Interface Library
2803  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2804  * YUI licensed under the BSD License:
2805  * http://developer.yahoo.net/yui/license.txt
2806  * <script type="text/javascript">
2807  *
2808  */
2809
2810 Roo.lib.Region = function(t, r, b, l) {
2811     this.top = t;
2812     this[1] = t;
2813     this.right = r;
2814     this.bottom = b;
2815     this.left = l;
2816     this[0] = l;
2817 };
2818
2819
2820 Roo.lib.Region.prototype = {
2821     contains : function(region) {
2822         return ( region.left >= this.left &&
2823                  region.right <= this.right &&
2824                  region.top >= this.top &&
2825                  region.bottom <= this.bottom    );
2826
2827     },
2828
2829     getArea : function() {
2830         return ( (this.bottom - this.top) * (this.right - this.left) );
2831     },
2832
2833     intersect : function(region) {
2834         var t = Math.max(this.top, region.top);
2835         var r = Math.min(this.right, region.right);
2836         var b = Math.min(this.bottom, region.bottom);
2837         var l = Math.max(this.left, region.left);
2838
2839         if (b >= t && r >= l) {
2840             return new Roo.lib.Region(t, r, b, l);
2841         } else {
2842             return null;
2843         }
2844     },
2845     union : function(region) {
2846         var t = Math.min(this.top, region.top);
2847         var r = Math.max(this.right, region.right);
2848         var b = Math.max(this.bottom, region.bottom);
2849         var l = Math.min(this.left, region.left);
2850
2851         return new Roo.lib.Region(t, r, b, l);
2852     },
2853
2854     adjust : function(t, l, b, r) {
2855         this.top += t;
2856         this.left += l;
2857         this.right += r;
2858         this.bottom += b;
2859         return this;
2860     }
2861 };
2862
2863 Roo.lib.Region.getRegion = function(el) {
2864     var p = Roo.lib.Dom.getXY(el);
2865
2866     var t = p[1];
2867     var r = p[0] + el.offsetWidth;
2868     var b = p[1] + el.offsetHeight;
2869     var l = p[0];
2870
2871     return new Roo.lib.Region(t, r, b, l);
2872 };
2873 /*
2874  * Portions of this file are based on pieces of Yahoo User Interface Library
2875  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2876  * YUI licensed under the BSD License:
2877  * http://developer.yahoo.net/yui/license.txt
2878  * <script type="text/javascript">
2879  *
2880  */
2881 //@@dep Roo.lib.Region
2882
2883
2884 Roo.lib.Point = function(x, y) {
2885     if (x instanceof Array) {
2886         y = x[1];
2887         x = x[0];
2888     }
2889     this.x = this.right = this.left = this[0] = x;
2890     this.y = this.top = this.bottom = this[1] = y;
2891 };
2892
2893 Roo.lib.Point.prototype = new Roo.lib.Region();
2894 /*
2895  * Portions of this file are based on pieces of Yahoo User Interface Library
2896  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2897  * YUI licensed under the BSD License:
2898  * http://developer.yahoo.net/yui/license.txt
2899  * <script type="text/javascript">
2900  *
2901  */
2902  
2903 (function() {   
2904
2905     Roo.lib.Anim = {
2906         scroll : function(el, args, duration, easing, cb, scope) {
2907             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2908         },
2909
2910         motion : function(el, args, duration, easing, cb, scope) {
2911             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2912         },
2913
2914         color : function(el, args, duration, easing, cb, scope) {
2915             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2916         },
2917
2918         run : function(el, args, duration, easing, cb, scope, type) {
2919             type = type || Roo.lib.AnimBase;
2920             if (typeof easing == "string") {
2921                 easing = Roo.lib.Easing[easing];
2922             }
2923             var anim = new type(el, args, duration, easing);
2924             anim.animateX(function() {
2925                 Roo.callback(cb, scope);
2926             });
2927             return anim;
2928         }
2929     };
2930 })();/*
2931  * Portions of this file are based on pieces of Yahoo User Interface Library
2932  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2933  * YUI licensed under the BSD License:
2934  * http://developer.yahoo.net/yui/license.txt
2935  * <script type="text/javascript">
2936  *
2937  */
2938
2939 (function() {    
2940     var libFlyweight;
2941     
2942     function fly(el) {
2943         if (!libFlyweight) {
2944             libFlyweight = new Roo.Element.Flyweight();
2945         }
2946         libFlyweight.dom = el;
2947         return libFlyweight;
2948     }
2949
2950     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2951     
2952    
2953     
2954     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2955         if (el) {
2956             this.init(el, attributes, duration, method);
2957         }
2958     };
2959
2960     Roo.lib.AnimBase.fly = fly;
2961     
2962     
2963     
2964     Roo.lib.AnimBase.prototype = {
2965
2966         toString: function() {
2967             var el = this.getEl();
2968             var id = el.id || el.tagName;
2969             return ("Anim " + id);
2970         },
2971
2972         patterns: {
2973             noNegatives:        /width|height|opacity|padding/i,
2974             offsetAttribute:  /^((width|height)|(top|left))$/,
2975             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2976             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2977         },
2978
2979
2980         doMethod: function(attr, start, end) {
2981             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2982         },
2983
2984
2985         setAttribute: function(attr, val, unit) {
2986             if (this.patterns.noNegatives.test(attr)) {
2987                 val = (val > 0) ? val : 0;
2988             }
2989
2990             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2991         },
2992
2993
2994         getAttribute: function(attr) {
2995             var el = this.getEl();
2996             var val = fly(el).getStyle(attr);
2997
2998             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2999                 return parseFloat(val);
3000             }
3001
3002             var a = this.patterns.offsetAttribute.exec(attr) || [];
3003             var pos = !!( a[3] );
3004             var box = !!( a[2] );
3005
3006
3007             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3008                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3009             } else {
3010                 val = 0;
3011             }
3012
3013             return val;
3014         },
3015
3016
3017         getDefaultUnit: function(attr) {
3018             if (this.patterns.defaultUnit.test(attr)) {
3019                 return 'px';
3020             }
3021
3022             return '';
3023         },
3024
3025         animateX : function(callback, scope) {
3026             var f = function() {
3027                 this.onComplete.removeListener(f);
3028                 if (typeof callback == "function") {
3029                     callback.call(scope || this, this);
3030                 }
3031             };
3032             this.onComplete.addListener(f, this);
3033             this.animate();
3034         },
3035
3036
3037         setRuntimeAttribute: function(attr) {
3038             var start;
3039             var end;
3040             var attributes = this.attributes;
3041
3042             this.runtimeAttributes[attr] = {};
3043
3044             var isset = function(prop) {
3045                 return (typeof prop !== 'undefined');
3046             };
3047
3048             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3049                 return false;
3050             }
3051
3052             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3053
3054
3055             if (isset(attributes[attr]['to'])) {
3056                 end = attributes[attr]['to'];
3057             } else if (isset(attributes[attr]['by'])) {
3058                 if (start.constructor == Array) {
3059                     end = [];
3060                     for (var i = 0, len = start.length; i < len; ++i) {
3061                         end[i] = start[i] + attributes[attr]['by'][i];
3062                     }
3063                 } else {
3064                     end = start + attributes[attr]['by'];
3065                 }
3066             }
3067
3068             this.runtimeAttributes[attr].start = start;
3069             this.runtimeAttributes[attr].end = end;
3070
3071
3072             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3073         },
3074
3075
3076         init: function(el, attributes, duration, method) {
3077
3078             var isAnimated = false;
3079
3080
3081             var startTime = null;
3082
3083
3084             var actualFrames = 0;
3085
3086
3087             el = Roo.getDom(el);
3088
3089
3090             this.attributes = attributes || {};
3091
3092
3093             this.duration = duration || 1;
3094
3095
3096             this.method = method || Roo.lib.Easing.easeNone;
3097
3098
3099             this.useSeconds = true;
3100
3101
3102             this.currentFrame = 0;
3103
3104
3105             this.totalFrames = Roo.lib.AnimMgr.fps;
3106
3107
3108             this.getEl = function() {
3109                 return el;
3110             };
3111
3112
3113             this.isAnimated = function() {
3114                 return isAnimated;
3115             };
3116
3117
3118             this.getStartTime = function() {
3119                 return startTime;
3120             };
3121
3122             this.runtimeAttributes = {};
3123
3124
3125             this.animate = function() {
3126                 if (this.isAnimated()) {
3127                     return false;
3128                 }
3129
3130                 this.currentFrame = 0;
3131
3132                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3133
3134                 Roo.lib.AnimMgr.registerElement(this);
3135             };
3136
3137
3138             this.stop = function(finish) {
3139                 if (finish) {
3140                     this.currentFrame = this.totalFrames;
3141                     this._onTween.fire();
3142                 }
3143                 Roo.lib.AnimMgr.stop(this);
3144             };
3145
3146             var onStart = function() {
3147                 this.onStart.fire();
3148
3149                 this.runtimeAttributes = {};
3150                 for (var attr in this.attributes) {
3151                     this.setRuntimeAttribute(attr);
3152                 }
3153
3154                 isAnimated = true;
3155                 actualFrames = 0;
3156                 startTime = new Date();
3157             };
3158
3159
3160             var onTween = function() {
3161                 var data = {
3162                     duration: new Date() - this.getStartTime(),
3163                     currentFrame: this.currentFrame
3164                 };
3165
3166                 data.toString = function() {
3167                     return (
3168                             'duration: ' + data.duration +
3169                             ', currentFrame: ' + data.currentFrame
3170                             );
3171                 };
3172
3173                 this.onTween.fire(data);
3174
3175                 var runtimeAttributes = this.runtimeAttributes;
3176
3177                 for (var attr in runtimeAttributes) {
3178                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3179                 }
3180
3181                 actualFrames += 1;
3182             };
3183
3184             var onComplete = function() {
3185                 var actual_duration = (new Date() - startTime) / 1000 ;
3186
3187                 var data = {
3188                     duration: actual_duration,
3189                     frames: actualFrames,
3190                     fps: actualFrames / actual_duration
3191                 };
3192
3193                 data.toString = function() {
3194                     return (
3195                             'duration: ' + data.duration +
3196                             ', frames: ' + data.frames +
3197                             ', fps: ' + data.fps
3198                             );
3199                 };
3200
3201                 isAnimated = false;
3202                 actualFrames = 0;
3203                 this.onComplete.fire(data);
3204             };
3205
3206
3207             this._onStart = new Roo.util.Event(this);
3208             this.onStart = new Roo.util.Event(this);
3209             this.onTween = new Roo.util.Event(this);
3210             this._onTween = new Roo.util.Event(this);
3211             this.onComplete = new Roo.util.Event(this);
3212             this._onComplete = new Roo.util.Event(this);
3213             this._onStart.addListener(onStart);
3214             this._onTween.addListener(onTween);
3215             this._onComplete.addListener(onComplete);
3216         }
3217     };
3218 })();
3219 /*
3220  * Portions of this file are based on pieces of Yahoo User Interface Library
3221  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3222  * YUI licensed under the BSD License:
3223  * http://developer.yahoo.net/yui/license.txt
3224  * <script type="text/javascript">
3225  *
3226  */
3227
3228 Roo.lib.AnimMgr = new function() {
3229
3230         var thread = null;
3231
3232
3233         var queue = [];
3234
3235
3236         var tweenCount = 0;
3237
3238
3239         this.fps = 1000;
3240
3241
3242         this.delay = 1;
3243
3244
3245         this.registerElement = function(tween) {
3246             queue[queue.length] = tween;
3247             tweenCount += 1;
3248             tween._onStart.fire();
3249             this.start();
3250         };
3251
3252
3253         this.unRegister = function(tween, index) {
3254             tween._onComplete.fire();
3255             index = index || getIndex(tween);
3256             if (index != -1) {
3257                 queue.splice(index, 1);
3258             }
3259
3260             tweenCount -= 1;
3261             if (tweenCount <= 0) {
3262                 this.stop();
3263             }
3264         };
3265
3266
3267         this.start = function() {
3268             if (thread === null) {
3269                 thread = setInterval(this.run, this.delay);
3270             }
3271         };
3272
3273
3274         this.stop = function(tween) {
3275             if (!tween) {
3276                 clearInterval(thread);
3277
3278                 for (var i = 0, len = queue.length; i < len; ++i) {
3279                     if (queue[0].isAnimated()) {
3280                         this.unRegister(queue[0], 0);
3281                     }
3282                 }
3283
3284                 queue = [];
3285                 thread = null;
3286                 tweenCount = 0;
3287             }
3288             else {
3289                 this.unRegister(tween);
3290             }
3291         };
3292
3293
3294         this.run = function() {
3295             for (var i = 0, len = queue.length; i < len; ++i) {
3296                 var tween = queue[i];
3297                 if (!tween || !tween.isAnimated()) {
3298                     continue;
3299                 }
3300
3301                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3302                 {
3303                     tween.currentFrame += 1;
3304
3305                     if (tween.useSeconds) {
3306                         correctFrame(tween);
3307                     }
3308                     tween._onTween.fire();
3309                 }
3310                 else {
3311                     Roo.lib.AnimMgr.stop(tween, i);
3312                 }
3313             }
3314         };
3315
3316         var getIndex = function(anim) {
3317             for (var i = 0, len = queue.length; i < len; ++i) {
3318                 if (queue[i] == anim) {
3319                     return i;
3320                 }
3321             }
3322             return -1;
3323         };
3324
3325
3326         var correctFrame = function(tween) {
3327             var frames = tween.totalFrames;
3328             var frame = tween.currentFrame;
3329             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3330             var elapsed = (new Date() - tween.getStartTime());
3331             var tweak = 0;
3332
3333             if (elapsed < tween.duration * 1000) {
3334                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3335             } else {
3336                 tweak = frames - (frame + 1);
3337             }
3338             if (tweak > 0 && isFinite(tweak)) {
3339                 if (tween.currentFrame + tweak >= frames) {
3340                     tweak = frames - (frame + 1);
3341                 }
3342
3343                 tween.currentFrame += tweak;
3344             }
3345         };
3346     };/*
3347  * Portions of this file are based on pieces of Yahoo User Interface Library
3348  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3349  * YUI licensed under the BSD License:
3350  * http://developer.yahoo.net/yui/license.txt
3351  * <script type="text/javascript">
3352  *
3353  */
3354 Roo.lib.Bezier = new function() {
3355
3356         this.getPosition = function(points, t) {
3357             var n = points.length;
3358             var tmp = [];
3359
3360             for (var i = 0; i < n; ++i) {
3361                 tmp[i] = [points[i][0], points[i][1]];
3362             }
3363
3364             for (var j = 1; j < n; ++j) {
3365                 for (i = 0; i < n - j; ++i) {
3366                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3367                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3368                 }
3369             }
3370
3371             return [ tmp[0][0], tmp[0][1] ];
3372
3373         };
3374     };/*
3375  * Portions of this file are based on pieces of Yahoo User Interface Library
3376  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3377  * YUI licensed under the BSD License:
3378  * http://developer.yahoo.net/yui/license.txt
3379  * <script type="text/javascript">
3380  *
3381  */
3382 (function() {
3383
3384     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3385         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3386     };
3387
3388     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3389
3390     var fly = Roo.lib.AnimBase.fly;
3391     var Y = Roo.lib;
3392     var superclass = Y.ColorAnim.superclass;
3393     var proto = Y.ColorAnim.prototype;
3394
3395     proto.toString = function() {
3396         var el = this.getEl();
3397         var id = el.id || el.tagName;
3398         return ("ColorAnim " + id);
3399     };
3400
3401     proto.patterns.color = /color$/i;
3402     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3403     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3404     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3405     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3406
3407
3408     proto.parseColor = function(s) {
3409         if (s.length == 3) {
3410             return s;
3411         }
3412
3413         var c = this.patterns.hex.exec(s);
3414         if (c && c.length == 4) {
3415             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3416         }
3417
3418         c = this.patterns.rgb.exec(s);
3419         if (c && c.length == 4) {
3420             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3421         }
3422
3423         c = this.patterns.hex3.exec(s);
3424         if (c && c.length == 4) {
3425             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3426         }
3427
3428         return null;
3429     };
3430     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3431     proto.getAttribute = function(attr) {
3432         var el = this.getEl();
3433         if (this.patterns.color.test(attr)) {
3434             var val = fly(el).getStyle(attr);
3435
3436             if (this.patterns.transparent.test(val)) {
3437                 var parent = el.parentNode;
3438                 val = fly(parent).getStyle(attr);
3439
3440                 while (parent && this.patterns.transparent.test(val)) {
3441                     parent = parent.parentNode;
3442                     val = fly(parent).getStyle(attr);
3443                     if (parent.tagName.toUpperCase() == 'HTML') {
3444                         val = '#fff';
3445                     }
3446                 }
3447             }
3448         } else {
3449             val = superclass.getAttribute.call(this, attr);
3450         }
3451
3452         return val;
3453     };
3454     proto.getAttribute = function(attr) {
3455         var el = this.getEl();
3456         if (this.patterns.color.test(attr)) {
3457             var val = fly(el).getStyle(attr);
3458
3459             if (this.patterns.transparent.test(val)) {
3460                 var parent = el.parentNode;
3461                 val = fly(parent).getStyle(attr);
3462
3463                 while (parent && this.patterns.transparent.test(val)) {
3464                     parent = parent.parentNode;
3465                     val = fly(parent).getStyle(attr);
3466                     if (parent.tagName.toUpperCase() == 'HTML') {
3467                         val = '#fff';
3468                     }
3469                 }
3470             }
3471         } else {
3472             val = superclass.getAttribute.call(this, attr);
3473         }
3474
3475         return val;
3476     };
3477
3478     proto.doMethod = function(attr, start, end) {
3479         var val;
3480
3481         if (this.patterns.color.test(attr)) {
3482             val = [];
3483             for (var i = 0, len = start.length; i < len; ++i) {
3484                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3485             }
3486
3487             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3488         }
3489         else {
3490             val = superclass.doMethod.call(this, attr, start, end);
3491         }
3492
3493         return val;
3494     };
3495
3496     proto.setRuntimeAttribute = function(attr) {
3497         superclass.setRuntimeAttribute.call(this, attr);
3498
3499         if (this.patterns.color.test(attr)) {
3500             var attributes = this.attributes;
3501             var start = this.parseColor(this.runtimeAttributes[attr].start);
3502             var end = this.parseColor(this.runtimeAttributes[attr].end);
3503
3504             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3505                 end = this.parseColor(attributes[attr].by);
3506
3507                 for (var i = 0, len = start.length; i < len; ++i) {
3508                     end[i] = start[i] + end[i];
3509                 }
3510             }
3511
3512             this.runtimeAttributes[attr].start = start;
3513             this.runtimeAttributes[attr].end = end;
3514         }
3515     };
3516 })();
3517
3518 /*
3519  * Portions of this file are based on pieces of Yahoo User Interface Library
3520  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3521  * YUI licensed under the BSD License:
3522  * http://developer.yahoo.net/yui/license.txt
3523  * <script type="text/javascript">
3524  *
3525  */
3526 Roo.lib.Easing = {
3527
3528
3529     easeNone: function (t, b, c, d) {
3530         return c * t / d + b;
3531     },
3532
3533
3534     easeIn: function (t, b, c, d) {
3535         return c * (t /= d) * t + b;
3536     },
3537
3538
3539     easeOut: function (t, b, c, d) {
3540         return -c * (t /= d) * (t - 2) + b;
3541     },
3542
3543
3544     easeBoth: function (t, b, c, d) {
3545         if ((t /= d / 2) < 1) {
3546             return c / 2 * t * t + b;
3547         }
3548
3549         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3550     },
3551
3552
3553     easeInStrong: function (t, b, c, d) {
3554         return c * (t /= d) * t * t * t + b;
3555     },
3556
3557
3558     easeOutStrong: function (t, b, c, d) {
3559         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3560     },
3561
3562
3563     easeBothStrong: function (t, b, c, d) {
3564         if ((t /= d / 2) < 1) {
3565             return c / 2 * t * t * t * t + b;
3566         }
3567
3568         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3569     },
3570
3571
3572
3573     elasticIn: function (t, b, c, d, a, p) {
3574         if (t == 0) {
3575             return b;
3576         }
3577         if ((t /= d) == 1) {
3578             return b + c;
3579         }
3580         if (!p) {
3581             p = d * .3;
3582         }
3583
3584         if (!a || a < Math.abs(c)) {
3585             a = c;
3586             var s = p / 4;
3587         }
3588         else {
3589             var s = p / (2 * Math.PI) * Math.asin(c / a);
3590         }
3591
3592         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3593     },
3594
3595
3596     elasticOut: function (t, b, c, d, a, p) {
3597         if (t == 0) {
3598             return b;
3599         }
3600         if ((t /= d) == 1) {
3601             return b + c;
3602         }
3603         if (!p) {
3604             p = d * .3;
3605         }
3606
3607         if (!a || a < Math.abs(c)) {
3608             a = c;
3609             var s = p / 4;
3610         }
3611         else {
3612             var s = p / (2 * Math.PI) * Math.asin(c / a);
3613         }
3614
3615         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3616     },
3617
3618
3619     elasticBoth: function (t, b, c, d, a, p) {
3620         if (t == 0) {
3621             return b;
3622         }
3623
3624         if ((t /= d / 2) == 2) {
3625             return b + c;
3626         }
3627
3628         if (!p) {
3629             p = d * (.3 * 1.5);
3630         }
3631
3632         if (!a || a < Math.abs(c)) {
3633             a = c;
3634             var s = p / 4;
3635         }
3636         else {
3637             var s = p / (2 * Math.PI) * Math.asin(c / a);
3638         }
3639
3640         if (t < 1) {
3641             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3642                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3643         }
3644         return a * Math.pow(2, -10 * (t -= 1)) *
3645                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3646     },
3647
3648
3649
3650     backIn: function (t, b, c, d, s) {
3651         if (typeof s == 'undefined') {
3652             s = 1.70158;
3653         }
3654         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3655     },
3656
3657
3658     backOut: function (t, b, c, d, s) {
3659         if (typeof s == 'undefined') {
3660             s = 1.70158;
3661         }
3662         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3663     },
3664
3665
3666     backBoth: function (t, b, c, d, s) {
3667         if (typeof s == 'undefined') {
3668             s = 1.70158;
3669         }
3670
3671         if ((t /= d / 2 ) < 1) {
3672             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3673         }
3674         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3675     },
3676
3677
3678     bounceIn: function (t, b, c, d) {
3679         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3680     },
3681
3682
3683     bounceOut: function (t, b, c, d) {
3684         if ((t /= d) < (1 / 2.75)) {
3685             return c * (7.5625 * t * t) + b;
3686         } else if (t < (2 / 2.75)) {
3687             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3688         } else if (t < (2.5 / 2.75)) {
3689             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3690         }
3691         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3692     },
3693
3694
3695     bounceBoth: function (t, b, c, d) {
3696         if (t < d / 2) {
3697             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3698         }
3699         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3700     }
3701 };/*
3702  * Portions of this file are based on pieces of Yahoo User Interface Library
3703  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3704  * YUI licensed under the BSD License:
3705  * http://developer.yahoo.net/yui/license.txt
3706  * <script type="text/javascript">
3707  *
3708  */
3709     (function() {
3710         Roo.lib.Motion = function(el, attributes, duration, method) {
3711             if (el) {
3712                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3713             }
3714         };
3715
3716         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3717
3718
3719         var Y = Roo.lib;
3720         var superclass = Y.Motion.superclass;
3721         var proto = Y.Motion.prototype;
3722
3723         proto.toString = function() {
3724             var el = this.getEl();
3725             var id = el.id || el.tagName;
3726             return ("Motion " + id);
3727         };
3728
3729         proto.patterns.points = /^points$/i;
3730
3731         proto.setAttribute = function(attr, val, unit) {
3732             if (this.patterns.points.test(attr)) {
3733                 unit = unit || 'px';
3734                 superclass.setAttribute.call(this, 'left', val[0], unit);
3735                 superclass.setAttribute.call(this, 'top', val[1], unit);
3736             } else {
3737                 superclass.setAttribute.call(this, attr, val, unit);
3738             }
3739         };
3740
3741         proto.getAttribute = function(attr) {
3742             if (this.patterns.points.test(attr)) {
3743                 var val = [
3744                         superclass.getAttribute.call(this, 'left'),
3745                         superclass.getAttribute.call(this, 'top')
3746                         ];
3747             } else {
3748                 val = superclass.getAttribute.call(this, attr);
3749             }
3750
3751             return val;
3752         };
3753
3754         proto.doMethod = function(attr, start, end) {
3755             var val = null;
3756
3757             if (this.patterns.points.test(attr)) {
3758                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3759                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3760             } else {
3761                 val = superclass.doMethod.call(this, attr, start, end);
3762             }
3763             return val;
3764         };
3765
3766         proto.setRuntimeAttribute = function(attr) {
3767             if (this.patterns.points.test(attr)) {
3768                 var el = this.getEl();
3769                 var attributes = this.attributes;
3770                 var start;
3771                 var control = attributes['points']['control'] || [];
3772                 var end;
3773                 var i, len;
3774
3775                 if (control.length > 0 && !(control[0] instanceof Array)) {
3776                     control = [control];
3777                 } else {
3778                     var tmp = [];
3779                     for (i = 0,len = control.length; i < len; ++i) {
3780                         tmp[i] = control[i];
3781                     }
3782                     control = tmp;
3783                 }
3784
3785                 Roo.fly(el).position();
3786
3787                 if (isset(attributes['points']['from'])) {
3788                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3789                 }
3790                 else {
3791                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3792                 }
3793
3794                 start = this.getAttribute('points');
3795
3796
3797                 if (isset(attributes['points']['to'])) {
3798                     end = translateValues.call(this, attributes['points']['to'], start);
3799
3800                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3801                     for (i = 0,len = control.length; i < len; ++i) {
3802                         control[i] = translateValues.call(this, control[i], start);
3803                     }
3804
3805
3806                 } else if (isset(attributes['points']['by'])) {
3807                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3808
3809                     for (i = 0,len = control.length; i < len; ++i) {
3810                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3811                     }
3812                 }
3813
3814                 this.runtimeAttributes[attr] = [start];
3815
3816                 if (control.length > 0) {
3817                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3818                 }
3819
3820                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3821             }
3822             else {
3823                 superclass.setRuntimeAttribute.call(this, attr);
3824             }
3825         };
3826
3827         var translateValues = function(val, start) {
3828             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3829             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3830
3831             return val;
3832         };
3833
3834         var isset = function(prop) {
3835             return (typeof prop !== 'undefined');
3836         };
3837     })();
3838 /*
3839  * Portions of this file are based on pieces of Yahoo User Interface Library
3840  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3841  * YUI licensed under the BSD License:
3842  * http://developer.yahoo.net/yui/license.txt
3843  * <script type="text/javascript">
3844  *
3845  */
3846     (function() {
3847         Roo.lib.Scroll = function(el, attributes, duration, method) {
3848             if (el) {
3849                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3850             }
3851         };
3852
3853         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3854
3855
3856         var Y = Roo.lib;
3857         var superclass = Y.Scroll.superclass;
3858         var proto = Y.Scroll.prototype;
3859
3860         proto.toString = function() {
3861             var el = this.getEl();
3862             var id = el.id || el.tagName;
3863             return ("Scroll " + id);
3864         };
3865
3866         proto.doMethod = function(attr, start, end) {
3867             var val = null;
3868
3869             if (attr == 'scroll') {
3870                 val = [
3871                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3872                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3873                         ];
3874
3875             } else {
3876                 val = superclass.doMethod.call(this, attr, start, end);
3877             }
3878             return val;
3879         };
3880
3881         proto.getAttribute = function(attr) {
3882             var val = null;
3883             var el = this.getEl();
3884
3885             if (attr == 'scroll') {
3886                 val = [ el.scrollLeft, el.scrollTop ];
3887             } else {
3888                 val = superclass.getAttribute.call(this, attr);
3889             }
3890
3891             return val;
3892         };
3893
3894         proto.setAttribute = function(attr, val, unit) {
3895             var el = this.getEl();
3896
3897             if (attr == 'scroll') {
3898                 el.scrollLeft = val[0];
3899                 el.scrollTop = val[1];
3900             } else {
3901                 superclass.setAttribute.call(this, attr, val, unit);
3902             }
3903         };
3904     })();
3905 /*
3906  * Based on:
3907  * Ext JS Library 1.1.1
3908  * Copyright(c) 2006-2007, Ext JS, LLC.
3909  *
3910  * Originally Released Under LGPL - original licence link has changed is not relivant.
3911  *
3912  * Fork - LGPL
3913  * <script type="text/javascript">
3914  */
3915  
3916
3917 /**
3918  * @class Roo.DomHelper
3919  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3920  * 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>.
3921  * @singleton
3922  */
3923 Roo.DomHelper = function(){
3924     var tempTableEl = null;
3925     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3926     var tableRe = /^table|tbody|tr|td$/i;
3927     var xmlns = {};
3928     // build as innerHTML where available
3929     /** @ignore */
3930     var createHtml = function(o){
3931         if(typeof o == 'string'){
3932             return o;
3933         }
3934         var b = "";
3935         if(!o.tag){
3936             o.tag = "div";
3937         }
3938         b += "<" + o.tag;
3939         for(var attr in o){
3940             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3941             if(attr == "style"){
3942                 var s = o["style"];
3943                 if(typeof s == "function"){
3944                     s = s.call();
3945                 }
3946                 if(typeof s == "string"){
3947                     b += ' style="' + s + '"';
3948                 }else if(typeof s == "object"){
3949                     b += ' style="';
3950                     for(var key in s){
3951                         if(typeof s[key] != "function"){
3952                             b += key + ":" + s[key] + ";";
3953                         }
3954                     }
3955                     b += '"';
3956                 }
3957             }else{
3958                 if(attr == "cls"){
3959                     b += ' class="' + o["cls"] + '"';
3960                 }else if(attr == "htmlFor"){
3961                     b += ' for="' + o["htmlFor"] + '"';
3962                 }else{
3963                     b += " " + attr + '="' + o[attr] + '"';
3964                 }
3965             }
3966         }
3967         if(emptyTags.test(o.tag)){
3968             b += "/>";
3969         }else{
3970             b += ">";
3971             var cn = o.children || o.cn;
3972             if(cn){
3973                 //http://bugs.kde.org/show_bug.cgi?id=71506
3974                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3975                     for(var i = 0, len = cn.length; i < len; i++) {
3976                         b += createHtml(cn[i], b);
3977                     }
3978                 }else{
3979                     b += createHtml(cn, b);
3980                 }
3981             }
3982             if(o.html){
3983                 b += o.html;
3984             }
3985             b += "</" + o.tag + ">";
3986         }
3987         return b;
3988     };
3989
3990     // build as dom
3991     /** @ignore */
3992     var createDom = function(o, parentNode){
3993          
3994         // defininition craeted..
3995         var ns = false;
3996         if (o.ns && o.ns != 'html') {
3997                
3998             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3999                 xmlns[o.ns] = o.xmlns;
4000                 ns = o.xmlns;
4001             }
4002             if (typeof(xmlns[o.ns]) == 'undefined') {
4003                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4004             }
4005             ns = xmlns[o.ns];
4006         }
4007         
4008         
4009         if (typeof(o) == 'string') {
4010             return parentNode.appendChild(document.createTextNode(o));
4011         }
4012         o.tag = o.tag || div;
4013         if (o.ns && Roo.isIE) {
4014             ns = false;
4015             o.tag = o.ns + ':' + o.tag;
4016             
4017         }
4018         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4019         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4020         for(var attr in o){
4021             
4022             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4023                     attr == "style" || typeof o[attr] == "function") continue;
4024                     
4025             if(attr=="cls" && Roo.isIE){
4026                 el.className = o["cls"];
4027             }else{
4028                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4029                 else el[attr] = o[attr];
4030             }
4031         }
4032         Roo.DomHelper.applyStyles(el, o.style);
4033         var cn = o.children || o.cn;
4034         if(cn){
4035             //http://bugs.kde.org/show_bug.cgi?id=71506
4036              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4037                 for(var i = 0, len = cn.length; i < len; i++) {
4038                     createDom(cn[i], el);
4039                 }
4040             }else{
4041                 createDom(cn, el);
4042             }
4043         }
4044         if(o.html){
4045             el.innerHTML = o.html;
4046         }
4047         if(parentNode){
4048            parentNode.appendChild(el);
4049         }
4050         return el;
4051     };
4052
4053     var ieTable = function(depth, s, h, e){
4054         tempTableEl.innerHTML = [s, h, e].join('');
4055         var i = -1, el = tempTableEl;
4056         while(++i < depth){
4057             el = el.firstChild;
4058         }
4059         return el;
4060     };
4061
4062     // kill repeat to save bytes
4063     var ts = '<table>',
4064         te = '</table>',
4065         tbs = ts+'<tbody>',
4066         tbe = '</tbody>'+te,
4067         trs = tbs + '<tr>',
4068         tre = '</tr>'+tbe;
4069
4070     /**
4071      * @ignore
4072      * Nasty code for IE's broken table implementation
4073      */
4074     var insertIntoTable = function(tag, where, el, html){
4075         if(!tempTableEl){
4076             tempTableEl = document.createElement('div');
4077         }
4078         var node;
4079         var before = null;
4080         if(tag == 'td'){
4081             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4082                 return;
4083             }
4084             if(where == 'beforebegin'){
4085                 before = el;
4086                 el = el.parentNode;
4087             } else{
4088                 before = el.nextSibling;
4089                 el = el.parentNode;
4090             }
4091             node = ieTable(4, trs, html, tre);
4092         }
4093         else if(tag == 'tr'){
4094             if(where == 'beforebegin'){
4095                 before = el;
4096                 el = el.parentNode;
4097                 node = ieTable(3, tbs, html, tbe);
4098             } else if(where == 'afterend'){
4099                 before = el.nextSibling;
4100                 el = el.parentNode;
4101                 node = ieTable(3, tbs, html, tbe);
4102             } else{ // INTO a TR
4103                 if(where == 'afterbegin'){
4104                     before = el.firstChild;
4105                 }
4106                 node = ieTable(4, trs, html, tre);
4107             }
4108         } else if(tag == 'tbody'){
4109             if(where == 'beforebegin'){
4110                 before = el;
4111                 el = el.parentNode;
4112                 node = ieTable(2, ts, html, te);
4113             } else if(where == 'afterend'){
4114                 before = el.nextSibling;
4115                 el = el.parentNode;
4116                 node = ieTable(2, ts, html, te);
4117             } else{
4118                 if(where == 'afterbegin'){
4119                     before = el.firstChild;
4120                 }
4121                 node = ieTable(3, tbs, html, tbe);
4122             }
4123         } else{ // TABLE
4124             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4125                 return;
4126             }
4127             if(where == 'afterbegin'){
4128                 before = el.firstChild;
4129             }
4130             node = ieTable(2, ts, html, te);
4131         }
4132         el.insertBefore(node, before);
4133         return node;
4134     };
4135
4136     return {
4137     /** True to force the use of DOM instead of html fragments @type Boolean */
4138     useDom : false,
4139
4140     /**
4141      * Returns the markup for the passed Element(s) config
4142      * @param {Object} o The Dom object spec (and children)
4143      * @return {String}
4144      */
4145     markup : function(o){
4146         return createHtml(o);
4147     },
4148
4149     /**
4150      * Applies a style specification to an element
4151      * @param {String/HTMLElement} el The element to apply styles to
4152      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4153      * a function which returns such a specification.
4154      */
4155     applyStyles : function(el, styles){
4156         if(styles){
4157            el = Roo.fly(el);
4158            if(typeof styles == "string"){
4159                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4160                var matches;
4161                while ((matches = re.exec(styles)) != null){
4162                    el.setStyle(matches[1], matches[2]);
4163                }
4164            }else if (typeof styles == "object"){
4165                for (var style in styles){
4166                   el.setStyle(style, styles[style]);
4167                }
4168            }else if (typeof styles == "function"){
4169                 Roo.DomHelper.applyStyles(el, styles.call());
4170            }
4171         }
4172     },
4173
4174     /**
4175      * Inserts an HTML fragment into the Dom
4176      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4177      * @param {HTMLElement} el The context element
4178      * @param {String} html The HTML fragmenet
4179      * @return {HTMLElement} The new node
4180      */
4181     insertHtml : function(where, el, html){
4182         where = where.toLowerCase();
4183         if(el.insertAdjacentHTML){
4184             if(tableRe.test(el.tagName)){
4185                 var rs;
4186                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4187                     return rs;
4188                 }
4189             }
4190             switch(where){
4191                 case "beforebegin":
4192                     el.insertAdjacentHTML('BeforeBegin', html);
4193                     return el.previousSibling;
4194                 case "afterbegin":
4195                     el.insertAdjacentHTML('AfterBegin', html);
4196                     return el.firstChild;
4197                 case "beforeend":
4198                     el.insertAdjacentHTML('BeforeEnd', html);
4199                     return el.lastChild;
4200                 case "afterend":
4201                     el.insertAdjacentHTML('AfterEnd', html);
4202                     return el.nextSibling;
4203             }
4204             throw 'Illegal insertion point -> "' + where + '"';
4205         }
4206         var range = el.ownerDocument.createRange();
4207         var frag;
4208         switch(where){
4209              case "beforebegin":
4210                 range.setStartBefore(el);
4211                 frag = range.createContextualFragment(html);
4212                 el.parentNode.insertBefore(frag, el);
4213                 return el.previousSibling;
4214              case "afterbegin":
4215                 if(el.firstChild){
4216                     range.setStartBefore(el.firstChild);
4217                     frag = range.createContextualFragment(html);
4218                     el.insertBefore(frag, el.firstChild);
4219                     return el.firstChild;
4220                 }else{
4221                     el.innerHTML = html;
4222                     return el.firstChild;
4223                 }
4224             case "beforeend":
4225                 if(el.lastChild){
4226                     range.setStartAfter(el.lastChild);
4227                     frag = range.createContextualFragment(html);
4228                     el.appendChild(frag);
4229                     return el.lastChild;
4230                 }else{
4231                     el.innerHTML = html;
4232                     return el.lastChild;
4233                 }
4234             case "afterend":
4235                 range.setStartAfter(el);
4236                 frag = range.createContextualFragment(html);
4237                 el.parentNode.insertBefore(frag, el.nextSibling);
4238                 return el.nextSibling;
4239             }
4240             throw 'Illegal insertion point -> "' + where + '"';
4241     },
4242
4243     /**
4244      * Creates new Dom element(s) and inserts them before el
4245      * @param {String/HTMLElement/Element} el The context element
4246      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4247      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4248      * @return {HTMLElement/Roo.Element} The new node
4249      */
4250     insertBefore : function(el, o, returnElement){
4251         return this.doInsert(el, o, returnElement, "beforeBegin");
4252     },
4253
4254     /**
4255      * Creates new Dom element(s) and inserts them after el
4256      * @param {String/HTMLElement/Element} el The context element
4257      * @param {Object} o The Dom object spec (and children)
4258      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4259      * @return {HTMLElement/Roo.Element} The new node
4260      */
4261     insertAfter : function(el, o, returnElement){
4262         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4263     },
4264
4265     /**
4266      * Creates new Dom element(s) and inserts them as the first child of el
4267      * @param {String/HTMLElement/Element} el The context element
4268      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4269      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4270      * @return {HTMLElement/Roo.Element} The new node
4271      */
4272     insertFirst : function(el, o, returnElement){
4273         return this.doInsert(el, o, returnElement, "afterBegin");
4274     },
4275
4276     // private
4277     doInsert : function(el, o, returnElement, pos, sibling){
4278         el = Roo.getDom(el);
4279         var newNode;
4280         if(this.useDom || o.ns){
4281             newNode = createDom(o, null);
4282             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4283         }else{
4284             var html = createHtml(o);
4285             newNode = this.insertHtml(pos, el, html);
4286         }
4287         return returnElement ? Roo.get(newNode, true) : newNode;
4288     },
4289
4290     /**
4291      * Creates new Dom element(s) and appends them to el
4292      * @param {String/HTMLElement/Element} el The context element
4293      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4294      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4295      * @return {HTMLElement/Roo.Element} The new node
4296      */
4297     append : function(el, o, returnElement){
4298         el = Roo.getDom(el);
4299         var newNode;
4300         if(this.useDom || o.ns){
4301             newNode = createDom(o, null);
4302             el.appendChild(newNode);
4303         }else{
4304             var html = createHtml(o);
4305             newNode = this.insertHtml("beforeEnd", el, html);
4306         }
4307         return returnElement ? Roo.get(newNode, true) : newNode;
4308     },
4309
4310     /**
4311      * Creates new Dom element(s) and overwrites the contents of el with them
4312      * @param {String/HTMLElement/Element} el The context element
4313      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4314      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4315      * @return {HTMLElement/Roo.Element} The new node
4316      */
4317     overwrite : function(el, o, returnElement){
4318         el = Roo.getDom(el);
4319         if (o.ns) {
4320           
4321             while (el.childNodes.length) {
4322                 el.removeChild(el.firstChild);
4323             }
4324             createDom(o, el);
4325         } else {
4326             el.innerHTML = createHtml(o);   
4327         }
4328         
4329         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4330     },
4331
4332     /**
4333      * Creates a new Roo.DomHelper.Template from the Dom object spec
4334      * @param {Object} o The Dom object spec (and children)
4335      * @return {Roo.DomHelper.Template} The new template
4336      */
4337     createTemplate : function(o){
4338         var html = createHtml(o);
4339         return new Roo.Template(html);
4340     }
4341     };
4342 }();
4343 /*
4344  * Based on:
4345  * Ext JS Library 1.1.1
4346  * Copyright(c) 2006-2007, Ext JS, LLC.
4347  *
4348  * Originally Released Under LGPL - original licence link has changed is not relivant.
4349  *
4350  * Fork - LGPL
4351  * <script type="text/javascript">
4352  */
4353  
4354 /**
4355 * @class Roo.Template
4356 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4357 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4358 * Usage:
4359 <pre><code>
4360 var t = new Roo.Template(
4361     '&lt;div name="{id}"&gt;',
4362         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4363     '&lt;/div&gt;'
4364 );
4365 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4366 </code></pre>
4367 * 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>. 
4368 * @constructor
4369 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4370 */
4371 Roo.Template = function(html){
4372     if(html instanceof Array){
4373         html = html.join("");
4374     }else if(arguments.length > 1){
4375         html = Array.prototype.join.call(arguments, "");
4376     }
4377     /**@private*/
4378     this.html = html;
4379     
4380 };
4381 Roo.Template.prototype = {
4382     /**
4383      * Returns an HTML fragment of this template with the specified values applied.
4384      * @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'})
4385      * @return {String} The HTML fragment
4386      */
4387     applyTemplate : function(values){
4388         if(this.compiled){
4389             return this.compiled(values);
4390         }
4391         var useF = this.disableFormats !== true;
4392         var fm = Roo.util.Format, tpl = this;
4393         var fn = function(m, name, format, args){
4394             if(format && useF){
4395                 if(format.substr(0, 5) == "this."){
4396                     return tpl.call(format.substr(5), values[name], values);
4397                 }else{
4398                     if(args){
4399                         // quoted values are required for strings in compiled templates, 
4400                         // but for non compiled we need to strip them
4401                         // quoted reversed for jsmin
4402                         var re = /^\s*['"](.*)["']\s*$/;
4403                         args = args.split(',');
4404                         for(var i = 0, len = args.length; i < len; i++){
4405                             args[i] = args[i].replace(re, "$1");
4406                         }
4407                         args = [values[name]].concat(args);
4408                     }else{
4409                         args = [values[name]];
4410                     }
4411                     return fm[format].apply(fm, args);
4412                 }
4413             }else{
4414                 return values[name] !== undefined ? values[name] : "";
4415             }
4416         };
4417         return this.html.replace(this.re, fn);
4418     },
4419     
4420     /**
4421      * Sets the HTML used as the template and optionally compiles it.
4422      * @param {String} html
4423      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4424      * @return {Roo.Template} this
4425      */
4426     set : function(html, compile){
4427         this.html = html;
4428         this.compiled = null;
4429         if(compile){
4430             this.compile();
4431         }
4432         return this;
4433     },
4434     
4435     /**
4436      * True to disable format functions (defaults to false)
4437      * @type Boolean
4438      */
4439     disableFormats : false,
4440     
4441     /**
4442     * The regular expression used to match template variables 
4443     * @type RegExp
4444     * @property 
4445     */
4446     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4447     
4448     /**
4449      * Compiles the template into an internal function, eliminating the RegEx overhead.
4450      * @return {Roo.Template} this
4451      */
4452     compile : function(){
4453         var fm = Roo.util.Format;
4454         var useF = this.disableFormats !== true;
4455         var sep = Roo.isGecko ? "+" : ",";
4456         var fn = function(m, name, format, args){
4457             if(format && useF){
4458                 args = args ? ',' + args : "";
4459                 if(format.substr(0, 5) != "this."){
4460                     format = "fm." + format + '(';
4461                 }else{
4462                     format = 'this.call("'+ format.substr(5) + '", ';
4463                     args = ", values";
4464                 }
4465             }else{
4466                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4467             }
4468             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4469         };
4470         var body;
4471         // branched to use + in gecko and [].join() in others
4472         if(Roo.isGecko){
4473             body = "this.compiled = function(values){ return '" +
4474                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4475                     "';};";
4476         }else{
4477             body = ["this.compiled = function(values){ return ['"];
4478             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4479             body.push("'].join('');};");
4480             body = body.join('');
4481         }
4482         /**
4483          * eval:var:values
4484          * eval:var:fm
4485          */
4486         eval(body);
4487         return this;
4488     },
4489     
4490     // private function used to call members
4491     call : function(fnName, value, allValues){
4492         return this[fnName](value, allValues);
4493     },
4494     
4495     /**
4496      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4497      * @param {String/HTMLElement/Roo.Element} el The context element
4498      * @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'})
4499      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4500      * @return {HTMLElement/Roo.Element} The new node or Element
4501      */
4502     insertFirst: function(el, values, returnElement){
4503         return this.doInsert('afterBegin', el, values, returnElement);
4504     },
4505
4506     /**
4507      * Applies the supplied values to the template and inserts the new node(s) before el.
4508      * @param {String/HTMLElement/Roo.Element} el The context element
4509      * @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'})
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4511      * @return {HTMLElement/Roo.Element} The new node or Element
4512      */
4513     insertBefore: function(el, values, returnElement){
4514         return this.doInsert('beforeBegin', el, values, returnElement);
4515     },
4516
4517     /**
4518      * Applies the supplied values to the template and inserts the new node(s) after el.
4519      * @param {String/HTMLElement/Roo.Element} el The context element
4520      * @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'})
4521      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4522      * @return {HTMLElement/Roo.Element} The new node or Element
4523      */
4524     insertAfter : function(el, values, returnElement){
4525         return this.doInsert('afterEnd', el, values, returnElement);
4526     },
4527     
4528     /**
4529      * Applies the supplied values to the template and appends the new node(s) to el.
4530      * @param {String/HTMLElement/Roo.Element} el The context element
4531      * @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'})
4532      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4533      * @return {HTMLElement/Roo.Element} The new node or Element
4534      */
4535     append : function(el, values, returnElement){
4536         return this.doInsert('beforeEnd', el, values, returnElement);
4537     },
4538
4539     doInsert : function(where, el, values, returnEl){
4540         el = Roo.getDom(el);
4541         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4542         return returnEl ? Roo.get(newNode, true) : newNode;
4543     },
4544
4545     /**
4546      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4547      * @param {String/HTMLElement/Roo.Element} el The context element
4548      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4549      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4550      * @return {HTMLElement/Roo.Element} The new node or Element
4551      */
4552     overwrite : function(el, values, returnElement){
4553         el = Roo.getDom(el);
4554         el.innerHTML = this.applyTemplate(values);
4555         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4556     }
4557 };
4558 /**
4559  * Alias for {@link #applyTemplate}
4560  * @method
4561  */
4562 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4563
4564 // backwards compat
4565 Roo.DomHelper.Template = Roo.Template;
4566
4567 /**
4568  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4569  * @param {String/HTMLElement} el A DOM element or its id
4570  * @returns {Roo.Template} The created template
4571  * @static
4572  */
4573 Roo.Template.from = function(el){
4574     el = Roo.getDom(el);
4575     return new Roo.Template(el.value || el.innerHTML);
4576 };/*
4577  * Based on:
4578  * Ext JS Library 1.1.1
4579  * Copyright(c) 2006-2007, Ext JS, LLC.
4580  *
4581  * Originally Released Under LGPL - original licence link has changed is not relivant.
4582  *
4583  * Fork - LGPL
4584  * <script type="text/javascript">
4585  */
4586  
4587
4588 /*
4589  * This is code is also distributed under MIT license for use
4590  * with jQuery and prototype JavaScript libraries.
4591  */
4592 /**
4593  * @class Roo.DomQuery
4594 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).
4595 <p>
4596 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>
4597
4598 <p>
4599 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.
4600 </p>
4601 <h4>Element Selectors:</h4>
4602 <ul class="list">
4603     <li> <b>*</b> any element</li>
4604     <li> <b>E</b> an element with the tag E</li>
4605     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4606     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4607     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4608     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4609 </ul>
4610 <h4>Attribute Selectors:</h4>
4611 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4612 <ul class="list">
4613     <li> <b>E[foo]</b> has an attribute "foo"</li>
4614     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4615     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4616     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4617     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4618     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4619     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4620 </ul>
4621 <h4>Pseudo Classes:</h4>
4622 <ul class="list">
4623     <li> <b>E:first-child</b> E is the first child of its parent</li>
4624     <li> <b>E:last-child</b> E is the last child of its parent</li>
4625     <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>
4626     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4627     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4628     <li> <b>E:only-child</b> E is the only child of its parent</li>
4629     <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>
4630     <li> <b>E:first</b> the first E in the resultset</li>
4631     <li> <b>E:last</b> the last E in the resultset</li>
4632     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4633     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4634     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4635     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4636     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4637     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4638     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4639     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4640     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4641 </ul>
4642 <h4>CSS Value Selectors:</h4>
4643 <ul class="list">
4644     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4645     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4646     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4647     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4648     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4649     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4650 </ul>
4651  * @singleton
4652  */
4653 Roo.DomQuery = function(){
4654     var cache = {}, simpleCache = {}, valueCache = {};
4655     var nonSpace = /\S/;
4656     var trimRe = /^\s+|\s+$/g;
4657     var tplRe = /\{(\d+)\}/g;
4658     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4659     var tagTokenRe = /^(#)?([\w-\*]+)/;
4660     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4661
4662     function child(p, index){
4663         var i = 0;
4664         var n = p.firstChild;
4665         while(n){
4666             if(n.nodeType == 1){
4667                if(++i == index){
4668                    return n;
4669                }
4670             }
4671             n = n.nextSibling;
4672         }
4673         return null;
4674     };
4675
4676     function next(n){
4677         while((n = n.nextSibling) && n.nodeType != 1);
4678         return n;
4679     };
4680
4681     function prev(n){
4682         while((n = n.previousSibling) && n.nodeType != 1);
4683         return n;
4684     };
4685
4686     function children(d){
4687         var n = d.firstChild, ni = -1;
4688             while(n){
4689                 var nx = n.nextSibling;
4690                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4691                     d.removeChild(n);
4692                 }else{
4693                     n.nodeIndex = ++ni;
4694                 }
4695                 n = nx;
4696             }
4697             return this;
4698         };
4699
4700     function byClassName(c, a, v){
4701         if(!v){
4702             return c;
4703         }
4704         var r = [], ri = -1, cn;
4705         for(var i = 0, ci; ci = c[i]; i++){
4706             if((' '+ci.className+' ').indexOf(v) != -1){
4707                 r[++ri] = ci;
4708             }
4709         }
4710         return r;
4711     };
4712
4713     function attrValue(n, attr){
4714         if(!n.tagName && typeof n.length != "undefined"){
4715             n = n[0];
4716         }
4717         if(!n){
4718             return null;
4719         }
4720         if(attr == "for"){
4721             return n.htmlFor;
4722         }
4723         if(attr == "class" || attr == "className"){
4724             return n.className;
4725         }
4726         return n.getAttribute(attr) || n[attr];
4727
4728     };
4729
4730     function getNodes(ns, mode, tagName){
4731         var result = [], ri = -1, cs;
4732         if(!ns){
4733             return result;
4734         }
4735         tagName = tagName || "*";
4736         if(typeof ns.getElementsByTagName != "undefined"){
4737             ns = [ns];
4738         }
4739         if(!mode){
4740             for(var i = 0, ni; ni = ns[i]; i++){
4741                 cs = ni.getElementsByTagName(tagName);
4742                 for(var j = 0, ci; ci = cs[j]; j++){
4743                     result[++ri] = ci;
4744                 }
4745             }
4746         }else if(mode == "/" || mode == ">"){
4747             var utag = tagName.toUpperCase();
4748             for(var i = 0, ni, cn; ni = ns[i]; i++){
4749                 cn = ni.children || ni.childNodes;
4750                 for(var j = 0, cj; cj = cn[j]; j++){
4751                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4752                         result[++ri] = cj;
4753                     }
4754                 }
4755             }
4756         }else if(mode == "+"){
4757             var utag = tagName.toUpperCase();
4758             for(var i = 0, n; n = ns[i]; i++){
4759                 while((n = n.nextSibling) && n.nodeType != 1);
4760                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4761                     result[++ri] = n;
4762                 }
4763             }
4764         }else if(mode == "~"){
4765             for(var i = 0, n; n = ns[i]; i++){
4766                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4767                 if(n){
4768                     result[++ri] = n;
4769                 }
4770             }
4771         }
4772         return result;
4773     };
4774
4775     function concat(a, b){
4776         if(b.slice){
4777             return a.concat(b);
4778         }
4779         for(var i = 0, l = b.length; i < l; i++){
4780             a[a.length] = b[i];
4781         }
4782         return a;
4783     }
4784
4785     function byTag(cs, tagName){
4786         if(cs.tagName || cs == document){
4787             cs = [cs];
4788         }
4789         if(!tagName){
4790             return cs;
4791         }
4792         var r = [], ri = -1;
4793         tagName = tagName.toLowerCase();
4794         for(var i = 0, ci; ci = cs[i]; i++){
4795             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4796                 r[++ri] = ci;
4797             }
4798         }
4799         return r;
4800     };
4801
4802     function byId(cs, attr, id){
4803         if(cs.tagName || cs == document){
4804             cs = [cs];
4805         }
4806         if(!id){
4807             return cs;
4808         }
4809         var r = [], ri = -1;
4810         for(var i = 0,ci; ci = cs[i]; i++){
4811             if(ci && ci.id == id){
4812                 r[++ri] = ci;
4813                 return r;
4814             }
4815         }
4816         return r;
4817     };
4818
4819     function byAttribute(cs, attr, value, op, custom){
4820         var r = [], ri = -1, st = custom=="{";
4821         var f = Roo.DomQuery.operators[op];
4822         for(var i = 0, ci; ci = cs[i]; i++){
4823             var a;
4824             if(st){
4825                 a = Roo.DomQuery.getStyle(ci, attr);
4826             }
4827             else if(attr == "class" || attr == "className"){
4828                 a = ci.className;
4829             }else if(attr == "for"){
4830                 a = ci.htmlFor;
4831             }else if(attr == "href"){
4832                 a = ci.getAttribute("href", 2);
4833             }else{
4834                 a = ci.getAttribute(attr);
4835             }
4836             if((f && f(a, value)) || (!f && a)){
4837                 r[++ri] = ci;
4838             }
4839         }
4840         return r;
4841     };
4842
4843     function byPseudo(cs, name, value){
4844         return Roo.DomQuery.pseudos[name](cs, value);
4845     };
4846
4847     // This is for IE MSXML which does not support expandos.
4848     // IE runs the same speed using setAttribute, however FF slows way down
4849     // and Safari completely fails so they need to continue to use expandos.
4850     var isIE = window.ActiveXObject ? true : false;
4851
4852     // this eval is stop the compressor from
4853     // renaming the variable to something shorter
4854     
4855     /** eval:var:batch */
4856     var batch = 30803; 
4857
4858     var key = 30803;
4859
4860     function nodupIEXml(cs){
4861         var d = ++key;
4862         cs[0].setAttribute("_nodup", d);
4863         var r = [cs[0]];
4864         for(var i = 1, len = cs.length; i < len; i++){
4865             var c = cs[i];
4866             if(!c.getAttribute("_nodup") != d){
4867                 c.setAttribute("_nodup", d);
4868                 r[r.length] = c;
4869             }
4870         }
4871         for(var i = 0, len = cs.length; i < len; i++){
4872             cs[i].removeAttribute("_nodup");
4873         }
4874         return r;
4875     }
4876
4877     function nodup(cs){
4878         if(!cs){
4879             return [];
4880         }
4881         var len = cs.length, c, i, r = cs, cj, ri = -1;
4882         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4883             return cs;
4884         }
4885         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4886             return nodupIEXml(cs);
4887         }
4888         var d = ++key;
4889         cs[0]._nodup = d;
4890         for(i = 1; c = cs[i]; i++){
4891             if(c._nodup != d){
4892                 c._nodup = d;
4893             }else{
4894                 r = [];
4895                 for(var j = 0; j < i; j++){
4896                     r[++ri] = cs[j];
4897                 }
4898                 for(j = i+1; cj = cs[j]; j++){
4899                     if(cj._nodup != d){
4900                         cj._nodup = d;
4901                         r[++ri] = cj;
4902                     }
4903                 }
4904                 return r;
4905             }
4906         }
4907         return r;
4908     }
4909
4910     function quickDiffIEXml(c1, c2){
4911         var d = ++key;
4912         for(var i = 0, len = c1.length; i < len; i++){
4913             c1[i].setAttribute("_qdiff", d);
4914         }
4915         var r = [];
4916         for(var i = 0, len = c2.length; i < len; i++){
4917             if(c2[i].getAttribute("_qdiff") != d){
4918                 r[r.length] = c2[i];
4919             }
4920         }
4921         for(var i = 0, len = c1.length; i < len; i++){
4922            c1[i].removeAttribute("_qdiff");
4923         }
4924         return r;
4925     }
4926
4927     function quickDiff(c1, c2){
4928         var len1 = c1.length;
4929         if(!len1){
4930             return c2;
4931         }
4932         if(isIE && c1[0].selectSingleNode){
4933             return quickDiffIEXml(c1, c2);
4934         }
4935         var d = ++key;
4936         for(var i = 0; i < len1; i++){
4937             c1[i]._qdiff = d;
4938         }
4939         var r = [];
4940         for(var i = 0, len = c2.length; i < len; i++){
4941             if(c2[i]._qdiff != d){
4942                 r[r.length] = c2[i];
4943             }
4944         }
4945         return r;
4946     }
4947
4948     function quickId(ns, mode, root, id){
4949         if(ns == root){
4950            var d = root.ownerDocument || root;
4951            return d.getElementById(id);
4952         }
4953         ns = getNodes(ns, mode, "*");
4954         return byId(ns, null, id);
4955     }
4956
4957     return {
4958         getStyle : function(el, name){
4959             return Roo.fly(el).getStyle(name);
4960         },
4961         /**
4962          * Compiles a selector/xpath query into a reusable function. The returned function
4963          * takes one parameter "root" (optional), which is the context node from where the query should start.
4964          * @param {String} selector The selector/xpath query
4965          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4966          * @return {Function}
4967          */
4968         compile : function(path, type){
4969             type = type || "select";
4970             
4971             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4972             var q = path, mode, lq;
4973             var tk = Roo.DomQuery.matchers;
4974             var tklen = tk.length;
4975             var mm;
4976
4977             // accept leading mode switch
4978             var lmode = q.match(modeRe);
4979             if(lmode && lmode[1]){
4980                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4981                 q = q.replace(lmode[1], "");
4982             }
4983             // strip leading slashes
4984             while(path.substr(0, 1)=="/"){
4985                 path = path.substr(1);
4986             }
4987
4988             while(q && lq != q){
4989                 lq = q;
4990                 var tm = q.match(tagTokenRe);
4991                 if(type == "select"){
4992                     if(tm){
4993                         if(tm[1] == "#"){
4994                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4995                         }else{
4996                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4997                         }
4998                         q = q.replace(tm[0], "");
4999                     }else if(q.substr(0, 1) != '@'){
5000                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5001                     }
5002                 }else{
5003                     if(tm){
5004                         if(tm[1] == "#"){
5005                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5006                         }else{
5007                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5008                         }
5009                         q = q.replace(tm[0], "");
5010                     }
5011                 }
5012                 while(!(mm = q.match(modeRe))){
5013                     var matched = false;
5014                     for(var j = 0; j < tklen; j++){
5015                         var t = tk[j];
5016                         var m = q.match(t.re);
5017                         if(m){
5018                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5019                                                     return m[i];
5020                                                 });
5021                             q = q.replace(m[0], "");
5022                             matched = true;
5023                             break;
5024                         }
5025                     }
5026                     // prevent infinite loop on bad selector
5027                     if(!matched){
5028                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5029                     }
5030                 }
5031                 if(mm[1]){
5032                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5033                     q = q.replace(mm[1], "");
5034                 }
5035             }
5036             fn[fn.length] = "return nodup(n);\n}";
5037             
5038              /** 
5039               * list of variables that need from compression as they are used by eval.
5040              *  eval:var:batch 
5041              *  eval:var:nodup
5042              *  eval:var:byTag
5043              *  eval:var:ById
5044              *  eval:var:getNodes
5045              *  eval:var:quickId
5046              *  eval:var:mode
5047              *  eval:var:root
5048              *  eval:var:n
5049              *  eval:var:byClassName
5050              *  eval:var:byPseudo
5051              *  eval:var:byAttribute
5052              *  eval:var:attrValue
5053              * 
5054              **/ 
5055             eval(fn.join(""));
5056             return f;
5057         },
5058
5059         /**
5060          * Selects a group of elements.
5061          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5062          * @param {Node} root (optional) The start of the query (defaults to document).
5063          * @return {Array}
5064          */
5065         select : function(path, root, type){
5066             if(!root || root == document){
5067                 root = document;
5068             }
5069             if(typeof root == "string"){
5070                 root = document.getElementById(root);
5071             }
5072             var paths = path.split(",");
5073             var results = [];
5074             for(var i = 0, len = paths.length; i < len; i++){
5075                 var p = paths[i].replace(trimRe, "");
5076                 if(!cache[p]){
5077                     cache[p] = Roo.DomQuery.compile(p);
5078                     if(!cache[p]){
5079                         throw p + " is not a valid selector";
5080                     }
5081                 }
5082                 var result = cache[p](root);
5083                 if(result && result != document){
5084                     results = results.concat(result);
5085                 }
5086             }
5087             if(paths.length > 1){
5088                 return nodup(results);
5089             }
5090             return results;
5091         },
5092
5093         /**
5094          * Selects a single element.
5095          * @param {String} selector The selector/xpath query
5096          * @param {Node} root (optional) The start of the query (defaults to document).
5097          * @return {Element}
5098          */
5099         selectNode : function(path, root){
5100             return Roo.DomQuery.select(path, root)[0];
5101         },
5102
5103         /**
5104          * Selects the value of a node, optionally replacing null with the defaultValue.
5105          * @param {String} selector The selector/xpath query
5106          * @param {Node} root (optional) The start of the query (defaults to document).
5107          * @param {String} defaultValue
5108          */
5109         selectValue : function(path, root, defaultValue){
5110             path = path.replace(trimRe, "");
5111             if(!valueCache[path]){
5112                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5113             }
5114             var n = valueCache[path](root);
5115             n = n[0] ? n[0] : n;
5116             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5117             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5118         },
5119
5120         /**
5121          * Selects the value of a node, parsing integers and floats.
5122          * @param {String} selector The selector/xpath query
5123          * @param {Node} root (optional) The start of the query (defaults to document).
5124          * @param {Number} defaultValue
5125          * @return {Number}
5126          */
5127         selectNumber : function(path, root, defaultValue){
5128             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5129             return parseFloat(v);
5130         },
5131
5132         /**
5133          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5134          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5135          * @param {String} selector The simple selector to test
5136          * @return {Boolean}
5137          */
5138         is : function(el, ss){
5139             if(typeof el == "string"){
5140                 el = document.getElementById(el);
5141             }
5142             var isArray = (el instanceof Array);
5143             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5144             return isArray ? (result.length == el.length) : (result.length > 0);
5145         },
5146
5147         /**
5148          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5149          * @param {Array} el An array of elements to filter
5150          * @param {String} selector The simple selector to test
5151          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5152          * the selector instead of the ones that match
5153          * @return {Array}
5154          */
5155         filter : function(els, ss, nonMatches){
5156             ss = ss.replace(trimRe, "");
5157             if(!simpleCache[ss]){
5158                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5159             }
5160             var result = simpleCache[ss](els);
5161             return nonMatches ? quickDiff(result, els) : result;
5162         },
5163
5164         /**
5165          * Collection of matching regular expressions and code snippets.
5166          */
5167         matchers : [{
5168                 re: /^\.([\w-]+)/,
5169                 select: 'n = byClassName(n, null, " {1} ");'
5170             }, {
5171                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5172                 select: 'n = byPseudo(n, "{1}", "{2}");'
5173             },{
5174                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5175                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5176             }, {
5177                 re: /^#([\w-]+)/,
5178                 select: 'n = byId(n, null, "{1}");'
5179             },{
5180                 re: /^@([\w-]+)/,
5181                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5182             }
5183         ],
5184
5185         /**
5186          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5187          * 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;.
5188          */
5189         operators : {
5190             "=" : function(a, v){
5191                 return a == v;
5192             },
5193             "!=" : function(a, v){
5194                 return a != v;
5195             },
5196             "^=" : function(a, v){
5197                 return a && a.substr(0, v.length) == v;
5198             },
5199             "$=" : function(a, v){
5200                 return a && a.substr(a.length-v.length) == v;
5201             },
5202             "*=" : function(a, v){
5203                 return a && a.indexOf(v) !== -1;
5204             },
5205             "%=" : function(a, v){
5206                 return (a % v) == 0;
5207             },
5208             "|=" : function(a, v){
5209                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5210             },
5211             "~=" : function(a, v){
5212                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5213             }
5214         },
5215
5216         /**
5217          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5218          * and the argument (if any) supplied in the selector.
5219          */
5220         pseudos : {
5221             "first-child" : function(c){
5222                 var r = [], ri = -1, n;
5223                 for(var i = 0, ci; ci = n = c[i]; i++){
5224                     while((n = n.previousSibling) && n.nodeType != 1);
5225                     if(!n){
5226                         r[++ri] = ci;
5227                     }
5228                 }
5229                 return r;
5230             },
5231
5232             "last-child" : function(c){
5233                 var r = [], ri = -1, n;
5234                 for(var i = 0, ci; ci = n = c[i]; i++){
5235                     while((n = n.nextSibling) && n.nodeType != 1);
5236                     if(!n){
5237                         r[++ri] = ci;
5238                     }
5239                 }
5240                 return r;
5241             },
5242
5243             "nth-child" : function(c, a) {
5244                 var r = [], ri = -1;
5245                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5246                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5247                 for(var i = 0, n; n = c[i]; i++){
5248                     var pn = n.parentNode;
5249                     if (batch != pn._batch) {
5250                         var j = 0;
5251                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5252                             if(cn.nodeType == 1){
5253                                cn.nodeIndex = ++j;
5254                             }
5255                         }
5256                         pn._batch = batch;
5257                     }
5258                     if (f == 1) {
5259                         if (l == 0 || n.nodeIndex == l){
5260                             r[++ri] = n;
5261                         }
5262                     } else if ((n.nodeIndex + l) % f == 0){
5263                         r[++ri] = n;
5264                     }
5265                 }
5266
5267                 return r;
5268             },
5269
5270             "only-child" : function(c){
5271                 var r = [], ri = -1;;
5272                 for(var i = 0, ci; ci = c[i]; i++){
5273                     if(!prev(ci) && !next(ci)){
5274                         r[++ri] = ci;
5275                     }
5276                 }
5277                 return r;
5278             },
5279
5280             "empty" : function(c){
5281                 var r = [], ri = -1;
5282                 for(var i = 0, ci; ci = c[i]; i++){
5283                     var cns = ci.childNodes, j = 0, cn, empty = true;
5284                     while(cn = cns[j]){
5285                         ++j;
5286                         if(cn.nodeType == 1 || cn.nodeType == 3){
5287                             empty = false;
5288                             break;
5289                         }
5290                     }
5291                     if(empty){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "contains" : function(c, v){
5299                 var r = [], ri = -1;
5300                 for(var i = 0, ci; ci = c[i]; i++){
5301                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5302                         r[++ri] = ci;
5303                     }
5304                 }
5305                 return r;
5306             },
5307
5308             "nodeValue" : function(c, v){
5309                 var r = [], ri = -1;
5310                 for(var i = 0, ci; ci = c[i]; i++){
5311                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5312                         r[++ri] = ci;
5313                     }
5314                 }
5315                 return r;
5316             },
5317
5318             "checked" : function(c){
5319                 var r = [], ri = -1;
5320                 for(var i = 0, ci; ci = c[i]; i++){
5321                     if(ci.checked == true){
5322                         r[++ri] = ci;
5323                     }
5324                 }
5325                 return r;
5326             },
5327
5328             "not" : function(c, ss){
5329                 return Roo.DomQuery.filter(c, ss, true);
5330             },
5331
5332             "odd" : function(c){
5333                 return this["nth-child"](c, "odd");
5334             },
5335
5336             "even" : function(c){
5337                 return this["nth-child"](c, "even");
5338             },
5339
5340             "nth" : function(c, a){
5341                 return c[a-1] || [];
5342             },
5343
5344             "first" : function(c){
5345                 return c[0] || [];
5346             },
5347
5348             "last" : function(c){
5349                 return c[c.length-1] || [];
5350             },
5351
5352             "has" : function(c, ss){
5353                 var s = Roo.DomQuery.select;
5354                 var r = [], ri = -1;
5355                 for(var i = 0, ci; ci = c[i]; i++){
5356                     if(s(ss, ci).length > 0){
5357                         r[++ri] = ci;
5358                     }
5359                 }
5360                 return r;
5361             },
5362
5363             "next" : function(c, ss){
5364                 var is = Roo.DomQuery.is;
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     var n = next(ci);
5368                     if(n && is(n, ss)){
5369                         r[++ri] = ci;
5370                     }
5371                 }
5372                 return r;
5373             },
5374
5375             "prev" : function(c, ss){
5376                 var is = Roo.DomQuery.is;
5377                 var r = [], ri = -1;
5378                 for(var i = 0, ci; ci = c[i]; i++){
5379                     var n = prev(ci);
5380                     if(n && is(n, ss)){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             }
5386         }
5387     };
5388 }();
5389
5390 /**
5391  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5392  * @param {String} path The selector/xpath query
5393  * @param {Node} root (optional) The start of the query (defaults to document).
5394  * @return {Array}
5395  * @member Roo
5396  * @method query
5397  */
5398 Roo.query = Roo.DomQuery.select;
5399 /*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411  * @class Roo.util.Observable
5412  * Base class that provides a common interface for publishing events. Subclasses are expected to
5413  * to have a property "events" with all the events defined.<br>
5414  * For example:
5415  * <pre><code>
5416  Employee = function(name){
5417     this.name = name;
5418     this.addEvents({
5419         "fired" : true,
5420         "quit" : true
5421     });
5422  }
5423  Roo.extend(Employee, Roo.util.Observable);
5424 </code></pre>
5425  * @param {Object} config properties to use (incuding events / listeners)
5426  */
5427
5428 Roo.util.Observable = function(cfg){
5429     
5430     cfg = cfg|| {};
5431     this.addEvents(cfg.events || {});
5432     if (cfg.events) {
5433         delete cfg.events; // make sure
5434     }
5435      
5436     Roo.apply(this, cfg);
5437     
5438     if(this.listeners){
5439         this.on(this.listeners);
5440         delete this.listeners;
5441     }
5442 };
5443 Roo.util.Observable.prototype = {
5444     /** 
5445  * @cfg {Object} listeners  list of events and functions to call for this object, 
5446  * For example :
5447  * <pre><code>
5448     listeners :  { 
5449        'click' : function(e) {
5450            ..... 
5451         } ,
5452         .... 
5453     } 
5454   </code></pre>
5455  */
5456     
5457     
5458     /**
5459      * Fires the specified event with the passed parameters (minus the event name).
5460      * @param {String} eventName
5461      * @param {Object...} args Variable number of parameters are passed to handlers
5462      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5463      */
5464     fireEvent : function(){
5465         var ce = this.events[arguments[0].toLowerCase()];
5466         if(typeof ce == "object"){
5467             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5468         }else{
5469             return true;
5470         }
5471     },
5472
5473     // private
5474     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5475
5476     /**
5477      * Appends an event handler to this component
5478      * @param {String}   eventName The type of event to listen for
5479      * @param {Function} handler The method the event invokes
5480      * @param {Object}   scope (optional) The scope in which to execute the handler
5481      * function. The handler function's "this" context.
5482      * @param {Object}   options (optional) An object containing handler configuration
5483      * properties. This may contain any of the following properties:<ul>
5484      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5485      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5486      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5487      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5488      * by the specified number of milliseconds. If the event fires again within that time, the original
5489      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5490      * </ul><br>
5491      * <p>
5492      * <b>Combining Options</b><br>
5493      * Using the options argument, it is possible to combine different types of listeners:<br>
5494      * <br>
5495      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5496                 <pre><code>
5497                 el.on('click', this.onClick, this, {
5498                         single: true,
5499                 delay: 100,
5500                 forumId: 4
5501                 });
5502                 </code></pre>
5503      * <p>
5504      * <b>Attaching multiple handlers in 1 call</b><br>
5505      * The method also allows for a single argument to be passed which is a config object containing properties
5506      * which specify multiple handlers.
5507      * <pre><code>
5508                 el.on({
5509                         'click': {
5510                         fn: this.onClick,
5511                         scope: this,
5512                         delay: 100
5513                 }, 
5514                 'mouseover': {
5515                         fn: this.onMouseOver,
5516                         scope: this
5517                 },
5518                 'mouseout': {
5519                         fn: this.onMouseOut,
5520                         scope: this
5521                 }
5522                 });
5523                 </code></pre>
5524      * <p>
5525      * Or a shorthand syntax which passes the same scope object to all handlers:
5526         <pre><code>
5527                 el.on({
5528                         'click': this.onClick,
5529                 'mouseover': this.onMouseOver,
5530                 'mouseout': this.onMouseOut,
5531                 scope: this
5532                 });
5533                 </code></pre>
5534      */
5535     addListener : function(eventName, fn, scope, o){
5536         if(typeof eventName == "object"){
5537             o = eventName;
5538             for(var e in o){
5539                 if(this.filterOptRe.test(e)){
5540                     continue;
5541                 }
5542                 if(typeof o[e] == "function"){
5543                     // shared options
5544                     this.addListener(e, o[e], o.scope,  o);
5545                 }else{
5546                     // individual options
5547                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5548                 }
5549             }
5550             return;
5551         }
5552         o = (!o || typeof o == "boolean") ? {} : o;
5553         eventName = eventName.toLowerCase();
5554         var ce = this.events[eventName] || true;
5555         if(typeof ce == "boolean"){
5556             ce = new Roo.util.Event(this, eventName);
5557             this.events[eventName] = ce;
5558         }
5559         ce.addListener(fn, scope, o);
5560     },
5561
5562     /**
5563      * Removes a listener
5564      * @param {String}   eventName     The type of event to listen for
5565      * @param {Function} handler        The handler to remove
5566      * @param {Object}   scope  (optional) The scope (this object) for the handler
5567      */
5568     removeListener : function(eventName, fn, scope){
5569         var ce = this.events[eventName.toLowerCase()];
5570         if(typeof ce == "object"){
5571             ce.removeListener(fn, scope);
5572         }
5573     },
5574
5575     /**
5576      * Removes all listeners for this object
5577      */
5578     purgeListeners : function(){
5579         for(var evt in this.events){
5580             if(typeof this.events[evt] == "object"){
5581                  this.events[evt].clearListeners();
5582             }
5583         }
5584     },
5585
5586     relayEvents : function(o, events){
5587         var createHandler = function(ename){
5588             return function(){
5589                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5590             };
5591         };
5592         for(var i = 0, len = events.length; i < len; i++){
5593             var ename = events[i];
5594             if(!this.events[ename]){ this.events[ename] = true; };
5595             o.on(ename, createHandler(ename), this);
5596         }
5597     },
5598
5599     /**
5600      * Used to define events on this Observable
5601      * @param {Object} object The object with the events defined
5602      */
5603     addEvents : function(o){
5604         if(!this.events){
5605             this.events = {};
5606         }
5607         Roo.applyIf(this.events, o);
5608     },
5609
5610     /**
5611      * Checks to see if this object has any listeners for a specified event
5612      * @param {String} eventName The name of the event to check for
5613      * @return {Boolean} True if the event is being listened for, else false
5614      */
5615     hasListener : function(eventName){
5616         var e = this.events[eventName];
5617         return typeof e == "object" && e.listeners.length > 0;
5618     }
5619 };
5620 /**
5621  * Appends an event handler to this element (shorthand for addListener)
5622  * @param {String}   eventName     The type of event to listen for
5623  * @param {Function} handler        The method the event invokes
5624  * @param {Object}   scope (optional) The scope in which to execute the handler
5625  * function. The handler function's "this" context.
5626  * @param {Object}   options  (optional)
5627  * @method
5628  */
5629 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5630 /**
5631  * Removes a listener (shorthand for removeListener)
5632  * @param {String}   eventName     The type of event to listen for
5633  * @param {Function} handler        The handler to remove
5634  * @param {Object}   scope  (optional) The scope (this object) for the handler
5635  * @method
5636  */
5637 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5638
5639 /**
5640  * Starts capture on the specified Observable. All events will be passed
5641  * to the supplied function with the event name + standard signature of the event
5642  * <b>before</b> the event is fired. If the supplied function returns false,
5643  * the event will not fire.
5644  * @param {Observable} o The Observable to capture
5645  * @param {Function} fn The function to call
5646  * @param {Object} scope (optional) The scope (this object) for the fn
5647  * @static
5648  */
5649 Roo.util.Observable.capture = function(o, fn, scope){
5650     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5651 };
5652
5653 /**
5654  * Removes <b>all</b> added captures from the Observable.
5655  * @param {Observable} o The Observable to release
5656  * @static
5657  */
5658 Roo.util.Observable.releaseCapture = function(o){
5659     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5660 };
5661
5662 (function(){
5663
5664     var createBuffered = function(h, o, scope){
5665         var task = new Roo.util.DelayedTask();
5666         return function(){
5667             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5668         };
5669     };
5670
5671     var createSingle = function(h, e, fn, scope){
5672         return function(){
5673             e.removeListener(fn, scope);
5674             return h.apply(scope, arguments);
5675         };
5676     };
5677
5678     var createDelayed = function(h, o, scope){
5679         return function(){
5680             var args = Array.prototype.slice.call(arguments, 0);
5681             setTimeout(function(){
5682                 h.apply(scope, args);
5683             }, o.delay || 10);
5684         };
5685     };
5686
5687     Roo.util.Event = function(obj, name){
5688         this.name = name;
5689         this.obj = obj;
5690         this.listeners = [];
5691     };
5692
5693     Roo.util.Event.prototype = {
5694         addListener : function(fn, scope, options){
5695             var o = options || {};
5696             scope = scope || this.obj;
5697             if(!this.isListening(fn, scope)){
5698                 var l = {fn: fn, scope: scope, options: o};
5699                 var h = fn;
5700                 if(o.delay){
5701                     h = createDelayed(h, o, scope);
5702                 }
5703                 if(o.single){
5704                     h = createSingle(h, this, fn, scope);
5705                 }
5706                 if(o.buffer){
5707                     h = createBuffered(h, o, scope);
5708                 }
5709                 l.fireFn = h;
5710                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5711                     this.listeners.push(l);
5712                 }else{
5713                     this.listeners = this.listeners.slice(0);
5714                     this.listeners.push(l);
5715                 }
5716             }
5717         },
5718
5719         findListener : function(fn, scope){
5720             scope = scope || this.obj;
5721             var ls = this.listeners;
5722             for(var i = 0, len = ls.length; i < len; i++){
5723                 var l = ls[i];
5724                 if(l.fn == fn && l.scope == scope){
5725                     return i;
5726                 }
5727             }
5728             return -1;
5729         },
5730
5731         isListening : function(fn, scope){
5732             return this.findListener(fn, scope) != -1;
5733         },
5734
5735         removeListener : function(fn, scope){
5736             var index;
5737             if((index = this.findListener(fn, scope)) != -1){
5738                 if(!this.firing){
5739                     this.listeners.splice(index, 1);
5740                 }else{
5741                     this.listeners = this.listeners.slice(0);
5742                     this.listeners.splice(index, 1);
5743                 }
5744                 return true;
5745             }
5746             return false;
5747         },
5748
5749         clearListeners : function(){
5750             this.listeners = [];
5751         },
5752
5753         fire : function(){
5754             var ls = this.listeners, scope, len = ls.length;
5755             if(len > 0){
5756                 this.firing = true;
5757                 var args = Array.prototype.slice.call(arguments, 0);
5758                 for(var i = 0; i < len; i++){
5759                     var l = ls[i];
5760                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5761                         this.firing = false;
5762                         return false;
5763                     }
5764                 }
5765                 this.firing = false;
5766             }
5767             return true;
5768         }
5769     };
5770 })();/*
5771  * Based on:
5772  * Ext JS Library 1.1.1
5773  * Copyright(c) 2006-2007, Ext JS, LLC.
5774  *
5775  * Originally Released Under LGPL - original licence link has changed is not relivant.
5776  *
5777  * Fork - LGPL
5778  * <script type="text/javascript">
5779  */
5780
5781 /**
5782  * @class Roo.EventManager
5783  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5784  * several useful events directly.
5785  * See {@link Roo.EventObject} for more details on normalized event objects.
5786  * @singleton
5787  */
5788 Roo.EventManager = function(){
5789     var docReadyEvent, docReadyProcId, docReadyState = false;
5790     var resizeEvent, resizeTask, textEvent, textSize;
5791     var E = Roo.lib.Event;
5792     var D = Roo.lib.Dom;
5793
5794
5795     var fireDocReady = function(){
5796         if(!docReadyState){
5797             docReadyState = true;
5798             Roo.isReady = true;
5799             if(docReadyProcId){
5800                 clearInterval(docReadyProcId);
5801             }
5802             if(Roo.isGecko || Roo.isOpera) {
5803                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5804             }
5805             if(Roo.isIE){
5806                 var defer = document.getElementById("ie-deferred-loader");
5807                 if(defer){
5808                     defer.onreadystatechange = null;
5809                     defer.parentNode.removeChild(defer);
5810                 }
5811             }
5812             if(docReadyEvent){
5813                 docReadyEvent.fire();
5814                 docReadyEvent.clearListeners();
5815             }
5816         }
5817     };
5818     
5819     var initDocReady = function(){
5820         docReadyEvent = new Roo.util.Event();
5821         if(Roo.isGecko || Roo.isOpera) {
5822             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5823         }else if(Roo.isIE){
5824             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5825             var defer = document.getElementById("ie-deferred-loader");
5826             defer.onreadystatechange = function(){
5827                 if(this.readyState == "complete"){
5828                     fireDocReady();
5829                 }
5830             };
5831         }else if(Roo.isSafari){ 
5832             docReadyProcId = setInterval(function(){
5833                 var rs = document.readyState;
5834                 if(rs == "complete") {
5835                     fireDocReady();     
5836                  }
5837             }, 10);
5838         }
5839         // no matter what, make sure it fires on load
5840         E.on(window, "load", fireDocReady);
5841     };
5842
5843     var createBuffered = function(h, o){
5844         var task = new Roo.util.DelayedTask(h);
5845         return function(e){
5846             // create new event object impl so new events don't wipe out properties
5847             e = new Roo.EventObjectImpl(e);
5848             task.delay(o.buffer, h, null, [e]);
5849         };
5850     };
5851
5852     var createSingle = function(h, el, ename, fn){
5853         return function(e){
5854             Roo.EventManager.removeListener(el, ename, fn);
5855             h(e);
5856         };
5857     };
5858
5859     var createDelayed = function(h, o){
5860         return function(e){
5861             // create new event object impl so new events don't wipe out properties
5862             e = new Roo.EventObjectImpl(e);
5863             setTimeout(function(){
5864                 h(e);
5865             }, o.delay || 10);
5866         };
5867     };
5868
5869     var listen = function(element, ename, opt, fn, scope){
5870         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5871         fn = fn || o.fn; scope = scope || o.scope;
5872         var el = Roo.getDom(element);
5873         if(!el){
5874             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5875         }
5876         var h = function(e){
5877             e = Roo.EventObject.setEvent(e);
5878             var t;
5879             if(o.delegate){
5880                 t = e.getTarget(o.delegate, el);
5881                 if(!t){
5882                     return;
5883                 }
5884             }else{
5885                 t = e.target;
5886             }
5887             if(o.stopEvent === true){
5888                 e.stopEvent();
5889             }
5890             if(o.preventDefault === true){
5891                e.preventDefault();
5892             }
5893             if(o.stopPropagation === true){
5894                 e.stopPropagation();
5895             }
5896
5897             if(o.normalized === false){
5898                 e = e.browserEvent;
5899             }
5900
5901             fn.call(scope || el, e, t, o);
5902         };
5903         if(o.delay){
5904             h = createDelayed(h, o);
5905         }
5906         if(o.single){
5907             h = createSingle(h, el, ename, fn);
5908         }
5909         if(o.buffer){
5910             h = createBuffered(h, o);
5911         }
5912         fn._handlers = fn._handlers || [];
5913         fn._handlers.push([Roo.id(el), ename, h]);
5914
5915         E.on(el, ename, h);
5916         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5917             el.addEventListener("DOMMouseScroll", h, false);
5918             E.on(window, 'unload', function(){
5919                 el.removeEventListener("DOMMouseScroll", h, false);
5920             });
5921         }
5922         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5923             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5924         }
5925         return h;
5926     };
5927
5928     var stopListening = function(el, ename, fn){
5929         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5930         if(hds){
5931             for(var i = 0, len = hds.length; i < len; i++){
5932                 var h = hds[i];
5933                 if(h[0] == id && h[1] == ename){
5934                     hd = h[2];
5935                     hds.splice(i, 1);
5936                     break;
5937                 }
5938             }
5939         }
5940         E.un(el, ename, hd);
5941         el = Roo.getDom(el);
5942         if(ename == "mousewheel" && el.addEventListener){
5943             el.removeEventListener("DOMMouseScroll", hd, false);
5944         }
5945         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5946             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5947         }
5948     };
5949
5950     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5951     
5952     var pub = {
5953         
5954         
5955         /** 
5956          * Fix for doc tools
5957          * @scope Roo.EventManager
5958          */
5959         
5960         
5961         /** 
5962          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5963          * object with a Roo.EventObject
5964          * @param {Function} fn        The method the event invokes
5965          * @param {Object}   scope    An object that becomes the scope of the handler
5966          * @param {boolean}  override If true, the obj passed in becomes
5967          *                             the execution scope of the listener
5968          * @return {Function} The wrapped function
5969          * @deprecated
5970          */
5971         wrap : function(fn, scope, override){
5972             return function(e){
5973                 Roo.EventObject.setEvent(e);
5974                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5975             };
5976         },
5977         
5978         /**
5979      * Appends an event handler to an element (shorthand for addListener)
5980      * @param {String/HTMLElement}   element        The html element or id to assign the
5981      * @param {String}   eventName The type of event to listen for
5982      * @param {Function} handler The method the event invokes
5983      * @param {Object}   scope (optional) The scope in which to execute the handler
5984      * function. The handler function's "this" context.
5985      * @param {Object}   options (optional) An object containing handler configuration
5986      * properties. This may contain any of the following properties:<ul>
5987      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5988      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5989      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5990      * <li>preventDefault {Boolean} True to prevent the default action</li>
5991      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5992      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5993      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5994      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5995      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5996      * by the specified number of milliseconds. If the event fires again within that time, the original
5997      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5998      * </ul><br>
5999      * <p>
6000      * <b>Combining Options</b><br>
6001      * Using the options argument, it is possible to combine different types of listeners:<br>
6002      * <br>
6003      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6004      * Code:<pre><code>
6005 el.on('click', this.onClick, this, {
6006     single: true,
6007     delay: 100,
6008     stopEvent : true,
6009     forumId: 4
6010 });</code></pre>
6011      * <p>
6012      * <b>Attaching multiple handlers in 1 call</b><br>
6013       * The method also allows for a single argument to be passed which is a config object containing properties
6014      * which specify multiple handlers.
6015      * <p>
6016      * Code:<pre><code>
6017 el.on({
6018     'click' : {
6019         fn: this.onClick
6020         scope: this,
6021         delay: 100
6022     },
6023     'mouseover' : {
6024         fn: this.onMouseOver
6025         scope: this
6026     },
6027     'mouseout' : {
6028         fn: this.onMouseOut
6029         scope: this
6030     }
6031 });</code></pre>
6032      * <p>
6033      * Or a shorthand syntax:<br>
6034      * Code:<pre><code>
6035 el.on({
6036     'click' : this.onClick,
6037     'mouseover' : this.onMouseOver,
6038     'mouseout' : this.onMouseOut
6039     scope: this
6040 });</code></pre>
6041      */
6042         addListener : function(element, eventName, fn, scope, options){
6043             if(typeof eventName == "object"){
6044                 var o = eventName;
6045                 for(var e in o){
6046                     if(propRe.test(e)){
6047                         continue;
6048                     }
6049                     if(typeof o[e] == "function"){
6050                         // shared options
6051                         listen(element, e, o, o[e], o.scope);
6052                     }else{
6053                         // individual options
6054                         listen(element, e, o[e]);
6055                     }
6056                 }
6057                 return;
6058             }
6059             return listen(element, eventName, options, fn, scope);
6060         },
6061         
6062         /**
6063          * Removes an event handler
6064          *
6065          * @param {String/HTMLElement}   element        The id or html element to remove the 
6066          *                             event from
6067          * @param {String}   eventName     The type of event
6068          * @param {Function} fn
6069          * @return {Boolean} True if a listener was actually removed
6070          */
6071         removeListener : function(element, eventName, fn){
6072             return stopListening(element, eventName, fn);
6073         },
6074         
6075         /**
6076          * Fires when the document is ready (before onload and before images are loaded). Can be 
6077          * accessed shorthanded Roo.onReady().
6078          * @param {Function} fn        The method the event invokes
6079          * @param {Object}   scope    An  object that becomes the scope of the handler
6080          * @param {boolean}  options
6081          */
6082         onDocumentReady : function(fn, scope, options){
6083             if(docReadyState){ // if it already fired
6084                 docReadyEvent.addListener(fn, scope, options);
6085                 docReadyEvent.fire();
6086                 docReadyEvent.clearListeners();
6087                 return;
6088             }
6089             if(!docReadyEvent){
6090                 initDocReady();
6091             }
6092             docReadyEvent.addListener(fn, scope, options);
6093         },
6094         
6095         /**
6096          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6097          * @param {Function} fn        The method the event invokes
6098          * @param {Object}   scope    An object that becomes the scope of the handler
6099          * @param {boolean}  options
6100          */
6101         onWindowResize : function(fn, scope, options){
6102             if(!resizeEvent){
6103                 resizeEvent = new Roo.util.Event();
6104                 resizeTask = new Roo.util.DelayedTask(function(){
6105                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6106                 });
6107                 E.on(window, "resize", function(){
6108                     if(Roo.isIE){
6109                         resizeTask.delay(50);
6110                     }else{
6111                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6112                     }
6113                 });
6114             }
6115             resizeEvent.addListener(fn, scope, options);
6116         },
6117
6118         /**
6119          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6120          * @param {Function} fn        The method the event invokes
6121          * @param {Object}   scope    An object that becomes the scope of the handler
6122          * @param {boolean}  options
6123          */
6124         onTextResize : function(fn, scope, options){
6125             if(!textEvent){
6126                 textEvent = new Roo.util.Event();
6127                 var textEl = new Roo.Element(document.createElement('div'));
6128                 textEl.dom.className = 'x-text-resize';
6129                 textEl.dom.innerHTML = 'X';
6130                 textEl.appendTo(document.body);
6131                 textSize = textEl.dom.offsetHeight;
6132                 setInterval(function(){
6133                     if(textEl.dom.offsetHeight != textSize){
6134                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6135                     }
6136                 }, this.textResizeInterval);
6137             }
6138             textEvent.addListener(fn, scope, options);
6139         },
6140
6141         /**
6142          * Removes the passed window resize listener.
6143          * @param {Function} fn        The method the event invokes
6144          * @param {Object}   scope    The scope of handler
6145          */
6146         removeResizeListener : function(fn, scope){
6147             if(resizeEvent){
6148                 resizeEvent.removeListener(fn, scope);
6149             }
6150         },
6151
6152         // private
6153         fireResize : function(){
6154             if(resizeEvent){
6155                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6156             }   
6157         },
6158         /**
6159          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6160          */
6161         ieDeferSrc : false,
6162         /**
6163          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6164          */
6165         textResizeInterval : 50
6166     };
6167     
6168     /**
6169      * Fix for doc tools
6170      * @scopeAlias pub=Roo.EventManager
6171      */
6172     
6173      /**
6174      * Appends an event handler to an element (shorthand for addListener)
6175      * @param {String/HTMLElement}   element        The html element or id to assign the
6176      * @param {String}   eventName The type of event to listen for
6177      * @param {Function} handler The method the event invokes
6178      * @param {Object}   scope (optional) The scope in which to execute the handler
6179      * function. The handler function's "this" context.
6180      * @param {Object}   options (optional) An object containing handler configuration
6181      * properties. This may contain any of the following properties:<ul>
6182      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6183      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6184      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6185      * <li>preventDefault {Boolean} True to prevent the default action</li>
6186      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6187      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6188      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6189      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6190      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6191      * by the specified number of milliseconds. If the event fires again within that time, the original
6192      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6193      * </ul><br>
6194      * <p>
6195      * <b>Combining Options</b><br>
6196      * Using the options argument, it is possible to combine different types of listeners:<br>
6197      * <br>
6198      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6199      * Code:<pre><code>
6200 el.on('click', this.onClick, this, {
6201     single: true,
6202     delay: 100,
6203     stopEvent : true,
6204     forumId: 4
6205 });</code></pre>
6206      * <p>
6207      * <b>Attaching multiple handlers in 1 call</b><br>
6208       * The method also allows for a single argument to be passed which is a config object containing properties
6209      * which specify multiple handlers.
6210      * <p>
6211      * Code:<pre><code>
6212 el.on({
6213     'click' : {
6214         fn: this.onClick
6215         scope: this,
6216         delay: 100
6217     },
6218     'mouseover' : {
6219         fn: this.onMouseOver
6220         scope: this
6221     },
6222     'mouseout' : {
6223         fn: this.onMouseOut
6224         scope: this
6225     }
6226 });</code></pre>
6227      * <p>
6228      * Or a shorthand syntax:<br>
6229      * Code:<pre><code>
6230 el.on({
6231     'click' : this.onClick,
6232     'mouseover' : this.onMouseOver,
6233     'mouseout' : this.onMouseOut
6234     scope: this
6235 });</code></pre>
6236      */
6237     pub.on = pub.addListener;
6238     pub.un = pub.removeListener;
6239
6240     pub.stoppedMouseDownEvent = new Roo.util.Event();
6241     return pub;
6242 }();
6243 /**
6244   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6245   * @param {Function} fn        The method the event invokes
6246   * @param {Object}   scope    An  object that becomes the scope of the handler
6247   * @param {boolean}  override If true, the obj passed in becomes
6248   *                             the execution scope of the listener
6249   * @member Roo
6250   * @method onReady
6251  */
6252 Roo.onReady = Roo.EventManager.onDocumentReady;
6253
6254 Roo.onReady(function(){
6255     var bd = Roo.get(document.body);
6256     if(!bd){ return; }
6257
6258     var cls = [
6259             Roo.isIE ? "roo-ie"
6260             : Roo.isGecko ? "roo-gecko"
6261             : Roo.isOpera ? "roo-opera"
6262             : Roo.isSafari ? "roo-safari" : ""];
6263
6264     if(Roo.isMac){
6265         cls.push("roo-mac");
6266     }
6267     if(Roo.isLinux){
6268         cls.push("roo-linux");
6269     }
6270     if(Roo.isBorderBox){
6271         cls.push('roo-border-box');
6272     }
6273     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6274         var p = bd.dom.parentNode;
6275         if(p){
6276             p.className += ' roo-strict';
6277         }
6278     }
6279     bd.addClass(cls.join(' '));
6280 });
6281
6282 /**
6283  * @class Roo.EventObject
6284  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6285  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6286  * Example:
6287  * <pre><code>
6288  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6289     e.preventDefault();
6290     var target = e.getTarget();
6291     ...
6292  }
6293  var myDiv = Roo.get("myDiv");
6294  myDiv.on("click", handleClick);
6295  //or
6296  Roo.EventManager.on("myDiv", 'click', handleClick);
6297  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6298  </code></pre>
6299  * @singleton
6300  */
6301 Roo.EventObject = function(){
6302     
6303     var E = Roo.lib.Event;
6304     
6305     // safari keypress events for special keys return bad keycodes
6306     var safariKeys = {
6307         63234 : 37, // left
6308         63235 : 39, // right
6309         63232 : 38, // up
6310         63233 : 40, // down
6311         63276 : 33, // page up
6312         63277 : 34, // page down
6313         63272 : 46, // delete
6314         63273 : 36, // home
6315         63275 : 35  // end
6316     };
6317
6318     // normalize button clicks
6319     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6320                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6321
6322     Roo.EventObjectImpl = function(e){
6323         if(e){
6324             this.setEvent(e.browserEvent || e);
6325         }
6326     };
6327     Roo.EventObjectImpl.prototype = {
6328         /**
6329          * Used to fix doc tools.
6330          * @scope Roo.EventObject.prototype
6331          */
6332             
6333
6334         
6335         
6336         /** The normal browser event */
6337         browserEvent : null,
6338         /** The button pressed in a mouse event */
6339         button : -1,
6340         /** True if the shift key was down during the event */
6341         shiftKey : false,
6342         /** True if the control key was down during the event */
6343         ctrlKey : false,
6344         /** True if the alt key was down during the event */
6345         altKey : false,
6346
6347         /** Key constant 
6348         * @type Number */
6349         BACKSPACE : 8,
6350         /** Key constant 
6351         * @type Number */
6352         TAB : 9,
6353         /** Key constant 
6354         * @type Number */
6355         RETURN : 13,
6356         /** Key constant 
6357         * @type Number */
6358         ENTER : 13,
6359         /** Key constant 
6360         * @type Number */
6361         SHIFT : 16,
6362         /** Key constant 
6363         * @type Number */
6364         CONTROL : 17,
6365         /** Key constant 
6366         * @type Number */
6367         ESC : 27,
6368         /** Key constant 
6369         * @type Number */
6370         SPACE : 32,
6371         /** Key constant 
6372         * @type Number */
6373         PAGEUP : 33,
6374         /** Key constant 
6375         * @type Number */
6376         PAGEDOWN : 34,
6377         /** Key constant 
6378         * @type Number */
6379         END : 35,
6380         /** Key constant 
6381         * @type Number */
6382         HOME : 36,
6383         /** Key constant 
6384         * @type Number */
6385         LEFT : 37,
6386         /** Key constant 
6387         * @type Number */
6388         UP : 38,
6389         /** Key constant 
6390         * @type Number */
6391         RIGHT : 39,
6392         /** Key constant 
6393         * @type Number */
6394         DOWN : 40,
6395         /** Key constant 
6396         * @type Number */
6397         DELETE : 46,
6398         /** Key constant 
6399         * @type Number */
6400         F5 : 116,
6401
6402            /** @private */
6403         setEvent : function(e){
6404             if(e == this || (e && e.browserEvent)){ // already wrapped
6405                 return e;
6406             }
6407             this.browserEvent = e;
6408             if(e){
6409                 // normalize buttons
6410                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6411                 if(e.type == 'click' && this.button == -1){
6412                     this.button = 0;
6413                 }
6414                 this.type = e.type;
6415                 this.shiftKey = e.shiftKey;
6416                 // mac metaKey behaves like ctrlKey
6417                 this.ctrlKey = e.ctrlKey || e.metaKey;
6418                 this.altKey = e.altKey;
6419                 // in getKey these will be normalized for the mac
6420                 this.keyCode = e.keyCode;
6421                 // keyup warnings on firefox.
6422                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6423                 // cache the target for the delayed and or buffered events
6424                 this.target = E.getTarget(e);
6425                 // same for XY
6426                 this.xy = E.getXY(e);
6427             }else{
6428                 this.button = -1;
6429                 this.shiftKey = false;
6430                 this.ctrlKey = false;
6431                 this.altKey = false;
6432                 this.keyCode = 0;
6433                 this.charCode =0;
6434                 this.target = null;
6435                 this.xy = [0, 0];
6436             }
6437             return this;
6438         },
6439
6440         /**
6441          * Stop the event (preventDefault and stopPropagation)
6442          */
6443         stopEvent : function(){
6444             if(this.browserEvent){
6445                 if(this.browserEvent.type == 'mousedown'){
6446                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6447                 }
6448                 E.stopEvent(this.browserEvent);
6449             }
6450         },
6451
6452         /**
6453          * Prevents the browsers default handling of the event.
6454          */
6455         preventDefault : function(){
6456             if(this.browserEvent){
6457                 E.preventDefault(this.browserEvent);
6458             }
6459         },
6460
6461         /** @private */
6462         isNavKeyPress : function(){
6463             var k = this.keyCode;
6464             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6465             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6466         },
6467
6468         isSpecialKey : function(){
6469             var k = this.keyCode;
6470             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6471             (k == 16) || (k == 17) ||
6472             (k >= 18 && k <= 20) ||
6473             (k >= 33 && k <= 35) ||
6474             (k >= 36 && k <= 39) ||
6475             (k >= 44 && k <= 45);
6476         },
6477         /**
6478          * Cancels bubbling of the event.
6479          */
6480         stopPropagation : function(){
6481             if(this.browserEvent){
6482                 if(this.type == 'mousedown'){
6483                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6484                 }
6485                 E.stopPropagation(this.browserEvent);
6486             }
6487         },
6488
6489         /**
6490          * Gets the key code for the event.
6491          * @return {Number}
6492          */
6493         getCharCode : function(){
6494             return this.charCode || this.keyCode;
6495         },
6496
6497         /**
6498          * Returns a normalized keyCode for the event.
6499          * @return {Number} The key code
6500          */
6501         getKey : function(){
6502             var k = this.keyCode || this.charCode;
6503             return Roo.isSafari ? (safariKeys[k] || k) : k;
6504         },
6505
6506         /**
6507          * Gets the x coordinate of the event.
6508          * @return {Number}
6509          */
6510         getPageX : function(){
6511             return this.xy[0];
6512         },
6513
6514         /**
6515          * Gets the y coordinate of the event.
6516          * @return {Number}
6517          */
6518         getPageY : function(){
6519             return this.xy[1];
6520         },
6521
6522         /**
6523          * Gets the time of the event.
6524          * @return {Number}
6525          */
6526         getTime : function(){
6527             if(this.browserEvent){
6528                 return E.getTime(this.browserEvent);
6529             }
6530             return null;
6531         },
6532
6533         /**
6534          * Gets the page coordinates of the event.
6535          * @return {Array} The xy values like [x, y]
6536          */
6537         getXY : function(){
6538             return this.xy;
6539         },
6540
6541         /**
6542          * Gets the target for the event.
6543          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6544          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6545                 search as a number or element (defaults to 10 || document.body)
6546          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6547          * @return {HTMLelement}
6548          */
6549         getTarget : function(selector, maxDepth, returnEl){
6550             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6551         },
6552         /**
6553          * Gets the related target.
6554          * @return {HTMLElement}
6555          */
6556         getRelatedTarget : function(){
6557             if(this.browserEvent){
6558                 return E.getRelatedTarget(this.browserEvent);
6559             }
6560             return null;
6561         },
6562
6563         /**
6564          * Normalizes mouse wheel delta across browsers
6565          * @return {Number} The delta
6566          */
6567         getWheelDelta : function(){
6568             var e = this.browserEvent;
6569             var delta = 0;
6570             if(e.wheelDelta){ /* IE/Opera. */
6571                 delta = e.wheelDelta/120;
6572             }else if(e.detail){ /* Mozilla case. */
6573                 delta = -e.detail/3;
6574             }
6575             return delta;
6576         },
6577
6578         /**
6579          * Returns true if the control, meta, shift or alt key was pressed during this event.
6580          * @return {Boolean}
6581          */
6582         hasModifier : function(){
6583             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6584         },
6585
6586         /**
6587          * Returns true if the target of this event equals el or is a child of el
6588          * @param {String/HTMLElement/Element} el
6589          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6590          * @return {Boolean}
6591          */
6592         within : function(el, related){
6593             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6594             return t && Roo.fly(el).contains(t);
6595         },
6596
6597         getPoint : function(){
6598             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6599         }
6600     };
6601
6602     return new Roo.EventObjectImpl();
6603 }();
6604             
6605     /*
6606  * Based on:
6607  * Ext JS Library 1.1.1
6608  * Copyright(c) 2006-2007, Ext JS, LLC.
6609  *
6610  * Originally Released Under LGPL - original licence link has changed is not relivant.
6611  *
6612  * Fork - LGPL
6613  * <script type="text/javascript">
6614  */
6615
6616  
6617 // was in Composite Element!??!?!
6618  
6619 (function(){
6620     var D = Roo.lib.Dom;
6621     var E = Roo.lib.Event;
6622     var A = Roo.lib.Anim;
6623
6624     // local style camelizing for speed
6625     var propCache = {};
6626     var camelRe = /(-[a-z])/gi;
6627     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6628     var view = document.defaultView;
6629
6630 /**
6631  * @class Roo.Element
6632  * Represents an Element in the DOM.<br><br>
6633  * Usage:<br>
6634 <pre><code>
6635 var el = Roo.get("my-div");
6636
6637 // or with getEl
6638 var el = getEl("my-div");
6639
6640 // or with a DOM element
6641 var el = Roo.get(myDivElement);
6642 </code></pre>
6643  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6644  * each call instead of constructing a new one.<br><br>
6645  * <b>Animations</b><br />
6646  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6647  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6648 <pre>
6649 Option    Default   Description
6650 --------- --------  ---------------------------------------------
6651 duration  .35       The duration of the animation in seconds
6652 easing    easeOut   The YUI easing method
6653 callback  none      A function to execute when the anim completes
6654 scope     this      The scope (this) of the callback function
6655 </pre>
6656 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6657 * manipulate the animation. Here's an example:
6658 <pre><code>
6659 var el = Roo.get("my-div");
6660
6661 // no animation
6662 el.setWidth(100);
6663
6664 // default animation
6665 el.setWidth(100, true);
6666
6667 // animation with some options set
6668 el.setWidth(100, {
6669     duration: 1,
6670     callback: this.foo,
6671     scope: this
6672 });
6673
6674 // using the "anim" property to get the Anim object
6675 var opt = {
6676     duration: 1,
6677     callback: this.foo,
6678     scope: this
6679 };
6680 el.setWidth(100, opt);
6681 ...
6682 if(opt.anim.isAnimated()){
6683     opt.anim.stop();
6684 }
6685 </code></pre>
6686 * <b> Composite (Collections of) Elements</b><br />
6687  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6688  * @constructor Create a new Element directly.
6689  * @param {String/HTMLElement} element
6690  * @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).
6691  */
6692     Roo.Element = function(element, forceNew){
6693         var dom = typeof element == "string" ?
6694                 document.getElementById(element) : element;
6695         if(!dom){ // invalid id/element
6696             return null;
6697         }
6698         var id = dom.id;
6699         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6700             return Roo.Element.cache[id];
6701         }
6702
6703         /**
6704          * The DOM element
6705          * @type HTMLElement
6706          */
6707         this.dom = dom;
6708
6709         /**
6710          * The DOM element ID
6711          * @type String
6712          */
6713         this.id = id || Roo.id(dom);
6714     };
6715
6716     var El = Roo.Element;
6717
6718     El.prototype = {
6719         /**
6720          * The element's default display mode  (defaults to "")
6721          * @type String
6722          */
6723         originalDisplay : "",
6724
6725         visibilityMode : 1,
6726         /**
6727          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6728          * @type String
6729          */
6730         defaultUnit : "px",
6731         /**
6732          * Sets the element's visibility mode. When setVisible() is called it
6733          * will use this to determine whether to set the visibility or the display property.
6734          * @param visMode Element.VISIBILITY or Element.DISPLAY
6735          * @return {Roo.Element} this
6736          */
6737         setVisibilityMode : function(visMode){
6738             this.visibilityMode = visMode;
6739             return this;
6740         },
6741         /**
6742          * Convenience method for setVisibilityMode(Element.DISPLAY)
6743          * @param {String} display (optional) What to set display to when visible
6744          * @return {Roo.Element} this
6745          */
6746         enableDisplayMode : function(display){
6747             this.setVisibilityMode(El.DISPLAY);
6748             if(typeof display != "undefined") this.originalDisplay = display;
6749             return this;
6750         },
6751
6752         /**
6753          * 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)
6754          * @param {String} selector The simple selector to test
6755          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6756                 search as a number or element (defaults to 10 || document.body)
6757          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6758          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6759          */
6760         findParent : function(simpleSelector, maxDepth, returnEl){
6761             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6762             maxDepth = maxDepth || 50;
6763             if(typeof maxDepth != "number"){
6764                 stopEl = Roo.getDom(maxDepth);
6765                 maxDepth = 10;
6766             }
6767             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6768                 if(dq.is(p, simpleSelector)){
6769                     return returnEl ? Roo.get(p) : p;
6770                 }
6771                 depth++;
6772                 p = p.parentNode;
6773             }
6774             return null;
6775         },
6776
6777
6778         /**
6779          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6780          * @param {String} selector The simple selector to test
6781          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6782                 search as a number or element (defaults to 10 || document.body)
6783          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6784          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6785          */
6786         findParentNode : function(simpleSelector, maxDepth, returnEl){
6787             var p = Roo.fly(this.dom.parentNode, '_internal');
6788             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6789         },
6790
6791         /**
6792          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6793          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6794          * @param {String} selector The simple selector to test
6795          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6796                 search as a number or element (defaults to 10 || document.body)
6797          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6798          */
6799         up : function(simpleSelector, maxDepth){
6800             return this.findParentNode(simpleSelector, maxDepth, true);
6801         },
6802
6803
6804
6805         /**
6806          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6807          * @param {String} selector The simple selector to test
6808          * @return {Boolean} True if this element matches the selector, else false
6809          */
6810         is : function(simpleSelector){
6811             return Roo.DomQuery.is(this.dom, simpleSelector);
6812         },
6813
6814         /**
6815          * Perform animation on this element.
6816          * @param {Object} args The YUI animation control args
6817          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6818          * @param {Function} onComplete (optional) Function to call when animation completes
6819          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6820          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6821          * @return {Roo.Element} this
6822          */
6823         animate : function(args, duration, onComplete, easing, animType){
6824             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6825             return this;
6826         },
6827
6828         /*
6829          * @private Internal animation call
6830          */
6831         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6832             animType = animType || 'run';
6833             opt = opt || {};
6834             var anim = Roo.lib.Anim[animType](
6835                 this.dom, args,
6836                 (opt.duration || defaultDur) || .35,
6837                 (opt.easing || defaultEase) || 'easeOut',
6838                 function(){
6839                     Roo.callback(cb, this);
6840                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6841                 },
6842                 this
6843             );
6844             opt.anim = anim;
6845             return anim;
6846         },
6847
6848         // private legacy anim prep
6849         preanim : function(a, i){
6850             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6851         },
6852
6853         /**
6854          * Removes worthless text nodes
6855          * @param {Boolean} forceReclean (optional) By default the element
6856          * keeps track if it has been cleaned already so
6857          * you can call this over and over. However, if you update the element and
6858          * need to force a reclean, you can pass true.
6859          */
6860         clean : function(forceReclean){
6861             if(this.isCleaned && forceReclean !== true){
6862                 return this;
6863             }
6864             var ns = /\S/;
6865             var d = this.dom, n = d.firstChild, ni = -1;
6866             while(n){
6867                 var nx = n.nextSibling;
6868                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6869                     d.removeChild(n);
6870                 }else{
6871                     n.nodeIndex = ++ni;
6872                 }
6873                 n = nx;
6874             }
6875             this.isCleaned = true;
6876             return this;
6877         },
6878
6879         // private
6880         calcOffsetsTo : function(el){
6881             el = Roo.get(el);
6882             var d = el.dom;
6883             var restorePos = false;
6884             if(el.getStyle('position') == 'static'){
6885                 el.position('relative');
6886                 restorePos = true;
6887             }
6888             var x = 0, y =0;
6889             var op = this.dom;
6890             while(op && op != d && op.tagName != 'HTML'){
6891                 x+= op.offsetLeft;
6892                 y+= op.offsetTop;
6893                 op = op.offsetParent;
6894             }
6895             if(restorePos){
6896                 el.position('static');
6897             }
6898             return [x, y];
6899         },
6900
6901         /**
6902          * Scrolls this element into view within the passed container.
6903          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6904          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6905          * @return {Roo.Element} this
6906          */
6907         scrollIntoView : function(container, hscroll){
6908             var c = Roo.getDom(container) || document.body;
6909             var el = this.dom;
6910
6911             var o = this.calcOffsetsTo(c),
6912                 l = o[0],
6913                 t = o[1],
6914                 b = t+el.offsetHeight,
6915                 r = l+el.offsetWidth;
6916
6917             var ch = c.clientHeight;
6918             var ct = parseInt(c.scrollTop, 10);
6919             var cl = parseInt(c.scrollLeft, 10);
6920             var cb = ct + ch;
6921             var cr = cl + c.clientWidth;
6922
6923             if(t < ct){
6924                 c.scrollTop = t;
6925             }else if(b > cb){
6926                 c.scrollTop = b-ch;
6927             }
6928
6929             if(hscroll !== false){
6930                 if(l < cl){
6931                     c.scrollLeft = l;
6932                 }else if(r > cr){
6933                     c.scrollLeft = r-c.clientWidth;
6934                 }
6935             }
6936             return this;
6937         },
6938
6939         // private
6940         scrollChildIntoView : function(child, hscroll){
6941             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6942         },
6943
6944         /**
6945          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6946          * the new height may not be available immediately.
6947          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6948          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6949          * @param {Function} onComplete (optional) Function to call when animation completes
6950          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6951          * @return {Roo.Element} this
6952          */
6953         autoHeight : function(animate, duration, onComplete, easing){
6954             var oldHeight = this.getHeight();
6955             this.clip();
6956             this.setHeight(1); // force clipping
6957             setTimeout(function(){
6958                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6959                 if(!animate){
6960                     this.setHeight(height);
6961                     this.unclip();
6962                     if(typeof onComplete == "function"){
6963                         onComplete();
6964                     }
6965                 }else{
6966                     this.setHeight(oldHeight); // restore original height
6967                     this.setHeight(height, animate, duration, function(){
6968                         this.unclip();
6969                         if(typeof onComplete == "function") onComplete();
6970                     }.createDelegate(this), easing);
6971                 }
6972             }.createDelegate(this), 0);
6973             return this;
6974         },
6975
6976         /**
6977          * Returns true if this element is an ancestor of the passed element
6978          * @param {HTMLElement/String} el The element to check
6979          * @return {Boolean} True if this element is an ancestor of el, else false
6980          */
6981         contains : function(el){
6982             if(!el){return false;}
6983             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6984         },
6985
6986         /**
6987          * Checks whether the element is currently visible using both visibility and display properties.
6988          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6989          * @return {Boolean} True if the element is currently visible, else false
6990          */
6991         isVisible : function(deep) {
6992             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6993             if(deep !== true || !vis){
6994                 return vis;
6995             }
6996             var p = this.dom.parentNode;
6997             while(p && p.tagName.toLowerCase() != "body"){
6998                 if(!Roo.fly(p, '_isVisible').isVisible()){
6999                     return false;
7000                 }
7001                 p = p.parentNode;
7002             }
7003             return true;
7004         },
7005
7006         /**
7007          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7008          * @param {String} selector The CSS selector
7009          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7010          * @return {CompositeElement/CompositeElementLite} The composite element
7011          */
7012         select : function(selector, unique){
7013             return El.select(selector, unique, this.dom);
7014         },
7015
7016         /**
7017          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7018          * @param {String} selector The CSS selector
7019          * @return {Array} An array of the matched nodes
7020          */
7021         query : function(selector, unique){
7022             return Roo.DomQuery.select(selector, this.dom);
7023         },
7024
7025         /**
7026          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7027          * @param {String} selector The CSS selector
7028          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7029          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7030          */
7031         child : function(selector, returnDom){
7032             var n = Roo.DomQuery.selectNode(selector, this.dom);
7033             return returnDom ? n : Roo.get(n);
7034         },
7035
7036         /**
7037          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7038          * @param {String} selector The CSS selector
7039          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7040          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7041          */
7042         down : function(selector, returnDom){
7043             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7044             return returnDom ? n : Roo.get(n);
7045         },
7046
7047         /**
7048          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7049          * @param {String} group The group the DD object is member of
7050          * @param {Object} config The DD config object
7051          * @param {Object} overrides An object containing methods to override/implement on the DD object
7052          * @return {Roo.dd.DD} The DD object
7053          */
7054         initDD : function(group, config, overrides){
7055             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7056             return Roo.apply(dd, overrides);
7057         },
7058
7059         /**
7060          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7061          * @param {String} group The group the DDProxy object is member of
7062          * @param {Object} config The DDProxy config object
7063          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7064          * @return {Roo.dd.DDProxy} The DDProxy object
7065          */
7066         initDDProxy : function(group, config, overrides){
7067             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7068             return Roo.apply(dd, overrides);
7069         },
7070
7071         /**
7072          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7073          * @param {String} group The group the DDTarget object is member of
7074          * @param {Object} config The DDTarget config object
7075          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7076          * @return {Roo.dd.DDTarget} The DDTarget object
7077          */
7078         initDDTarget : function(group, config, overrides){
7079             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7080             return Roo.apply(dd, overrides);
7081         },
7082
7083         /**
7084          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7085          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7086          * @param {Boolean} visible Whether the element is visible
7087          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7088          * @return {Roo.Element} this
7089          */
7090          setVisible : function(visible, animate){
7091             if(!animate || !A){
7092                 if(this.visibilityMode == El.DISPLAY){
7093                     this.setDisplayed(visible);
7094                 }else{
7095                     this.fixDisplay();
7096                     this.dom.style.visibility = visible ? "visible" : "hidden";
7097                 }
7098             }else{
7099                 // closure for composites
7100                 var dom = this.dom;
7101                 var visMode = this.visibilityMode;
7102                 if(visible){
7103                     this.setOpacity(.01);
7104                     this.setVisible(true);
7105                 }
7106                 this.anim({opacity: { to: (visible?1:0) }},
7107                       this.preanim(arguments, 1),
7108                       null, .35, 'easeIn', function(){
7109                          if(!visible){
7110                              if(visMode == El.DISPLAY){
7111                                  dom.style.display = "none";
7112                              }else{
7113                                  dom.style.visibility = "hidden";
7114                              }
7115                              Roo.get(dom).setOpacity(1);
7116                          }
7117                      });
7118             }
7119             return this;
7120         },
7121
7122         /**
7123          * Returns true if display is not "none"
7124          * @return {Boolean}
7125          */
7126         isDisplayed : function() {
7127             return this.getStyle("display") != "none";
7128         },
7129
7130         /**
7131          * Toggles the element's visibility or display, depending on visibility mode.
7132          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7133          * @return {Roo.Element} this
7134          */
7135         toggle : function(animate){
7136             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7137             return this;
7138         },
7139
7140         /**
7141          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7142          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7143          * @return {Roo.Element} this
7144          */
7145         setDisplayed : function(value) {
7146             if(typeof value == "boolean"){
7147                value = value ? this.originalDisplay : "none";
7148             }
7149             this.setStyle("display", value);
7150             return this;
7151         },
7152
7153         /**
7154          * Tries to focus the element. Any exceptions are caught and ignored.
7155          * @return {Roo.Element} this
7156          */
7157         focus : function() {
7158             try{
7159                 this.dom.focus();
7160             }catch(e){}
7161             return this;
7162         },
7163
7164         /**
7165          * Tries to blur the element. Any exceptions are caught and ignored.
7166          * @return {Roo.Element} this
7167          */
7168         blur : function() {
7169             try{
7170                 this.dom.blur();
7171             }catch(e){}
7172             return this;
7173         },
7174
7175         /**
7176          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7177          * @param {String/Array} className The CSS class to add, or an array of classes
7178          * @return {Roo.Element} this
7179          */
7180         addClass : function(className){
7181             if(className instanceof Array){
7182                 for(var i = 0, len = className.length; i < len; i++) {
7183                     this.addClass(className[i]);
7184                 }
7185             }else{
7186                 if(className && !this.hasClass(className)){
7187                     this.dom.className = this.dom.className + " " + className;
7188                 }
7189             }
7190             return this;
7191         },
7192
7193         /**
7194          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7195          * @param {String/Array} className The CSS class to add, or an array of classes
7196          * @return {Roo.Element} this
7197          */
7198         radioClass : function(className){
7199             var siblings = this.dom.parentNode.childNodes;
7200             for(var i = 0; i < siblings.length; i++) {
7201                 var s = siblings[i];
7202                 if(s.nodeType == 1){
7203                     Roo.get(s).removeClass(className);
7204                 }
7205             }
7206             this.addClass(className);
7207             return this;
7208         },
7209
7210         /**
7211          * Removes one or more CSS classes from the element.
7212          * @param {String/Array} className The CSS class to remove, or an array of classes
7213          * @return {Roo.Element} this
7214          */
7215         removeClass : function(className){
7216             if(!className || !this.dom.className){
7217                 return this;
7218             }
7219             if(className instanceof Array){
7220                 for(var i = 0, len = className.length; i < len; i++) {
7221                     this.removeClass(className[i]);
7222                 }
7223             }else{
7224                 if(this.hasClass(className)){
7225                     var re = this.classReCache[className];
7226                     if (!re) {
7227                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7228                        this.classReCache[className] = re;
7229                     }
7230                     this.dom.className =
7231                         this.dom.className.replace(re, " ");
7232                 }
7233             }
7234             return this;
7235         },
7236
7237         // private
7238         classReCache: {},
7239
7240         /**
7241          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7242          * @param {String} className The CSS class to toggle
7243          * @return {Roo.Element} this
7244          */
7245         toggleClass : function(className){
7246             if(this.hasClass(className)){
7247                 this.removeClass(className);
7248             }else{
7249                 this.addClass(className);
7250             }
7251             return this;
7252         },
7253
7254         /**
7255          * Checks if the specified CSS class exists on this element's DOM node.
7256          * @param {String} className The CSS class to check for
7257          * @return {Boolean} True if the class exists, else false
7258          */
7259         hasClass : function(className){
7260             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7261         },
7262
7263         /**
7264          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7265          * @param {String} oldClassName The CSS class to replace
7266          * @param {String} newClassName The replacement CSS class
7267          * @return {Roo.Element} this
7268          */
7269         replaceClass : function(oldClassName, newClassName){
7270             this.removeClass(oldClassName);
7271             this.addClass(newClassName);
7272             return this;
7273         },
7274
7275         /**
7276          * Returns an object with properties matching the styles requested.
7277          * For example, el.getStyles('color', 'font-size', 'width') might return
7278          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7279          * @param {String} style1 A style name
7280          * @param {String} style2 A style name
7281          * @param {String} etc.
7282          * @return {Object} The style object
7283          */
7284         getStyles : function(){
7285             var a = arguments, len = a.length, r = {};
7286             for(var i = 0; i < len; i++){
7287                 r[a[i]] = this.getStyle(a[i]);
7288             }
7289             return r;
7290         },
7291
7292         /**
7293          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7294          * @param {String} property The style property whose value is returned.
7295          * @return {String} The current value of the style property for this element.
7296          */
7297         getStyle : function(){
7298             return view && view.getComputedStyle ?
7299                 function(prop){
7300                     var el = this.dom, v, cs, camel;
7301                     if(prop == 'float'){
7302                         prop = "cssFloat";
7303                     }
7304                     if(el.style && (v = el.style[prop])){
7305                         return v;
7306                     }
7307                     if(cs = view.getComputedStyle(el, "")){
7308                         if(!(camel = propCache[prop])){
7309                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7310                         }
7311                         return cs[camel];
7312                     }
7313                     return null;
7314                 } :
7315                 function(prop){
7316                     var el = this.dom, v, cs, camel;
7317                     if(prop == 'opacity'){
7318                         if(typeof el.style.filter == 'string'){
7319                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7320                             if(m){
7321                                 var fv = parseFloat(m[1]);
7322                                 if(!isNaN(fv)){
7323                                     return fv ? fv / 100 : 0;
7324                                 }
7325                             }
7326                         }
7327                         return 1;
7328                     }else if(prop == 'float'){
7329                         prop = "styleFloat";
7330                     }
7331                     if(!(camel = propCache[prop])){
7332                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7333                     }
7334                     if(v = el.style[camel]){
7335                         return v;
7336                     }
7337                     if(cs = el.currentStyle){
7338                         return cs[camel];
7339                     }
7340                     return null;
7341                 };
7342         }(),
7343
7344         /**
7345          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7346          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7347          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7348          * @return {Roo.Element} this
7349          */
7350         setStyle : function(prop, value){
7351             if(typeof prop == "string"){
7352                 var camel;
7353                 if(!(camel = propCache[prop])){
7354                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7355                 }
7356                 if(camel == 'opacity') {
7357                     this.setOpacity(value);
7358                 }else{
7359                     this.dom.style[camel] = value;
7360                 }
7361             }else{
7362                 for(var style in prop){
7363                     if(typeof prop[style] != "function"){
7364                        this.setStyle(style, prop[style]);
7365                     }
7366                 }
7367             }
7368             return this;
7369         },
7370
7371         /**
7372          * More flexible version of {@link #setStyle} for setting style properties.
7373          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7374          * a function which returns such a specification.
7375          * @return {Roo.Element} this
7376          */
7377         applyStyles : function(style){
7378             Roo.DomHelper.applyStyles(this.dom, style);
7379             return this;
7380         },
7381
7382         /**
7383           * 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).
7384           * @return {Number} The X position of the element
7385           */
7386         getX : function(){
7387             return D.getX(this.dom);
7388         },
7389
7390         /**
7391           * 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).
7392           * @return {Number} The Y position of the element
7393           */
7394         getY : function(){
7395             return D.getY(this.dom);
7396         },
7397
7398         /**
7399           * 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).
7400           * @return {Array} The XY position of the element
7401           */
7402         getXY : function(){
7403             return D.getXY(this.dom);
7404         },
7405
7406         /**
7407          * 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).
7408          * @param {Number} The X position of the element
7409          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7410          * @return {Roo.Element} this
7411          */
7412         setX : function(x, animate){
7413             if(!animate || !A){
7414                 D.setX(this.dom, x);
7415             }else{
7416                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7417             }
7418             return this;
7419         },
7420
7421         /**
7422          * 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).
7423          * @param {Number} The Y position of the element
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         setY : function(y, animate){
7428             if(!animate || !A){
7429                 D.setY(this.dom, y);
7430             }else{
7431                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7432             }
7433             return this;
7434         },
7435
7436         /**
7437          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7438          * @param {String} left The left CSS property value
7439          * @return {Roo.Element} this
7440          */
7441         setLeft : function(left){
7442             this.setStyle("left", this.addUnits(left));
7443             return this;
7444         },
7445
7446         /**
7447          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7448          * @param {String} top The top CSS property value
7449          * @return {Roo.Element} this
7450          */
7451         setTop : function(top){
7452             this.setStyle("top", this.addUnits(top));
7453             return this;
7454         },
7455
7456         /**
7457          * Sets the element's CSS right style.
7458          * @param {String} right The right CSS property value
7459          * @return {Roo.Element} this
7460          */
7461         setRight : function(right){
7462             this.setStyle("right", this.addUnits(right));
7463             return this;
7464         },
7465
7466         /**
7467          * Sets the element's CSS bottom style.
7468          * @param {String} bottom The bottom CSS property value
7469          * @return {Roo.Element} this
7470          */
7471         setBottom : function(bottom){
7472             this.setStyle("bottom", this.addUnits(bottom));
7473             return this;
7474         },
7475
7476         /**
7477          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7478          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7479          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7480          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7481          * @return {Roo.Element} this
7482          */
7483         setXY : function(pos, animate){
7484             if(!animate || !A){
7485                 D.setXY(this.dom, pos);
7486             }else{
7487                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7488             }
7489             return this;
7490         },
7491
7492         /**
7493          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7494          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7495          * @param {Number} x X value for new position (coordinates are page-based)
7496          * @param {Number} y Y value for new position (coordinates are page-based)
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setLocation : function(x, y, animate){
7501             this.setXY([x, y], this.preanim(arguments, 2));
7502             return this;
7503         },
7504
7505         /**
7506          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7507          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7508          * @param {Number} x X value for new position (coordinates are page-based)
7509          * @param {Number} y Y value for new position (coordinates are page-based)
7510          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7511          * @return {Roo.Element} this
7512          */
7513         moveTo : function(x, y, animate){
7514             this.setXY([x, y], this.preanim(arguments, 2));
7515             return this;
7516         },
7517
7518         /**
7519          * Returns the region of the given element.
7520          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7521          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7522          */
7523         getRegion : function(){
7524             return D.getRegion(this.dom);
7525         },
7526
7527         /**
7528          * Returns the offset height of the element
7529          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7530          * @return {Number} The element's height
7531          */
7532         getHeight : function(contentHeight){
7533             var h = this.dom.offsetHeight || 0;
7534             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7535         },
7536
7537         /**
7538          * Returns the offset width of the element
7539          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7540          * @return {Number} The element's width
7541          */
7542         getWidth : function(contentWidth){
7543             var w = this.dom.offsetWidth || 0;
7544             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7545         },
7546
7547         /**
7548          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7549          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7550          * if a height has not been set using CSS.
7551          * @return {Number}
7552          */
7553         getComputedHeight : function(){
7554             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7555             if(!h){
7556                 h = parseInt(this.getStyle('height'), 10) || 0;
7557                 if(!this.isBorderBox()){
7558                     h += this.getFrameWidth('tb');
7559                 }
7560             }
7561             return h;
7562         },
7563
7564         /**
7565          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7566          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7567          * if a width has not been set using CSS.
7568          * @return {Number}
7569          */
7570         getComputedWidth : function(){
7571             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7572             if(!w){
7573                 w = parseInt(this.getStyle('width'), 10) || 0;
7574                 if(!this.isBorderBox()){
7575                     w += this.getFrameWidth('lr');
7576                 }
7577             }
7578             return w;
7579         },
7580
7581         /**
7582          * Returns the size of the element.
7583          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7584          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7585          */
7586         getSize : function(contentSize){
7587             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7588         },
7589
7590         /**
7591          * Returns the width and height of the viewport.
7592          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7593          */
7594         getViewSize : function(){
7595             var d = this.dom, doc = document, aw = 0, ah = 0;
7596             if(d == doc || d == doc.body){
7597                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7598             }else{
7599                 return {
7600                     width : d.clientWidth,
7601                     height: d.clientHeight
7602                 };
7603             }
7604         },
7605
7606         /**
7607          * Returns the value of the "value" attribute
7608          * @param {Boolean} asNumber true to parse the value as a number
7609          * @return {String/Number}
7610          */
7611         getValue : function(asNumber){
7612             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7613         },
7614
7615         // private
7616         adjustWidth : function(width){
7617             if(typeof width == "number"){
7618                 if(this.autoBoxAdjust && !this.isBorderBox()){
7619                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7620                 }
7621                 if(width < 0){
7622                     width = 0;
7623                 }
7624             }
7625             return width;
7626         },
7627
7628         // private
7629         adjustHeight : function(height){
7630             if(typeof height == "number"){
7631                if(this.autoBoxAdjust && !this.isBorderBox()){
7632                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7633                }
7634                if(height < 0){
7635                    height = 0;
7636                }
7637             }
7638             return height;
7639         },
7640
7641         /**
7642          * Set the width of the element
7643          * @param {Number} width The new width
7644          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7645          * @return {Roo.Element} this
7646          */
7647         setWidth : function(width, animate){
7648             width = this.adjustWidth(width);
7649             if(!animate || !A){
7650                 this.dom.style.width = this.addUnits(width);
7651             }else{
7652                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7653             }
7654             return this;
7655         },
7656
7657         /**
7658          * Set the height of the element
7659          * @param {Number} height The new height
7660          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7661          * @return {Roo.Element} this
7662          */
7663          setHeight : function(height, animate){
7664             height = this.adjustHeight(height);
7665             if(!animate || !A){
7666                 this.dom.style.height = this.addUnits(height);
7667             }else{
7668                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7669             }
7670             return this;
7671         },
7672
7673         /**
7674          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7675          * @param {Number} width The new width
7676          * @param {Number} height The new height
7677          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7678          * @return {Roo.Element} this
7679          */
7680          setSize : function(width, height, animate){
7681             if(typeof width == "object"){ // in case of object from getSize()
7682                 height = width.height; width = width.width;
7683             }
7684             width = this.adjustWidth(width); height = this.adjustHeight(height);
7685             if(!animate || !A){
7686                 this.dom.style.width = this.addUnits(width);
7687                 this.dom.style.height = this.addUnits(height);
7688             }else{
7689                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7690             }
7691             return this;
7692         },
7693
7694         /**
7695          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7696          * @param {Number} x X value for new position (coordinates are page-based)
7697          * @param {Number} y Y value for new position (coordinates are page-based)
7698          * @param {Number} width The new width
7699          * @param {Number} height The new height
7700          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7701          * @return {Roo.Element} this
7702          */
7703         setBounds : function(x, y, width, height, animate){
7704             if(!animate || !A){
7705                 this.setSize(width, height);
7706                 this.setLocation(x, y);
7707             }else{
7708                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7709                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7710                               this.preanim(arguments, 4), 'motion');
7711             }
7712             return this;
7713         },
7714
7715         /**
7716          * 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.
7717          * @param {Roo.lib.Region} region The region to fill
7718          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7719          * @return {Roo.Element} this
7720          */
7721         setRegion : function(region, animate){
7722             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7723             return this;
7724         },
7725
7726         /**
7727          * Appends an event handler
7728          *
7729          * @param {String}   eventName     The type of event to append
7730          * @param {Function} fn        The method the event invokes
7731          * @param {Object} scope       (optional) The scope (this object) of the fn
7732          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7733          */
7734         addListener : function(eventName, fn, scope, options){
7735             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7736         },
7737
7738         /**
7739          * Removes an event handler from this element
7740          * @param {String} eventName the type of event to remove
7741          * @param {Function} fn the method the event invokes
7742          * @return {Roo.Element} this
7743          */
7744         removeListener : function(eventName, fn){
7745             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7746             return this;
7747         },
7748
7749         /**
7750          * Removes all previous added listeners from this element
7751          * @return {Roo.Element} this
7752          */
7753         removeAllListeners : function(){
7754             E.purgeElement(this.dom);
7755             return this;
7756         },
7757
7758         relayEvent : function(eventName, observable){
7759             this.on(eventName, function(e){
7760                 observable.fireEvent(eventName, e);
7761             });
7762         },
7763
7764         /**
7765          * Set the opacity of the element
7766          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7767          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7768          * @return {Roo.Element} this
7769          */
7770          setOpacity : function(opacity, animate){
7771             if(!animate || !A){
7772                 var s = this.dom.style;
7773                 if(Roo.isIE){
7774                     s.zoom = 1;
7775                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7776                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7777                 }else{
7778                     s.opacity = opacity;
7779                 }
7780             }else{
7781                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7782             }
7783             return this;
7784         },
7785
7786         /**
7787          * Gets the left X coordinate
7788          * @param {Boolean} local True to get the local css position instead of page coordinate
7789          * @return {Number}
7790          */
7791         getLeft : function(local){
7792             if(!local){
7793                 return this.getX();
7794             }else{
7795                 return parseInt(this.getStyle("left"), 10) || 0;
7796             }
7797         },
7798
7799         /**
7800          * Gets the right X coordinate of the element (element X position + element width)
7801          * @param {Boolean} local True to get the local css position instead of page coordinate
7802          * @return {Number}
7803          */
7804         getRight : function(local){
7805             if(!local){
7806                 return this.getX() + this.getWidth();
7807             }else{
7808                 return (this.getLeft(true) + this.getWidth()) || 0;
7809             }
7810         },
7811
7812         /**
7813          * Gets the top Y coordinate
7814          * @param {Boolean} local True to get the local css position instead of page coordinate
7815          * @return {Number}
7816          */
7817         getTop : function(local) {
7818             if(!local){
7819                 return this.getY();
7820             }else{
7821                 return parseInt(this.getStyle("top"), 10) || 0;
7822             }
7823         },
7824
7825         /**
7826          * Gets the bottom Y coordinate of the element (element Y position + element height)
7827          * @param {Boolean} local True to get the local css position instead of page coordinate
7828          * @return {Number}
7829          */
7830         getBottom : function(local){
7831             if(!local){
7832                 return this.getY() + this.getHeight();
7833             }else{
7834                 return (this.getTop(true) + this.getHeight()) || 0;
7835             }
7836         },
7837
7838         /**
7839         * Initializes positioning on this element. If a desired position is not passed, it will make the
7840         * the element positioned relative IF it is not already positioned.
7841         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7842         * @param {Number} zIndex (optional) The zIndex to apply
7843         * @param {Number} x (optional) Set the page X position
7844         * @param {Number} y (optional) Set the page Y position
7845         */
7846         position : function(pos, zIndex, x, y){
7847             if(!pos){
7848                if(this.getStyle('position') == 'static'){
7849                    this.setStyle('position', 'relative');
7850                }
7851             }else{
7852                 this.setStyle("position", pos);
7853             }
7854             if(zIndex){
7855                 this.setStyle("z-index", zIndex);
7856             }
7857             if(x !== undefined && y !== undefined){
7858                 this.setXY([x, y]);
7859             }else if(x !== undefined){
7860                 this.setX(x);
7861             }else if(y !== undefined){
7862                 this.setY(y);
7863             }
7864         },
7865
7866         /**
7867         * Clear positioning back to the default when the document was loaded
7868         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7869         * @return {Roo.Element} this
7870          */
7871         clearPositioning : function(value){
7872             value = value ||'';
7873             this.setStyle({
7874                 "left": value,
7875                 "right": value,
7876                 "top": value,
7877                 "bottom": value,
7878                 "z-index": "",
7879                 "position" : "static"
7880             });
7881             return this;
7882         },
7883
7884         /**
7885         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7886         * snapshot before performing an update and then restoring the element.
7887         * @return {Object}
7888         */
7889         getPositioning : function(){
7890             var l = this.getStyle("left");
7891             var t = this.getStyle("top");
7892             return {
7893                 "position" : this.getStyle("position"),
7894                 "left" : l,
7895                 "right" : l ? "" : this.getStyle("right"),
7896                 "top" : t,
7897                 "bottom" : t ? "" : this.getStyle("bottom"),
7898                 "z-index" : this.getStyle("z-index")
7899             };
7900         },
7901
7902         /**
7903          * Gets the width of the border(s) for the specified side(s)
7904          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7905          * passing lr would get the border (l)eft width + the border (r)ight width.
7906          * @return {Number} The width of the sides passed added together
7907          */
7908         getBorderWidth : function(side){
7909             return this.addStyles(side, El.borders);
7910         },
7911
7912         /**
7913          * Gets the width of the padding(s) for the specified side(s)
7914          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7915          * passing lr would get the padding (l)eft + the padding (r)ight.
7916          * @return {Number} The padding of the sides passed added together
7917          */
7918         getPadding : function(side){
7919             return this.addStyles(side, El.paddings);
7920         },
7921
7922         /**
7923         * Set positioning with an object returned by getPositioning().
7924         * @param {Object} posCfg
7925         * @return {Roo.Element} this
7926          */
7927         setPositioning : function(pc){
7928             this.applyStyles(pc);
7929             if(pc.right == "auto"){
7930                 this.dom.style.right = "";
7931             }
7932             if(pc.bottom == "auto"){
7933                 this.dom.style.bottom = "";
7934             }
7935             return this;
7936         },
7937
7938         // private
7939         fixDisplay : function(){
7940             if(this.getStyle("display") == "none"){
7941                 this.setStyle("visibility", "hidden");
7942                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7943                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7944                     this.setStyle("display", "block");
7945                 }
7946             }
7947         },
7948
7949         /**
7950          * Quick set left and top adding default units
7951          * @param {String} left The left CSS property value
7952          * @param {String} top The top CSS property value
7953          * @return {Roo.Element} this
7954          */
7955          setLeftTop : function(left, top){
7956             this.dom.style.left = this.addUnits(left);
7957             this.dom.style.top = this.addUnits(top);
7958             return this;
7959         },
7960
7961         /**
7962          * Move this element relative to its current position.
7963          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7964          * @param {Number} distance How far to move the element in pixels
7965          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7966          * @return {Roo.Element} this
7967          */
7968          move : function(direction, distance, animate){
7969             var xy = this.getXY();
7970             direction = direction.toLowerCase();
7971             switch(direction){
7972                 case "l":
7973                 case "left":
7974                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7975                     break;
7976                case "r":
7977                case "right":
7978                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7979                     break;
7980                case "t":
7981                case "top":
7982                case "up":
7983                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7984                     break;
7985                case "b":
7986                case "bottom":
7987                case "down":
7988                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7989                     break;
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7996          * @return {Roo.Element} this
7997          */
7998         clip : function(){
7999             if(!this.isClipped){
8000                this.isClipped = true;
8001                this.originalClip = {
8002                    "o": this.getStyle("overflow"),
8003                    "x": this.getStyle("overflow-x"),
8004                    "y": this.getStyle("overflow-y")
8005                };
8006                this.setStyle("overflow", "hidden");
8007                this.setStyle("overflow-x", "hidden");
8008                this.setStyle("overflow-y", "hidden");
8009             }
8010             return this;
8011         },
8012
8013         /**
8014          *  Return clipping (overflow) to original clipping before clip() was called
8015          * @return {Roo.Element} this
8016          */
8017         unclip : function(){
8018             if(this.isClipped){
8019                 this.isClipped = false;
8020                 var o = this.originalClip;
8021                 if(o.o){this.setStyle("overflow", o.o);}
8022                 if(o.x){this.setStyle("overflow-x", o.x);}
8023                 if(o.y){this.setStyle("overflow-y", o.y);}
8024             }
8025             return this;
8026         },
8027
8028
8029         /**
8030          * Gets the x,y coordinates specified by the anchor position on the element.
8031          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8032          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8033          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8034          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8035          * @return {Array} [x, y] An array containing the element's x and y coordinates
8036          */
8037         getAnchorXY : function(anchor, local, s){
8038             //Passing a different size is useful for pre-calculating anchors,
8039             //especially for anchored animations that change the el size.
8040
8041             var w, h, vp = false;
8042             if(!s){
8043                 var d = this.dom;
8044                 if(d == document.body || d == document){
8045                     vp = true;
8046                     w = D.getViewWidth(); h = D.getViewHeight();
8047                 }else{
8048                     w = this.getWidth(); h = this.getHeight();
8049                 }
8050             }else{
8051                 w = s.width;  h = s.height;
8052             }
8053             var x = 0, y = 0, r = Math.round;
8054             switch((anchor || "tl").toLowerCase()){
8055                 case "c":
8056                     x = r(w*.5);
8057                     y = r(h*.5);
8058                 break;
8059                 case "t":
8060                     x = r(w*.5);
8061                     y = 0;
8062                 break;
8063                 case "l":
8064                     x = 0;
8065                     y = r(h*.5);
8066                 break;
8067                 case "r":
8068                     x = w;
8069                     y = r(h*.5);
8070                 break;
8071                 case "b":
8072                     x = r(w*.5);
8073                     y = h;
8074                 break;
8075                 case "tl":
8076                     x = 0;
8077                     y = 0;
8078                 break;
8079                 case "bl":
8080                     x = 0;
8081                     y = h;
8082                 break;
8083                 case "br":
8084                     x = w;
8085                     y = h;
8086                 break;
8087                 case "tr":
8088                     x = w;
8089                     y = 0;
8090                 break;
8091             }
8092             if(local === true){
8093                 return [x, y];
8094             }
8095             if(vp){
8096                 var sc = this.getScroll();
8097                 return [x + sc.left, y + sc.top];
8098             }
8099             //Add the element's offset xy
8100             var o = this.getXY();
8101             return [x+o[0], y+o[1]];
8102         },
8103
8104         /**
8105          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8106          * supported position values.
8107          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8108          * @param {String} position The position to align to.
8109          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8110          * @return {Array} [x, y]
8111          */
8112         getAlignToXY : function(el, p, o){
8113             el = Roo.get(el);
8114             var d = this.dom;
8115             if(!el.dom){
8116                 throw "Element.alignTo with an element that doesn't exist";
8117             }
8118             var c = false; //constrain to viewport
8119             var p1 = "", p2 = "";
8120             o = o || [0,0];
8121
8122             if(!p){
8123                 p = "tl-bl";
8124             }else if(p == "?"){
8125                 p = "tl-bl?";
8126             }else if(p.indexOf("-") == -1){
8127                 p = "tl-" + p;
8128             }
8129             p = p.toLowerCase();
8130             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8131             if(!m){
8132                throw "Element.alignTo with an invalid alignment " + p;
8133             }
8134             p1 = m[1]; p2 = m[2]; c = !!m[3];
8135
8136             //Subtract the aligned el's internal xy from the target's offset xy
8137             //plus custom offset to get the aligned el's new offset xy
8138             var a1 = this.getAnchorXY(p1, true);
8139             var a2 = el.getAnchorXY(p2, false);
8140             var x = a2[0] - a1[0] + o[0];
8141             var y = a2[1] - a1[1] + o[1];
8142             if(c){
8143                 //constrain the aligned el to viewport if necessary
8144                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8145                 // 5px of margin for ie
8146                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8147
8148                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8149                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8150                 //otherwise swap the aligned el to the opposite border of the target.
8151                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8152                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8153                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8154                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8155
8156                var doc = document;
8157                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8158                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8159
8160                if((x+w) > dw + scrollX){
8161                     x = swapX ? r.left-w : dw+scrollX-w;
8162                 }
8163                if(x < scrollX){
8164                    x = swapX ? r.right : scrollX;
8165                }
8166                if((y+h) > dh + scrollY){
8167                     y = swapY ? r.top-h : dh+scrollY-h;
8168                 }
8169                if (y < scrollY){
8170                    y = swapY ? r.bottom : scrollY;
8171                }
8172             }
8173             return [x,y];
8174         },
8175
8176         // private
8177         getConstrainToXY : function(){
8178             var os = {top:0, left:0, bottom:0, right: 0};
8179
8180             return function(el, local, offsets, proposedXY){
8181                 el = Roo.get(el);
8182                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8183
8184                 var vw, vh, vx = 0, vy = 0;
8185                 if(el.dom == document.body || el.dom == document){
8186                     vw = Roo.lib.Dom.getViewWidth();
8187                     vh = Roo.lib.Dom.getViewHeight();
8188                 }else{
8189                     vw = el.dom.clientWidth;
8190                     vh = el.dom.clientHeight;
8191                     if(!local){
8192                         var vxy = el.getXY();
8193                         vx = vxy[0];
8194                         vy = vxy[1];
8195                     }
8196                 }
8197
8198                 var s = el.getScroll();
8199
8200                 vx += offsets.left + s.left;
8201                 vy += offsets.top + s.top;
8202
8203                 vw -= offsets.right;
8204                 vh -= offsets.bottom;
8205
8206                 var vr = vx+vw;
8207                 var vb = vy+vh;
8208
8209                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8210                 var x = xy[0], y = xy[1];
8211                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8212
8213                 // only move it if it needs it
8214                 var moved = false;
8215
8216                 // first validate right/bottom
8217                 if((x + w) > vr){
8218                     x = vr - w;
8219                     moved = true;
8220                 }
8221                 if((y + h) > vb){
8222                     y = vb - h;
8223                     moved = true;
8224                 }
8225                 // then make sure top/left isn't negative
8226                 if(x < vx){
8227                     x = vx;
8228                     moved = true;
8229                 }
8230                 if(y < vy){
8231                     y = vy;
8232                     moved = true;
8233                 }
8234                 return moved ? [x, y] : false;
8235             };
8236         }(),
8237
8238         // private
8239         adjustForConstraints : function(xy, parent, offsets){
8240             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8241         },
8242
8243         /**
8244          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8245          * document it aligns it to the viewport.
8246          * The position parameter is optional, and can be specified in any one of the following formats:
8247          * <ul>
8248          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8249          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8250          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8251          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8252          *   <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
8253          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8254          * </ul>
8255          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8256          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8257          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8258          * that specified in order to enforce the viewport constraints.
8259          * Following are all of the supported anchor positions:
8260     <pre>
8261     Value  Description
8262     -----  -----------------------------
8263     tl     The top left corner (default)
8264     t      The center of the top edge
8265     tr     The top right corner
8266     l      The center of the left edge
8267     c      In the center of the element
8268     r      The center of the right edge
8269     bl     The bottom left corner
8270     b      The center of the bottom edge
8271     br     The bottom right corner
8272     </pre>
8273     Example Usage:
8274     <pre><code>
8275     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8276     el.alignTo("other-el");
8277
8278     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8279     el.alignTo("other-el", "tr?");
8280
8281     // align the bottom right corner of el with the center left edge of other-el
8282     el.alignTo("other-el", "br-l?");
8283
8284     // align the center of el with the bottom left corner of other-el and
8285     // adjust the x position by -6 pixels (and the y position by 0)
8286     el.alignTo("other-el", "c-bl", [-6, 0]);
8287     </code></pre>
8288          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8289          * @param {String} position The position to align to.
8290          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8291          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8292          * @return {Roo.Element} this
8293          */
8294         alignTo : function(element, position, offsets, animate){
8295             var xy = this.getAlignToXY(element, position, offsets);
8296             this.setXY(xy, this.preanim(arguments, 3));
8297             return this;
8298         },
8299
8300         /**
8301          * Anchors an element to another element and realigns it when the window is resized.
8302          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8303          * @param {String} position The position to align to.
8304          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8305          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8306          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8307          * is a number, it is used as the buffer delay (defaults to 50ms).
8308          * @param {Function} callback The function to call after the animation finishes
8309          * @return {Roo.Element} this
8310          */
8311         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8312             var action = function(){
8313                 this.alignTo(el, alignment, offsets, animate);
8314                 Roo.callback(callback, this);
8315             };
8316             Roo.EventManager.onWindowResize(action, this);
8317             var tm = typeof monitorScroll;
8318             if(tm != 'undefined'){
8319                 Roo.EventManager.on(window, 'scroll', action, this,
8320                     {buffer: tm == 'number' ? monitorScroll : 50});
8321             }
8322             action.call(this); // align immediately
8323             return this;
8324         },
8325         /**
8326          * Clears any opacity settings from this element. Required in some cases for IE.
8327          * @return {Roo.Element} this
8328          */
8329         clearOpacity : function(){
8330             if (window.ActiveXObject) {
8331                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8332                     this.dom.style.filter = "";
8333                 }
8334             } else {
8335                 this.dom.style.opacity = "";
8336                 this.dom.style["-moz-opacity"] = "";
8337                 this.dom.style["-khtml-opacity"] = "";
8338             }
8339             return this;
8340         },
8341
8342         /**
8343          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8344          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8345          * @return {Roo.Element} this
8346          */
8347         hide : function(animate){
8348             this.setVisible(false, this.preanim(arguments, 0));
8349             return this;
8350         },
8351
8352         /**
8353         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8354         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8355          * @return {Roo.Element} this
8356          */
8357         show : function(animate){
8358             this.setVisible(true, this.preanim(arguments, 0));
8359             return this;
8360         },
8361
8362         /**
8363          * @private Test if size has a unit, otherwise appends the default
8364          */
8365         addUnits : function(size){
8366             return Roo.Element.addUnits(size, this.defaultUnit);
8367         },
8368
8369         /**
8370          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8371          * @return {Roo.Element} this
8372          */
8373         beginMeasure : function(){
8374             var el = this.dom;
8375             if(el.offsetWidth || el.offsetHeight){
8376                 return this; // offsets work already
8377             }
8378             var changed = [];
8379             var p = this.dom, b = document.body; // start with this element
8380             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8381                 var pe = Roo.get(p);
8382                 if(pe.getStyle('display') == 'none'){
8383                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8384                     p.style.visibility = "hidden";
8385                     p.style.display = "block";
8386                 }
8387                 p = p.parentNode;
8388             }
8389             this._measureChanged = changed;
8390             return this;
8391
8392         },
8393
8394         /**
8395          * Restores displays to before beginMeasure was called
8396          * @return {Roo.Element} this
8397          */
8398         endMeasure : function(){
8399             var changed = this._measureChanged;
8400             if(changed){
8401                 for(var i = 0, len = changed.length; i < len; i++) {
8402                     var r = changed[i];
8403                     r.el.style.visibility = r.visibility;
8404                     r.el.style.display = "none";
8405                 }
8406                 this._measureChanged = null;
8407             }
8408             return this;
8409         },
8410
8411         /**
8412         * Update the innerHTML of this element, optionally searching for and processing scripts
8413         * @param {String} html The new HTML
8414         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8415         * @param {Function} callback For async script loading you can be noticed when the update completes
8416         * @return {Roo.Element} this
8417          */
8418         update : function(html, loadScripts, callback){
8419             if(typeof html == "undefined"){
8420                 html = "";
8421             }
8422             if(loadScripts !== true){
8423                 this.dom.innerHTML = html;
8424                 if(typeof callback == "function"){
8425                     callback();
8426                 }
8427                 return this;
8428             }
8429             var id = Roo.id();
8430             var dom = this.dom;
8431
8432             html += '<span id="' + id + '"></span>';
8433
8434             E.onAvailable(id, function(){
8435                 var hd = document.getElementsByTagName("head")[0];
8436                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8437                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8438                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8439
8440                 var match;
8441                 while(match = re.exec(html)){
8442                     var attrs = match[1];
8443                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8444                     if(srcMatch && srcMatch[2]){
8445                        var s = document.createElement("script");
8446                        s.src = srcMatch[2];
8447                        var typeMatch = attrs.match(typeRe);
8448                        if(typeMatch && typeMatch[2]){
8449                            s.type = typeMatch[2];
8450                        }
8451                        hd.appendChild(s);
8452                     }else if(match[2] && match[2].length > 0){
8453                         if(window.execScript) {
8454                            window.execScript(match[2]);
8455                         } else {
8456                             /**
8457                              * eval:var:id
8458                              * eval:var:dom
8459                              * eval:var:html
8460                              * 
8461                              */
8462                            window.eval(match[2]);
8463                         }
8464                     }
8465                 }
8466                 var el = document.getElementById(id);
8467                 if(el){el.parentNode.removeChild(el);}
8468                 if(typeof callback == "function"){
8469                     callback();
8470                 }
8471             });
8472             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8473             return this;
8474         },
8475
8476         /**
8477          * Direct access to the UpdateManager update() method (takes the same parameters).
8478          * @param {String/Function} url The url for this request or a function to call to get the url
8479          * @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}
8480          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8481          * @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.
8482          * @return {Roo.Element} this
8483          */
8484         load : function(){
8485             var um = this.getUpdateManager();
8486             um.update.apply(um, arguments);
8487             return this;
8488         },
8489
8490         /**
8491         * Gets this element's UpdateManager
8492         * @return {Roo.UpdateManager} The UpdateManager
8493         */
8494         getUpdateManager : function(){
8495             if(!this.updateManager){
8496                 this.updateManager = new Roo.UpdateManager(this);
8497             }
8498             return this.updateManager;
8499         },
8500
8501         /**
8502          * Disables text selection for this element (normalized across browsers)
8503          * @return {Roo.Element} this
8504          */
8505         unselectable : function(){
8506             this.dom.unselectable = "on";
8507             this.swallowEvent("selectstart", true);
8508             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8509             this.addClass("x-unselectable");
8510             return this;
8511         },
8512
8513         /**
8514         * Calculates the x, y to center this element on the screen
8515         * @return {Array} The x, y values [x, y]
8516         */
8517         getCenterXY : function(){
8518             return this.getAlignToXY(document, 'c-c');
8519         },
8520
8521         /**
8522         * Centers the Element in either the viewport, or another Element.
8523         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8524         */
8525         center : function(centerIn){
8526             this.alignTo(centerIn || document, 'c-c');
8527             return this;
8528         },
8529
8530         /**
8531          * Tests various css rules/browsers to determine if this element uses a border box
8532          * @return {Boolean}
8533          */
8534         isBorderBox : function(){
8535             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8536         },
8537
8538         /**
8539          * Return a box {x, y, width, height} that can be used to set another elements
8540          * size/location to match this element.
8541          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8542          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8543          * @return {Object} box An object in the format {x, y, width, height}
8544          */
8545         getBox : function(contentBox, local){
8546             var xy;
8547             if(!local){
8548                 xy = this.getXY();
8549             }else{
8550                 var left = parseInt(this.getStyle("left"), 10) || 0;
8551                 var top = parseInt(this.getStyle("top"), 10) || 0;
8552                 xy = [left, top];
8553             }
8554             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8555             if(!contentBox){
8556                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8557             }else{
8558                 var l = this.getBorderWidth("l")+this.getPadding("l");
8559                 var r = this.getBorderWidth("r")+this.getPadding("r");
8560                 var t = this.getBorderWidth("t")+this.getPadding("t");
8561                 var b = this.getBorderWidth("b")+this.getPadding("b");
8562                 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)};
8563             }
8564             bx.right = bx.x + bx.width;
8565             bx.bottom = bx.y + bx.height;
8566             return bx;
8567         },
8568
8569         /**
8570          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8571          for more information about the sides.
8572          * @param {String} sides
8573          * @return {Number}
8574          */
8575         getFrameWidth : function(sides, onlyContentBox){
8576             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8577         },
8578
8579         /**
8580          * 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.
8581          * @param {Object} box The box to fill {x, y, width, height}
8582          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8583          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8584          * @return {Roo.Element} this
8585          */
8586         setBox : function(box, adjust, animate){
8587             var w = box.width, h = box.height;
8588             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8589                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8590                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8591             }
8592             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8593             return this;
8594         },
8595
8596         /**
8597          * Forces the browser to repaint this element
8598          * @return {Roo.Element} this
8599          */
8600          repaint : function(){
8601             var dom = this.dom;
8602             this.addClass("x-repaint");
8603             setTimeout(function(){
8604                 Roo.get(dom).removeClass("x-repaint");
8605             }, 1);
8606             return this;
8607         },
8608
8609         /**
8610          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8611          * then it returns the calculated width of the sides (see getPadding)
8612          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8613          * @return {Object/Number}
8614          */
8615         getMargins : function(side){
8616             if(!side){
8617                 return {
8618                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8619                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8620                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8621                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8622                 };
8623             }else{
8624                 return this.addStyles(side, El.margins);
8625              }
8626         },
8627
8628         // private
8629         addStyles : function(sides, styles){
8630             var val = 0, v, w;
8631             for(var i = 0, len = sides.length; i < len; i++){
8632                 v = this.getStyle(styles[sides.charAt(i)]);
8633                 if(v){
8634                      w = parseInt(v, 10);
8635                      if(w){ val += w; }
8636                 }
8637             }
8638             return val;
8639         },
8640
8641         /**
8642          * Creates a proxy element of this element
8643          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8644          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8645          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8646          * @return {Roo.Element} The new proxy element
8647          */
8648         createProxy : function(config, renderTo, matchBox){
8649             if(renderTo){
8650                 renderTo = Roo.getDom(renderTo);
8651             }else{
8652                 renderTo = document.body;
8653             }
8654             config = typeof config == "object" ?
8655                 config : {tag : "div", cls: config};
8656             var proxy = Roo.DomHelper.append(renderTo, config, true);
8657             if(matchBox){
8658                proxy.setBox(this.getBox());
8659             }
8660             return proxy;
8661         },
8662
8663         /**
8664          * Puts a mask over this element to disable user interaction. Requires core.css.
8665          * This method can only be applied to elements which accept child nodes.
8666          * @param {String} msg (optional) A message to display in the mask
8667          * @param {String} msgCls (optional) A css class to apply to the msg element
8668          * @return {Element} The mask  element
8669          */
8670         mask : function(msg, msgCls){
8671             if(this.getStyle("position") == "static"){
8672                 this.setStyle("position", "relative");
8673             }
8674             if(!this._mask){
8675                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8676             }
8677             this.addClass("x-masked");
8678             this._mask.setDisplayed(true);
8679             if(typeof msg == 'string'){
8680                 if(!this._maskMsg){
8681                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8682                 }
8683                 var mm = this._maskMsg;
8684                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8685                 mm.dom.firstChild.innerHTML = msg;
8686                 mm.setDisplayed(true);
8687                 mm.center(this);
8688             }
8689             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8690                 this._mask.setHeight(this.getHeight());
8691             }
8692             return this._mask;
8693         },
8694
8695         /**
8696          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8697          * it is cached for reuse.
8698          */
8699         unmask : function(removeEl){
8700             if(this._mask){
8701                 if(removeEl === true){
8702                     this._mask.remove();
8703                     delete this._mask;
8704                     if(this._maskMsg){
8705                         this._maskMsg.remove();
8706                         delete this._maskMsg;
8707                     }
8708                 }else{
8709                     this._mask.setDisplayed(false);
8710                     if(this._maskMsg){
8711                         this._maskMsg.setDisplayed(false);
8712                     }
8713                 }
8714             }
8715             this.removeClass("x-masked");
8716         },
8717
8718         /**
8719          * Returns true if this element is masked
8720          * @return {Boolean}
8721          */
8722         isMasked : function(){
8723             return this._mask && this._mask.isVisible();
8724         },
8725
8726         /**
8727          * Creates an iframe shim for this element to keep selects and other windowed objects from
8728          * showing through.
8729          * @return {Roo.Element} The new shim element
8730          */
8731         createShim : function(){
8732             var el = document.createElement('iframe');
8733             el.frameBorder = 'no';
8734             el.className = 'roo-shim';
8735             if(Roo.isIE && Roo.isSecure){
8736                 el.src = Roo.SSL_SECURE_URL;
8737             }
8738             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8739             shim.autoBoxAdjust = false;
8740             return shim;
8741         },
8742
8743         /**
8744          * Removes this element from the DOM and deletes it from the cache
8745          */
8746         remove : function(){
8747             if(this.dom.parentNode){
8748                 this.dom.parentNode.removeChild(this.dom);
8749             }
8750             delete El.cache[this.dom.id];
8751         },
8752
8753         /**
8754          * Sets up event handlers to add and remove a css class when the mouse is over this element
8755          * @param {String} className
8756          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8757          * mouseout events for children elements
8758          * @return {Roo.Element} this
8759          */
8760         addClassOnOver : function(className, preventFlicker){
8761             this.on("mouseover", function(){
8762                 Roo.fly(this, '_internal').addClass(className);
8763             }, this.dom);
8764             var removeFn = function(e){
8765                 if(preventFlicker !== true || !e.within(this, true)){
8766                     Roo.fly(this, '_internal').removeClass(className);
8767                 }
8768             };
8769             this.on("mouseout", removeFn, this.dom);
8770             return this;
8771         },
8772
8773         /**
8774          * Sets up event handlers to add and remove a css class when this element has the focus
8775          * @param {String} className
8776          * @return {Roo.Element} this
8777          */
8778         addClassOnFocus : function(className){
8779             this.on("focus", function(){
8780                 Roo.fly(this, '_internal').addClass(className);
8781             }, this.dom);
8782             this.on("blur", function(){
8783                 Roo.fly(this, '_internal').removeClass(className);
8784             }, this.dom);
8785             return this;
8786         },
8787         /**
8788          * 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)
8789          * @param {String} className
8790          * @return {Roo.Element} this
8791          */
8792         addClassOnClick : function(className){
8793             var dom = this.dom;
8794             this.on("mousedown", function(){
8795                 Roo.fly(dom, '_internal').addClass(className);
8796                 var d = Roo.get(document);
8797                 var fn = function(){
8798                     Roo.fly(dom, '_internal').removeClass(className);
8799                     d.removeListener("mouseup", fn);
8800                 };
8801                 d.on("mouseup", fn);
8802             });
8803             return this;
8804         },
8805
8806         /**
8807          * Stops the specified event from bubbling and optionally prevents the default action
8808          * @param {String} eventName
8809          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8810          * @return {Roo.Element} this
8811          */
8812         swallowEvent : function(eventName, preventDefault){
8813             var fn = function(e){
8814                 e.stopPropagation();
8815                 if(preventDefault){
8816                     e.preventDefault();
8817                 }
8818             };
8819             if(eventName instanceof Array){
8820                 for(var i = 0, len = eventName.length; i < len; i++){
8821                      this.on(eventName[i], fn);
8822                 }
8823                 return this;
8824             }
8825             this.on(eventName, fn);
8826             return this;
8827         },
8828
8829         /**
8830          * @private
8831          */
8832       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8833
8834         /**
8835          * Sizes this element to its parent element's dimensions performing
8836          * neccessary box adjustments.
8837          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8838          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8839          * @return {Roo.Element} this
8840          */
8841         fitToParent : function(monitorResize, targetParent) {
8842           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8843           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8844           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8845             return;
8846           }
8847           var p = Roo.get(targetParent || this.dom.parentNode);
8848           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8849           if (monitorResize === true) {
8850             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8851             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8852           }
8853           return this;
8854         },
8855
8856         /**
8857          * Gets the next sibling, skipping text nodes
8858          * @return {HTMLElement} The next sibling or null
8859          */
8860         getNextSibling : function(){
8861             var n = this.dom.nextSibling;
8862             while(n && n.nodeType != 1){
8863                 n = n.nextSibling;
8864             }
8865             return n;
8866         },
8867
8868         /**
8869          * Gets the previous sibling, skipping text nodes
8870          * @return {HTMLElement} The previous sibling or null
8871          */
8872         getPrevSibling : function(){
8873             var n = this.dom.previousSibling;
8874             while(n && n.nodeType != 1){
8875                 n = n.previousSibling;
8876             }
8877             return n;
8878         },
8879
8880
8881         /**
8882          * Appends the passed element(s) to this element
8883          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8884          * @return {Roo.Element} this
8885          */
8886         appendChild: function(el){
8887             el = Roo.get(el);
8888             el.appendTo(this);
8889             return this;
8890         },
8891
8892         /**
8893          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8894          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8895          * automatically generated with the specified attributes.
8896          * @param {HTMLElement} insertBefore (optional) a child element of this element
8897          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8898          * @return {Roo.Element} The new child element
8899          */
8900         createChild: function(config, insertBefore, returnDom){
8901             config = config || {tag:'div'};
8902             if(insertBefore){
8903                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8904             }
8905             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8906         },
8907
8908         /**
8909          * Appends this element to the passed element
8910          * @param {String/HTMLElement/Element} el The new parent element
8911          * @return {Roo.Element} this
8912          */
8913         appendTo: function(el){
8914             el = Roo.getDom(el);
8915             el.appendChild(this.dom);
8916             return this;
8917         },
8918
8919         /**
8920          * Inserts this element before the passed element in the DOM
8921          * @param {String/HTMLElement/Element} el The element to insert before
8922          * @return {Roo.Element} this
8923          */
8924         insertBefore: function(el){
8925             el = Roo.getDom(el);
8926             el.parentNode.insertBefore(this.dom, el);
8927             return this;
8928         },
8929
8930         /**
8931          * Inserts this element after the passed element in the DOM
8932          * @param {String/HTMLElement/Element} el The element to insert after
8933          * @return {Roo.Element} this
8934          */
8935         insertAfter: function(el){
8936             el = Roo.getDom(el);
8937             el.parentNode.insertBefore(this.dom, el.nextSibling);
8938             return this;
8939         },
8940
8941         /**
8942          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8943          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8944          * @return {Roo.Element} The new child
8945          */
8946         insertFirst: function(el, returnDom){
8947             el = el || {};
8948             if(typeof el == 'object' && !el.nodeType){ // dh config
8949                 return this.createChild(el, this.dom.firstChild, returnDom);
8950             }else{
8951                 el = Roo.getDom(el);
8952                 this.dom.insertBefore(el, this.dom.firstChild);
8953                 return !returnDom ? Roo.get(el) : el;
8954             }
8955         },
8956
8957         /**
8958          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8959          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8960          * @param {String} where (optional) 'before' or 'after' defaults to before
8961          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8962          * @return {Roo.Element} the inserted Element
8963          */
8964         insertSibling: function(el, where, returnDom){
8965             where = where ? where.toLowerCase() : 'before';
8966             el = el || {};
8967             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8968
8969             if(typeof el == 'object' && !el.nodeType){ // dh config
8970                 if(where == 'after' && !this.dom.nextSibling){
8971                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8972                 }else{
8973                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8974                 }
8975
8976             }else{
8977                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8978                             where == 'before' ? this.dom : this.dom.nextSibling);
8979                 if(!returnDom){
8980                     rt = Roo.get(rt);
8981                 }
8982             }
8983             return rt;
8984         },
8985
8986         /**
8987          * Creates and wraps this element with another element
8988          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8989          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8990          * @return {HTMLElement/Element} The newly created wrapper element
8991          */
8992         wrap: function(config, returnDom){
8993             if(!config){
8994                 config = {tag: "div"};
8995             }
8996             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8997             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8998             return newEl;
8999         },
9000
9001         /**
9002          * Replaces the passed element with this element
9003          * @param {String/HTMLElement/Element} el The element to replace
9004          * @return {Roo.Element} this
9005          */
9006         replace: function(el){
9007             el = Roo.get(el);
9008             this.insertBefore(el);
9009             el.remove();
9010             return this;
9011         },
9012
9013         /**
9014          * Inserts an html fragment into this element
9015          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9016          * @param {String} html The HTML fragment
9017          * @param {Boolean} returnEl True to return an Roo.Element
9018          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9019          */
9020         insertHtml : function(where, html, returnEl){
9021             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9022             return returnEl ? Roo.get(el) : el;
9023         },
9024
9025         /**
9026          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9027          * @param {Object} o The object with the attributes
9028          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9029          * @return {Roo.Element} this
9030          */
9031         set : function(o, useSet){
9032             var el = this.dom;
9033             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9034             for(var attr in o){
9035                 if(attr == "style" || typeof o[attr] == "function") continue;
9036                 if(attr=="cls"){
9037                     el.className = o["cls"];
9038                 }else{
9039                     if(useSet) el.setAttribute(attr, o[attr]);
9040                     else el[attr] = o[attr];
9041                 }
9042             }
9043             if(o.style){
9044                 Roo.DomHelper.applyStyles(el, o.style);
9045             }
9046             return this;
9047         },
9048
9049         /**
9050          * Convenience method for constructing a KeyMap
9051          * @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:
9052          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9053          * @param {Function} fn The function to call
9054          * @param {Object} scope (optional) The scope of the function
9055          * @return {Roo.KeyMap} The KeyMap created
9056          */
9057         addKeyListener : function(key, fn, scope){
9058             var config;
9059             if(typeof key != "object" || key instanceof Array){
9060                 config = {
9061                     key: key,
9062                     fn: fn,
9063                     scope: scope
9064                 };
9065             }else{
9066                 config = {
9067                     key : key.key,
9068                     shift : key.shift,
9069                     ctrl : key.ctrl,
9070                     alt : key.alt,
9071                     fn: fn,
9072                     scope: scope
9073                 };
9074             }
9075             return new Roo.KeyMap(this, config);
9076         },
9077
9078         /**
9079          * Creates a KeyMap for this element
9080          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9081          * @return {Roo.KeyMap} The KeyMap created
9082          */
9083         addKeyMap : function(config){
9084             return new Roo.KeyMap(this, config);
9085         },
9086
9087         /**
9088          * Returns true if this element is scrollable.
9089          * @return {Boolean}
9090          */
9091          isScrollable : function(){
9092             var dom = this.dom;
9093             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9094         },
9095
9096         /**
9097          * 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().
9098          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9099          * @param {Number} value The new scroll value
9100          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9101          * @return {Element} this
9102          */
9103
9104         scrollTo : function(side, value, animate){
9105             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9106             if(!animate || !A){
9107                 this.dom[prop] = value;
9108             }else{
9109                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9110                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9111             }
9112             return this;
9113         },
9114
9115         /**
9116          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9117          * within this element's scrollable range.
9118          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9119          * @param {Number} distance How far to scroll the element in pixels
9120          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9121          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9122          * was scrolled as far as it could go.
9123          */
9124          scroll : function(direction, distance, animate){
9125              if(!this.isScrollable()){
9126                  return;
9127              }
9128              var el = this.dom;
9129              var l = el.scrollLeft, t = el.scrollTop;
9130              var w = el.scrollWidth, h = el.scrollHeight;
9131              var cw = el.clientWidth, ch = el.clientHeight;
9132              direction = direction.toLowerCase();
9133              var scrolled = false;
9134              var a = this.preanim(arguments, 2);
9135              switch(direction){
9136                  case "l":
9137                  case "left":
9138                      if(w - l > cw){
9139                          var v = Math.min(l + distance, w-cw);
9140                          this.scrollTo("left", v, a);
9141                          scrolled = true;
9142                      }
9143                      break;
9144                 case "r":
9145                 case "right":
9146                      if(l > 0){
9147                          var v = Math.max(l - distance, 0);
9148                          this.scrollTo("left", v, a);
9149                          scrolled = true;
9150                      }
9151                      break;
9152                 case "t":
9153                 case "top":
9154                 case "up":
9155                      if(t > 0){
9156                          var v = Math.max(t - distance, 0);
9157                          this.scrollTo("top", v, a);
9158                          scrolled = true;
9159                      }
9160                      break;
9161                 case "b":
9162                 case "bottom":
9163                 case "down":
9164                      if(h - t > ch){
9165                          var v = Math.min(t + distance, h-ch);
9166                          this.scrollTo("top", v, a);
9167                          scrolled = true;
9168                      }
9169                      break;
9170              }
9171              return scrolled;
9172         },
9173
9174         /**
9175          * Translates the passed page coordinates into left/top css values for this element
9176          * @param {Number/Array} x The page x or an array containing [x, y]
9177          * @param {Number} y The page y
9178          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9179          */
9180         translatePoints : function(x, y){
9181             if(typeof x == 'object' || x instanceof Array){
9182                 y = x[1]; x = x[0];
9183             }
9184             var p = this.getStyle('position');
9185             var o = this.getXY();
9186
9187             var l = parseInt(this.getStyle('left'), 10);
9188             var t = parseInt(this.getStyle('top'), 10);
9189
9190             if(isNaN(l)){
9191                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9192             }
9193             if(isNaN(t)){
9194                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9195             }
9196
9197             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9198         },
9199
9200         /**
9201          * Returns the current scroll position of the element.
9202          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9203          */
9204         getScroll : function(){
9205             var d = this.dom, doc = document;
9206             if(d == doc || d == doc.body){
9207                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9208                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9209                 return {left: l, top: t};
9210             }else{
9211                 return {left: d.scrollLeft, top: d.scrollTop};
9212             }
9213         },
9214
9215         /**
9216          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9217          * are convert to standard 6 digit hex color.
9218          * @param {String} attr The css attribute
9219          * @param {String} defaultValue The default value to use when a valid color isn't found
9220          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9221          * YUI color anims.
9222          */
9223         getColor : function(attr, defaultValue, prefix){
9224             var v = this.getStyle(attr);
9225             if(!v || v == "transparent" || v == "inherit") {
9226                 return defaultValue;
9227             }
9228             var color = typeof prefix == "undefined" ? "#" : prefix;
9229             if(v.substr(0, 4) == "rgb("){
9230                 var rvs = v.slice(4, v.length -1).split(",");
9231                 for(var i = 0; i < 3; i++){
9232                     var h = parseInt(rvs[i]).toString(16);
9233                     if(h < 16){
9234                         h = "0" + h;
9235                     }
9236                     color += h;
9237                 }
9238             } else {
9239                 if(v.substr(0, 1) == "#"){
9240                     if(v.length == 4) {
9241                         for(var i = 1; i < 4; i++){
9242                             var c = v.charAt(i);
9243                             color +=  c + c;
9244                         }
9245                     }else if(v.length == 7){
9246                         color += v.substr(1);
9247                     }
9248                 }
9249             }
9250             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9251         },
9252
9253         /**
9254          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9255          * gradient background, rounded corners and a 4-way shadow.
9256          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9257          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9258          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9259          * @return {Roo.Element} this
9260          */
9261         boxWrap : function(cls){
9262             cls = cls || 'x-box';
9263             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9264             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9265             return el;
9266         },
9267
9268         /**
9269          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9270          * @param {String} namespace The namespace in which to look for the attribute
9271          * @param {String} name The attribute name
9272          * @return {String} The attribute value
9273          */
9274         getAttributeNS : Roo.isIE ? function(ns, name){
9275             var d = this.dom;
9276             var type = typeof d[ns+":"+name];
9277             if(type != 'undefined' && type != 'unknown'){
9278                 return d[ns+":"+name];
9279             }
9280             return d[name];
9281         } : function(ns, name){
9282             var d = this.dom;
9283             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9284         }
9285     };
9286
9287     var ep = El.prototype;
9288
9289     /**
9290      * Appends an event handler (Shorthand for addListener)
9291      * @param {String}   eventName     The type of event to append
9292      * @param {Function} fn        The method the event invokes
9293      * @param {Object} scope       (optional) The scope (this object) of the fn
9294      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9295      * @method
9296      */
9297     ep.on = ep.addListener;
9298         // backwards compat
9299     ep.mon = ep.addListener;
9300
9301     /**
9302      * Removes an event handler from this element (shorthand for removeListener)
9303      * @param {String} eventName the type of event to remove
9304      * @param {Function} fn the method the event invokes
9305      * @return {Roo.Element} this
9306      * @method
9307      */
9308     ep.un = ep.removeListener;
9309
9310     /**
9311      * true to automatically adjust width and height settings for box-model issues (default to true)
9312      */
9313     ep.autoBoxAdjust = true;
9314
9315     // private
9316     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9317
9318     // private
9319     El.addUnits = function(v, defaultUnit){
9320         if(v === "" || v == "auto"){
9321             return v;
9322         }
9323         if(v === undefined){
9324             return '';
9325         }
9326         if(typeof v == "number" || !El.unitPattern.test(v)){
9327             return v + (defaultUnit || 'px');
9328         }
9329         return v;
9330     };
9331
9332     // special markup used throughout Roo when box wrapping elements
9333     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>';
9334     /**
9335      * Visibility mode constant - Use visibility to hide element
9336      * @static
9337      * @type Number
9338      */
9339     El.VISIBILITY = 1;
9340     /**
9341      * Visibility mode constant - Use display to hide element
9342      * @static
9343      * @type Number
9344      */
9345     El.DISPLAY = 2;
9346
9347     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9348     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9349     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9350
9351
9352
9353     /**
9354      * @private
9355      */
9356     El.cache = {};
9357
9358     var docEl;
9359
9360     /**
9361      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9362      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9363      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9364      * @return {Element} The Element object
9365      * @static
9366      */
9367     El.get = function(el){
9368         var ex, elm, id;
9369         if(!el){ return null; }
9370         if(typeof el == "string"){ // element id
9371             if(!(elm = document.getElementById(el))){
9372                 return null;
9373             }
9374             if(ex = El.cache[el]){
9375                 ex.dom = elm;
9376             }else{
9377                 ex = El.cache[el] = new El(elm);
9378             }
9379             return ex;
9380         }else if(el.tagName){ // dom element
9381             if(!(id = el.id)){
9382                 id = Roo.id(el);
9383             }
9384             if(ex = El.cache[id]){
9385                 ex.dom = el;
9386             }else{
9387                 ex = El.cache[id] = new El(el);
9388             }
9389             return ex;
9390         }else if(el instanceof El){
9391             if(el != docEl){
9392                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9393                                                               // catch case where it hasn't been appended
9394                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9395             }
9396             return el;
9397         }else if(el.isComposite){
9398             return el;
9399         }else if(el instanceof Array){
9400             return El.select(el);
9401         }else if(el == document){
9402             // create a bogus element object representing the document object
9403             if(!docEl){
9404                 var f = function(){};
9405                 f.prototype = El.prototype;
9406                 docEl = new f();
9407                 docEl.dom = document;
9408             }
9409             return docEl;
9410         }
9411         return null;
9412     };
9413
9414     // private
9415     El.uncache = function(el){
9416         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9417             if(a[i]){
9418                 delete El.cache[a[i].id || a[i]];
9419             }
9420         }
9421     };
9422
9423     // private
9424     // Garbage collection - uncache elements/purge listeners on orphaned elements
9425     // so we don't hold a reference and cause the browser to retain them
9426     El.garbageCollect = function(){
9427         if(!Roo.enableGarbageCollector){
9428             clearInterval(El.collectorThread);
9429             return;
9430         }
9431         for(var eid in El.cache){
9432             var el = El.cache[eid], d = el.dom;
9433             // -------------------------------------------------------
9434             // Determining what is garbage:
9435             // -------------------------------------------------------
9436             // !d
9437             // dom node is null, definitely garbage
9438             // -------------------------------------------------------
9439             // !d.parentNode
9440             // no parentNode == direct orphan, definitely garbage
9441             // -------------------------------------------------------
9442             // !d.offsetParent && !document.getElementById(eid)
9443             // display none elements have no offsetParent so we will
9444             // also try to look it up by it's id. However, check
9445             // offsetParent first so we don't do unneeded lookups.
9446             // This enables collection of elements that are not orphans
9447             // directly, but somewhere up the line they have an orphan
9448             // parent.
9449             // -------------------------------------------------------
9450             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9451                 delete El.cache[eid];
9452                 if(d && Roo.enableListenerCollection){
9453                     E.purgeElement(d);
9454                 }
9455             }
9456         }
9457     }
9458     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9459
9460
9461     // dom is optional
9462     El.Flyweight = function(dom){
9463         this.dom = dom;
9464     };
9465     El.Flyweight.prototype = El.prototype;
9466
9467     El._flyweights = {};
9468     /**
9469      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9470      * the dom node can be overwritten by other code.
9471      * @param {String/HTMLElement} el The dom node or id
9472      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9473      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9474      * @static
9475      * @return {Element} The shared Element object
9476      */
9477     El.fly = function(el, named){
9478         named = named || '_global';
9479         el = Roo.getDom(el);
9480         if(!el){
9481             return null;
9482         }
9483         if(!El._flyweights[named]){
9484             El._flyweights[named] = new El.Flyweight();
9485         }
9486         El._flyweights[named].dom = el;
9487         return El._flyweights[named];
9488     };
9489
9490     /**
9491      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9492      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9493      * Shorthand of {@link Roo.Element#get}
9494      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9495      * @return {Element} The Element object
9496      * @member Roo
9497      * @method get
9498      */
9499     Roo.get = El.get;
9500     /**
9501      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9502      * the dom node can be overwritten by other code.
9503      * Shorthand of {@link Roo.Element#fly}
9504      * @param {String/HTMLElement} el The dom node or id
9505      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9506      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9507      * @static
9508      * @return {Element} The shared Element object
9509      * @member Roo
9510      * @method fly
9511      */
9512     Roo.fly = El.fly;
9513
9514     // speedy lookup for elements never to box adjust
9515     var noBoxAdjust = Roo.isStrict ? {
9516         select:1
9517     } : {
9518         input:1, select:1, textarea:1
9519     };
9520     if(Roo.isIE || Roo.isGecko){
9521         noBoxAdjust['button'] = 1;
9522     }
9523
9524
9525     Roo.EventManager.on(window, 'unload', function(){
9526         delete El.cache;
9527         delete El._flyweights;
9528     });
9529 })();
9530
9531
9532
9533
9534 if(Roo.DomQuery){
9535     Roo.Element.selectorFunction = Roo.DomQuery.select;
9536 }
9537
9538 Roo.Element.select = function(selector, unique, root){
9539     var els;
9540     if(typeof selector == "string"){
9541         els = Roo.Element.selectorFunction(selector, root);
9542     }else if(selector.length !== undefined){
9543         els = selector;
9544     }else{
9545         throw "Invalid selector";
9546     }
9547     if(unique === true){
9548         return new Roo.CompositeElement(els);
9549     }else{
9550         return new Roo.CompositeElementLite(els);
9551     }
9552 };
9553 /**
9554  * Selects elements based on the passed CSS selector to enable working on them as 1.
9555  * @param {String/Array} selector The CSS selector or an array of elements
9556  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9557  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9558  * @return {CompositeElementLite/CompositeElement}
9559  * @member Roo
9560  * @method select
9561  */
9562 Roo.select = Roo.Element.select;
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577 /*
9578  * Based on:
9579  * Ext JS Library 1.1.1
9580  * Copyright(c) 2006-2007, Ext JS, LLC.
9581  *
9582  * Originally Released Under LGPL - original licence link has changed is not relivant.
9583  *
9584  * Fork - LGPL
9585  * <script type="text/javascript">
9586  */
9587
9588
9589
9590 //Notifies Element that fx methods are available
9591 Roo.enableFx = true;
9592
9593 /**
9594  * @class Roo.Fx
9595  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9596  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9597  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9598  * Element effects to work.</p><br/>
9599  *
9600  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9601  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9602  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9603  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9604  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9605  * expected results and should be done with care.</p><br/>
9606  *
9607  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9608  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9609 <pre>
9610 Value  Description
9611 -----  -----------------------------
9612 tl     The top left corner
9613 t      The center of the top edge
9614 tr     The top right corner
9615 l      The center of the left edge
9616 r      The center of the right edge
9617 bl     The bottom left corner
9618 b      The center of the bottom edge
9619 br     The bottom right corner
9620 </pre>
9621  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9622  * below are common options that can be passed to any Fx method.</b>
9623  * @cfg {Function} callback A function called when the effect is finished
9624  * @cfg {Object} scope The scope of the effect function
9625  * @cfg {String} easing A valid Easing value for the effect
9626  * @cfg {String} afterCls A css class to apply after the effect
9627  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9628  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9629  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9630  * effects that end with the element being visually hidden, ignored otherwise)
9631  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9632  * a function which returns such a specification that will be applied to the Element after the effect finishes
9633  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9634  * @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
9635  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9636  */
9637 Roo.Fx = {
9638         /**
9639          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9640          * origin for the slide effect.  This function automatically handles wrapping the element with
9641          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9642          * Usage:
9643          *<pre><code>
9644 // default: slide the element in from the top
9645 el.slideIn();
9646
9647 // custom: slide the element in from the right with a 2-second duration
9648 el.slideIn('r', { duration: 2 });
9649
9650 // common config options shown with default values
9651 el.slideIn('t', {
9652     easing: 'easeOut',
9653     duration: .5
9654 });
9655 </code></pre>
9656          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9657          * @param {Object} options (optional) Object literal with any of the Fx config options
9658          * @return {Roo.Element} The Element
9659          */
9660     slideIn : function(anchor, o){
9661         var el = this.getFxEl();
9662         o = o || {};
9663
9664         el.queueFx(o, function(){
9665
9666             anchor = anchor || "t";
9667
9668             // fix display to visibility
9669             this.fixDisplay();
9670
9671             // restore values after effect
9672             var r = this.getFxRestore();
9673             var b = this.getBox();
9674             // fixed size for slide
9675             this.setSize(b);
9676
9677             // wrap if needed
9678             var wrap = this.fxWrap(r.pos, o, "hidden");
9679
9680             var st = this.dom.style;
9681             st.visibility = "visible";
9682             st.position = "absolute";
9683
9684             // clear out temp styles after slide and unwrap
9685             var after = function(){
9686                 el.fxUnwrap(wrap, r.pos, o);
9687                 st.width = r.width;
9688                 st.height = r.height;
9689                 el.afterFx(o);
9690             };
9691             // time to calc the positions
9692             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9693
9694             switch(anchor.toLowerCase()){
9695                 case "t":
9696                     wrap.setSize(b.width, 0);
9697                     st.left = st.bottom = "0";
9698                     a = {height: bh};
9699                 break;
9700                 case "l":
9701                     wrap.setSize(0, b.height);
9702                     st.right = st.top = "0";
9703                     a = {width: bw};
9704                 break;
9705                 case "r":
9706                     wrap.setSize(0, b.height);
9707                     wrap.setX(b.right);
9708                     st.left = st.top = "0";
9709                     a = {width: bw, points: pt};
9710                 break;
9711                 case "b":
9712                     wrap.setSize(b.width, 0);
9713                     wrap.setY(b.bottom);
9714                     st.left = st.top = "0";
9715                     a = {height: bh, points: pt};
9716                 break;
9717                 case "tl":
9718                     wrap.setSize(0, 0);
9719                     st.right = st.bottom = "0";
9720                     a = {width: bw, height: bh};
9721                 break;
9722                 case "bl":
9723                     wrap.setSize(0, 0);
9724                     wrap.setY(b.y+b.height);
9725                     st.right = st.top = "0";
9726                     a = {width: bw, height: bh, points: pt};
9727                 break;
9728                 case "br":
9729                     wrap.setSize(0, 0);
9730                     wrap.setXY([b.right, b.bottom]);
9731                     st.left = st.top = "0";
9732                     a = {width: bw, height: bh, points: pt};
9733                 break;
9734                 case "tr":
9735                     wrap.setSize(0, 0);
9736                     wrap.setX(b.x+b.width);
9737                     st.left = st.bottom = "0";
9738                     a = {width: bw, height: bh, points: pt};
9739                 break;
9740             }
9741             this.dom.style.visibility = "visible";
9742             wrap.show();
9743
9744             arguments.callee.anim = wrap.fxanim(a,
9745                 o,
9746                 'motion',
9747                 .5,
9748                 'easeOut', after);
9749         });
9750         return this;
9751     },
9752     
9753         /**
9754          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9755          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9756          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9757          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9758          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9759          * Usage:
9760          *<pre><code>
9761 // default: slide the element out to the top
9762 el.slideOut();
9763
9764 // custom: slide the element out to the right with a 2-second duration
9765 el.slideOut('r', { duration: 2 });
9766
9767 // common config options shown with default values
9768 el.slideOut('t', {
9769     easing: 'easeOut',
9770     duration: .5,
9771     remove: false,
9772     useDisplay: false
9773 });
9774 </code></pre>
9775          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9776          * @param {Object} options (optional) Object literal with any of the Fx config options
9777          * @return {Roo.Element} The Element
9778          */
9779     slideOut : function(anchor, o){
9780         var el = this.getFxEl();
9781         o = o || {};
9782
9783         el.queueFx(o, function(){
9784
9785             anchor = anchor || "t";
9786
9787             // restore values after effect
9788             var r = this.getFxRestore();
9789             
9790             var b = this.getBox();
9791             // fixed size for slide
9792             this.setSize(b);
9793
9794             // wrap if needed
9795             var wrap = this.fxWrap(r.pos, o, "visible");
9796
9797             var st = this.dom.style;
9798             st.visibility = "visible";
9799             st.position = "absolute";
9800
9801             wrap.setSize(b);
9802
9803             var after = function(){
9804                 if(o.useDisplay){
9805                     el.setDisplayed(false);
9806                 }else{
9807                     el.hide();
9808                 }
9809
9810                 el.fxUnwrap(wrap, r.pos, o);
9811
9812                 st.width = r.width;
9813                 st.height = r.height;
9814
9815                 el.afterFx(o);
9816             };
9817
9818             var a, zero = {to: 0};
9819             switch(anchor.toLowerCase()){
9820                 case "t":
9821                     st.left = st.bottom = "0";
9822                     a = {height: zero};
9823                 break;
9824                 case "l":
9825                     st.right = st.top = "0";
9826                     a = {width: zero};
9827                 break;
9828                 case "r":
9829                     st.left = st.top = "0";
9830                     a = {width: zero, points: {to:[b.right, b.y]}};
9831                 break;
9832                 case "b":
9833                     st.left = st.top = "0";
9834                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9835                 break;
9836                 case "tl":
9837                     st.right = st.bottom = "0";
9838                     a = {width: zero, height: zero};
9839                 break;
9840                 case "bl":
9841                     st.right = st.top = "0";
9842                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9843                 break;
9844                 case "br":
9845                     st.left = st.top = "0";
9846                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9847                 break;
9848                 case "tr":
9849                     st.left = st.bottom = "0";
9850                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9851                 break;
9852             }
9853
9854             arguments.callee.anim = wrap.fxanim(a,
9855                 o,
9856                 'motion',
9857                 .5,
9858                 "easeOut", after);
9859         });
9860         return this;
9861     },
9862
9863         /**
9864          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9865          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9866          * The element must be removed from the DOM using the 'remove' config option if desired.
9867          * Usage:
9868          *<pre><code>
9869 // default
9870 el.puff();
9871
9872 // common config options shown with default values
9873 el.puff({
9874     easing: 'easeOut',
9875     duration: .5,
9876     remove: false,
9877     useDisplay: false
9878 });
9879 </code></pre>
9880          * @param {Object} options (optional) Object literal with any of the Fx config options
9881          * @return {Roo.Element} The Element
9882          */
9883     puff : function(o){
9884         var el = this.getFxEl();
9885         o = o || {};
9886
9887         el.queueFx(o, function(){
9888             this.clearOpacity();
9889             this.show();
9890
9891             // restore values after effect
9892             var r = this.getFxRestore();
9893             var st = this.dom.style;
9894
9895             var after = function(){
9896                 if(o.useDisplay){
9897                     el.setDisplayed(false);
9898                 }else{
9899                     el.hide();
9900                 }
9901
9902                 el.clearOpacity();
9903
9904                 el.setPositioning(r.pos);
9905                 st.width = r.width;
9906                 st.height = r.height;
9907                 st.fontSize = '';
9908                 el.afterFx(o);
9909             };
9910
9911             var width = this.getWidth();
9912             var height = this.getHeight();
9913
9914             arguments.callee.anim = this.fxanim({
9915                     width : {to: this.adjustWidth(width * 2)},
9916                     height : {to: this.adjustHeight(height * 2)},
9917                     points : {by: [-(width * .5), -(height * .5)]},
9918                     opacity : {to: 0},
9919                     fontSize: {to:200, unit: "%"}
9920                 },
9921                 o,
9922                 'motion',
9923                 .5,
9924                 "easeOut", after);
9925         });
9926         return this;
9927     },
9928
9929         /**
9930          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9931          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9932          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9933          * Usage:
9934          *<pre><code>
9935 // default
9936 el.switchOff();
9937
9938 // all config options shown with default values
9939 el.switchOff({
9940     easing: 'easeIn',
9941     duration: .3,
9942     remove: false,
9943     useDisplay: false
9944 });
9945 </code></pre>
9946          * @param {Object} options (optional) Object literal with any of the Fx config options
9947          * @return {Roo.Element} The Element
9948          */
9949     switchOff : function(o){
9950         var el = this.getFxEl();
9951         o = o || {};
9952
9953         el.queueFx(o, function(){
9954             this.clearOpacity();
9955             this.clip();
9956
9957             // restore values after effect
9958             var r = this.getFxRestore();
9959             var st = this.dom.style;
9960
9961             var after = function(){
9962                 if(o.useDisplay){
9963                     el.setDisplayed(false);
9964                 }else{
9965                     el.hide();
9966                 }
9967
9968                 el.clearOpacity();
9969                 el.setPositioning(r.pos);
9970                 st.width = r.width;
9971                 st.height = r.height;
9972
9973                 el.afterFx(o);
9974             };
9975
9976             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9977                 this.clearOpacity();
9978                 (function(){
9979                     this.fxanim({
9980                         height:{to:1},
9981                         points:{by:[0, this.getHeight() * .5]}
9982                     }, o, 'motion', 0.3, 'easeIn', after);
9983                 }).defer(100, this);
9984             });
9985         });
9986         return this;
9987     },
9988
9989     /**
9990      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9991      * changed using the "attr" config option) and then fading back to the original color. If no original
9992      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9993      * Usage:
9994 <pre><code>
9995 // default: highlight background to yellow
9996 el.highlight();
9997
9998 // custom: highlight foreground text to blue for 2 seconds
9999 el.highlight("0000ff", { attr: 'color', duration: 2 });
10000
10001 // common config options shown with default values
10002 el.highlight("ffff9c", {
10003     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10004     endColor: (current color) or "ffffff",
10005     easing: 'easeIn',
10006     duration: 1
10007 });
10008 </code></pre>
10009      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10010      * @param {Object} options (optional) Object literal with any of the Fx config options
10011      * @return {Roo.Element} The Element
10012      */ 
10013     highlight : function(color, o){
10014         var el = this.getFxEl();
10015         o = o || {};
10016
10017         el.queueFx(o, function(){
10018             color = color || "ffff9c";
10019             attr = o.attr || "backgroundColor";
10020
10021             this.clearOpacity();
10022             this.show();
10023
10024             var origColor = this.getColor(attr);
10025             var restoreColor = this.dom.style[attr];
10026             endColor = (o.endColor || origColor) || "ffffff";
10027
10028             var after = function(){
10029                 el.dom.style[attr] = restoreColor;
10030                 el.afterFx(o);
10031             };
10032
10033             var a = {};
10034             a[attr] = {from: color, to: endColor};
10035             arguments.callee.anim = this.fxanim(a,
10036                 o,
10037                 'color',
10038                 1,
10039                 'easeIn', after);
10040         });
10041         return this;
10042     },
10043
10044    /**
10045     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10046     * Usage:
10047 <pre><code>
10048 // default: a single light blue ripple
10049 el.frame();
10050
10051 // custom: 3 red ripples lasting 3 seconds total
10052 el.frame("ff0000", 3, { duration: 3 });
10053
10054 // common config options shown with default values
10055 el.frame("C3DAF9", 1, {
10056     duration: 1 //duration of entire animation (not each individual ripple)
10057     // Note: Easing is not configurable and will be ignored if included
10058 });
10059 </code></pre>
10060     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10061     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10062     * @param {Object} options (optional) Object literal with any of the Fx config options
10063     * @return {Roo.Element} The Element
10064     */
10065     frame : function(color, count, o){
10066         var el = this.getFxEl();
10067         o = o || {};
10068
10069         el.queueFx(o, function(){
10070             color = color || "#C3DAF9";
10071             if(color.length == 6){
10072                 color = "#" + color;
10073             }
10074             count = count || 1;
10075             duration = o.duration || 1;
10076             this.show();
10077
10078             var b = this.getBox();
10079             var animFn = function(){
10080                 var proxy = this.createProxy({
10081
10082                      style:{
10083                         visbility:"hidden",
10084                         position:"absolute",
10085                         "z-index":"35000", // yee haw
10086                         border:"0px solid " + color
10087                      }
10088                   });
10089                 var scale = Roo.isBorderBox ? 2 : 1;
10090                 proxy.animate({
10091                     top:{from:b.y, to:b.y - 20},
10092                     left:{from:b.x, to:b.x - 20},
10093                     borderWidth:{from:0, to:10},
10094                     opacity:{from:1, to:0},
10095                     height:{from:b.height, to:(b.height + (20*scale))},
10096                     width:{from:b.width, to:(b.width + (20*scale))}
10097                 }, duration, function(){
10098                     proxy.remove();
10099                 });
10100                 if(--count > 0){
10101                      animFn.defer((duration/2)*1000, this);
10102                 }else{
10103                     el.afterFx(o);
10104                 }
10105             };
10106             animFn.call(this);
10107         });
10108         return this;
10109     },
10110
10111    /**
10112     * Creates a pause before any subsequent queued effects begin.  If there are
10113     * no effects queued after the pause it will have no effect.
10114     * Usage:
10115 <pre><code>
10116 el.pause(1);
10117 </code></pre>
10118     * @param {Number} seconds The length of time to pause (in seconds)
10119     * @return {Roo.Element} The Element
10120     */
10121     pause : function(seconds){
10122         var el = this.getFxEl();
10123         var o = {};
10124
10125         el.queueFx(o, function(){
10126             setTimeout(function(){
10127                 el.afterFx(o);
10128             }, seconds * 1000);
10129         });
10130         return this;
10131     },
10132
10133    /**
10134     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10135     * using the "endOpacity" config option.
10136     * Usage:
10137 <pre><code>
10138 // default: fade in from opacity 0 to 100%
10139 el.fadeIn();
10140
10141 // custom: fade in from opacity 0 to 75% over 2 seconds
10142 el.fadeIn({ endOpacity: .75, duration: 2});
10143
10144 // common config options shown with default values
10145 el.fadeIn({
10146     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10147     easing: 'easeOut',
10148     duration: .5
10149 });
10150 </code></pre>
10151     * @param {Object} options (optional) Object literal with any of the Fx config options
10152     * @return {Roo.Element} The Element
10153     */
10154     fadeIn : function(o){
10155         var el = this.getFxEl();
10156         o = o || {};
10157         el.queueFx(o, function(){
10158             this.setOpacity(0);
10159             this.fixDisplay();
10160             this.dom.style.visibility = 'visible';
10161             var to = o.endOpacity || 1;
10162             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10163                 o, null, .5, "easeOut", function(){
10164                 if(to == 1){
10165                     this.clearOpacity();
10166                 }
10167                 el.afterFx(o);
10168             });
10169         });
10170         return this;
10171     },
10172
10173    /**
10174     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10175     * using the "endOpacity" config option.
10176     * Usage:
10177 <pre><code>
10178 // default: fade out from the element's current opacity to 0
10179 el.fadeOut();
10180
10181 // custom: fade out from the element's current opacity to 25% over 2 seconds
10182 el.fadeOut({ endOpacity: .25, duration: 2});
10183
10184 // common config options shown with default values
10185 el.fadeOut({
10186     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10187     easing: 'easeOut',
10188     duration: .5
10189     remove: false,
10190     useDisplay: false
10191 });
10192 </code></pre>
10193     * @param {Object} options (optional) Object literal with any of the Fx config options
10194     * @return {Roo.Element} The Element
10195     */
10196     fadeOut : function(o){
10197         var el = this.getFxEl();
10198         o = o || {};
10199         el.queueFx(o, function(){
10200             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10201                 o, null, .5, "easeOut", function(){
10202                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10203                      this.dom.style.display = "none";
10204                 }else{
10205                      this.dom.style.visibility = "hidden";
10206                 }
10207                 this.clearOpacity();
10208                 el.afterFx(o);
10209             });
10210         });
10211         return this;
10212     },
10213
10214    /**
10215     * Animates the transition of an element's dimensions from a starting height/width
10216     * to an ending height/width.
10217     * Usage:
10218 <pre><code>
10219 // change height and width to 100x100 pixels
10220 el.scale(100, 100);
10221
10222 // common config options shown with default values.  The height and width will default to
10223 // the element's existing values if passed as null.
10224 el.scale(
10225     [element's width],
10226     [element's height], {
10227     easing: 'easeOut',
10228     duration: .35
10229 });
10230 </code></pre>
10231     * @param {Number} width  The new width (pass undefined to keep the original width)
10232     * @param {Number} height  The new height (pass undefined to keep the original height)
10233     * @param {Object} options (optional) Object literal with any of the Fx config options
10234     * @return {Roo.Element} The Element
10235     */
10236     scale : function(w, h, o){
10237         this.shift(Roo.apply({}, o, {
10238             width: w,
10239             height: h
10240         }));
10241         return this;
10242     },
10243
10244    /**
10245     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10246     * Any of these properties not specified in the config object will not be changed.  This effect 
10247     * requires that at least one new dimension, position or opacity setting must be passed in on
10248     * the config object in order for the function to have any effect.
10249     * Usage:
10250 <pre><code>
10251 // slide the element horizontally to x position 200 while changing the height and opacity
10252 el.shift({ x: 200, height: 50, opacity: .8 });
10253
10254 // common config options shown with default values.
10255 el.shift({
10256     width: [element's width],
10257     height: [element's height],
10258     x: [element's x position],
10259     y: [element's y position],
10260     opacity: [element's opacity],
10261     easing: 'easeOut',
10262     duration: .35
10263 });
10264 </code></pre>
10265     * @param {Object} options  Object literal with any of the Fx config options
10266     * @return {Roo.Element} The Element
10267     */
10268     shift : function(o){
10269         var el = this.getFxEl();
10270         o = o || {};
10271         el.queueFx(o, function(){
10272             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10273             if(w !== undefined){
10274                 a.width = {to: this.adjustWidth(w)};
10275             }
10276             if(h !== undefined){
10277                 a.height = {to: this.adjustHeight(h)};
10278             }
10279             if(x !== undefined || y !== undefined){
10280                 a.points = {to: [
10281                     x !== undefined ? x : this.getX(),
10282                     y !== undefined ? y : this.getY()
10283                 ]};
10284             }
10285             if(op !== undefined){
10286                 a.opacity = {to: op};
10287             }
10288             if(o.xy !== undefined){
10289                 a.points = {to: o.xy};
10290             }
10291             arguments.callee.anim = this.fxanim(a,
10292                 o, 'motion', .35, "easeOut", function(){
10293                 el.afterFx(o);
10294             });
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10301          * ending point of the effect.
10302          * Usage:
10303          *<pre><code>
10304 // default: slide the element downward while fading out
10305 el.ghost();
10306
10307 // custom: slide the element out to the right with a 2-second duration
10308 el.ghost('r', { duration: 2 });
10309
10310 // common config options shown with default values
10311 el.ghost('b', {
10312     easing: 'easeOut',
10313     duration: .5
10314     remove: false,
10315     useDisplay: false
10316 });
10317 </code></pre>
10318          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10319          * @param {Object} options (optional) Object literal with any of the Fx config options
10320          * @return {Roo.Element} The Element
10321          */
10322     ghost : function(anchor, o){
10323         var el = this.getFxEl();
10324         o = o || {};
10325
10326         el.queueFx(o, function(){
10327             anchor = anchor || "b";
10328
10329             // restore values after effect
10330             var r = this.getFxRestore();
10331             var w = this.getWidth(),
10332                 h = this.getHeight();
10333
10334             var st = this.dom.style;
10335
10336             var after = function(){
10337                 if(o.useDisplay){
10338                     el.setDisplayed(false);
10339                 }else{
10340                     el.hide();
10341                 }
10342
10343                 el.clearOpacity();
10344                 el.setPositioning(r.pos);
10345                 st.width = r.width;
10346                 st.height = r.height;
10347
10348                 el.afterFx(o);
10349             };
10350
10351             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10352             switch(anchor.toLowerCase()){
10353                 case "t":
10354                     pt.by = [0, -h];
10355                 break;
10356                 case "l":
10357                     pt.by = [-w, 0];
10358                 break;
10359                 case "r":
10360                     pt.by = [w, 0];
10361                 break;
10362                 case "b":
10363                     pt.by = [0, h];
10364                 break;
10365                 case "tl":
10366                     pt.by = [-w, -h];
10367                 break;
10368                 case "bl":
10369                     pt.by = [-w, h];
10370                 break;
10371                 case "br":
10372                     pt.by = [w, h];
10373                 break;
10374                 case "tr":
10375                     pt.by = [w, -h];
10376                 break;
10377             }
10378
10379             arguments.callee.anim = this.fxanim(a,
10380                 o,
10381                 'motion',
10382                 .5,
10383                 "easeOut", after);
10384         });
10385         return this;
10386     },
10387
10388         /**
10389          * Ensures that all effects queued after syncFx is called on the element are
10390          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10391          * @return {Roo.Element} The Element
10392          */
10393     syncFx : function(){
10394         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10395             block : false,
10396             concurrent : true,
10397             stopFx : false
10398         });
10399         return this;
10400     },
10401
10402         /**
10403          * Ensures that all effects queued after sequenceFx is called on the element are
10404          * run in sequence.  This is the opposite of {@link #syncFx}.
10405          * @return {Roo.Element} The Element
10406          */
10407     sequenceFx : function(){
10408         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10409             block : false,
10410             concurrent : false,
10411             stopFx : false
10412         });
10413         return this;
10414     },
10415
10416         /* @private */
10417     nextFx : function(){
10418         var ef = this.fxQueue[0];
10419         if(ef){
10420             ef.call(this);
10421         }
10422     },
10423
10424         /**
10425          * Returns true if the element has any effects actively running or queued, else returns false.
10426          * @return {Boolean} True if element has active effects, else false
10427          */
10428     hasActiveFx : function(){
10429         return this.fxQueue && this.fxQueue[0];
10430     },
10431
10432         /**
10433          * Stops any running effects and clears the element's internal effects queue if it contains
10434          * any additional effects that haven't started yet.
10435          * @return {Roo.Element} The Element
10436          */
10437     stopFx : function(){
10438         if(this.hasActiveFx()){
10439             var cur = this.fxQueue[0];
10440             if(cur && cur.anim && cur.anim.isAnimated()){
10441                 this.fxQueue = [cur]; // clear out others
10442                 cur.anim.stop(true);
10443             }
10444         }
10445         return this;
10446     },
10447
10448         /* @private */
10449     beforeFx : function(o){
10450         if(this.hasActiveFx() && !o.concurrent){
10451            if(o.stopFx){
10452                this.stopFx();
10453                return true;
10454            }
10455            return false;
10456         }
10457         return true;
10458     },
10459
10460         /**
10461          * Returns true if the element is currently blocking so that no other effect can be queued
10462          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10463          * used to ensure that an effect initiated by a user action runs to completion prior to the
10464          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10465          * @return {Boolean} True if blocking, else false
10466          */
10467     hasFxBlock : function(){
10468         var q = this.fxQueue;
10469         return q && q[0] && q[0].block;
10470     },
10471
10472         /* @private */
10473     queueFx : function(o, fn){
10474         if(!this.fxQueue){
10475             this.fxQueue = [];
10476         }
10477         if(!this.hasFxBlock()){
10478             Roo.applyIf(o, this.fxDefaults);
10479             if(!o.concurrent){
10480                 var run = this.beforeFx(o);
10481                 fn.block = o.block;
10482                 this.fxQueue.push(fn);
10483                 if(run){
10484                     this.nextFx();
10485                 }
10486             }else{
10487                 fn.call(this);
10488             }
10489         }
10490         return this;
10491     },
10492
10493         /* @private */
10494     fxWrap : function(pos, o, vis){
10495         var wrap;
10496         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10497             var wrapXY;
10498             if(o.fixPosition){
10499                 wrapXY = this.getXY();
10500             }
10501             var div = document.createElement("div");
10502             div.style.visibility = vis;
10503             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10504             wrap.setPositioning(pos);
10505             if(wrap.getStyle("position") == "static"){
10506                 wrap.position("relative");
10507             }
10508             this.clearPositioning('auto');
10509             wrap.clip();
10510             wrap.dom.appendChild(this.dom);
10511             if(wrapXY){
10512                 wrap.setXY(wrapXY);
10513             }
10514         }
10515         return wrap;
10516     },
10517
10518         /* @private */
10519     fxUnwrap : function(wrap, pos, o){
10520         this.clearPositioning();
10521         this.setPositioning(pos);
10522         if(!o.wrap){
10523             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10524             wrap.remove();
10525         }
10526     },
10527
10528         /* @private */
10529     getFxRestore : function(){
10530         var st = this.dom.style;
10531         return {pos: this.getPositioning(), width: st.width, height : st.height};
10532     },
10533
10534         /* @private */
10535     afterFx : function(o){
10536         if(o.afterStyle){
10537             this.applyStyles(o.afterStyle);
10538         }
10539         if(o.afterCls){
10540             this.addClass(o.afterCls);
10541         }
10542         if(o.remove === true){
10543             this.remove();
10544         }
10545         Roo.callback(o.callback, o.scope, [this]);
10546         if(!o.concurrent){
10547             this.fxQueue.shift();
10548             this.nextFx();
10549         }
10550     },
10551
10552         /* @private */
10553     getFxEl : function(){ // support for composite element fx
10554         return Roo.get(this.dom);
10555     },
10556
10557         /* @private */
10558     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10559         animType = animType || 'run';
10560         opt = opt || {};
10561         var anim = Roo.lib.Anim[animType](
10562             this.dom, args,
10563             (opt.duration || defaultDur) || .35,
10564             (opt.easing || defaultEase) || 'easeOut',
10565             function(){
10566                 Roo.callback(cb, this);
10567             },
10568             this
10569         );
10570         opt.anim = anim;
10571         return anim;
10572     }
10573 };
10574
10575 // backwords compat
10576 Roo.Fx.resize = Roo.Fx.scale;
10577
10578 //When included, Roo.Fx is automatically applied to Element so that all basic
10579 //effects are available directly via the Element API
10580 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10581  * Based on:
10582  * Ext JS Library 1.1.1
10583  * Copyright(c) 2006-2007, Ext JS, LLC.
10584  *
10585  * Originally Released Under LGPL - original licence link has changed is not relivant.
10586  *
10587  * Fork - LGPL
10588  * <script type="text/javascript">
10589  */
10590
10591
10592 /**
10593  * @class Roo.CompositeElement
10594  * Standard composite class. Creates a Roo.Element for every element in the collection.
10595  * <br><br>
10596  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10597  * actions will be performed on all the elements in this collection.</b>
10598  * <br><br>
10599  * All methods return <i>this</i> and can be chained.
10600  <pre><code>
10601  var els = Roo.select("#some-el div.some-class", true);
10602  // or select directly from an existing element
10603  var el = Roo.get('some-el');
10604  el.select('div.some-class', true);
10605
10606  els.setWidth(100); // all elements become 100 width
10607  els.hide(true); // all elements fade out and hide
10608  // or
10609  els.setWidth(100).hide(true);
10610  </code></pre>
10611  */
10612 Roo.CompositeElement = function(els){
10613     this.elements = [];
10614     this.addElements(els);
10615 };
10616 Roo.CompositeElement.prototype = {
10617     isComposite: true,
10618     addElements : function(els){
10619         if(!els) return this;
10620         if(typeof els == "string"){
10621             els = Roo.Element.selectorFunction(els);
10622         }
10623         var yels = this.elements;
10624         var index = yels.length-1;
10625         for(var i = 0, len = els.length; i < len; i++) {
10626                 yels[++index] = Roo.get(els[i]);
10627         }
10628         return this;
10629     },
10630
10631     /**
10632     * Clears this composite and adds the elements returned by the passed selector.
10633     * @param {String/Array} els A string CSS selector, an array of elements or an element
10634     * @return {CompositeElement} this
10635     */
10636     fill : function(els){
10637         this.elements = [];
10638         this.add(els);
10639         return this;
10640     },
10641
10642     /**
10643     * Filters this composite to only elements that match the passed selector.
10644     * @param {String} selector A string CSS selector
10645     * @return {CompositeElement} this
10646     */
10647     filter : function(selector){
10648         var els = [];
10649         this.each(function(el){
10650             if(el.is(selector)){
10651                 els[els.length] = el.dom;
10652             }
10653         });
10654         this.fill(els);
10655         return this;
10656     },
10657
10658     invoke : function(fn, args){
10659         var els = this.elements;
10660         for(var i = 0, len = els.length; i < len; i++) {
10661                 Roo.Element.prototype[fn].apply(els[i], args);
10662         }
10663         return this;
10664     },
10665     /**
10666     * Adds elements to this composite.
10667     * @param {String/Array} els A string CSS selector, an array of elements or an element
10668     * @return {CompositeElement} this
10669     */
10670     add : function(els){
10671         if(typeof els == "string"){
10672             this.addElements(Roo.Element.selectorFunction(els));
10673         }else if(els.length !== undefined){
10674             this.addElements(els);
10675         }else{
10676             this.addElements([els]);
10677         }
10678         return this;
10679     },
10680     /**
10681     * Calls the passed function passing (el, this, index) for each element in this composite.
10682     * @param {Function} fn The function to call
10683     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10684     * @return {CompositeElement} this
10685     */
10686     each : function(fn, scope){
10687         var els = this.elements;
10688         for(var i = 0, len = els.length; i < len; i++){
10689             if(fn.call(scope || els[i], els[i], this, i) === false) {
10690                 break;
10691             }
10692         }
10693         return this;
10694     },
10695
10696     /**
10697      * Returns the Element object at the specified index
10698      * @param {Number} index
10699      * @return {Roo.Element}
10700      */
10701     item : function(index){
10702         return this.elements[index] || null;
10703     },
10704
10705     /**
10706      * Returns the first Element
10707      * @return {Roo.Element}
10708      */
10709     first : function(){
10710         return this.item(0);
10711     },
10712
10713     /**
10714      * Returns the last Element
10715      * @return {Roo.Element}
10716      */
10717     last : function(){
10718         return this.item(this.elements.length-1);
10719     },
10720
10721     /**
10722      * Returns the number of elements in this composite
10723      * @return Number
10724      */
10725     getCount : function(){
10726         return this.elements.length;
10727     },
10728
10729     /**
10730      * Returns true if this composite contains the passed element
10731      * @return Boolean
10732      */
10733     contains : function(el){
10734         return this.indexOf(el) !== -1;
10735     },
10736
10737     /**
10738      * Returns true if this composite contains the passed element
10739      * @return Boolean
10740      */
10741     indexOf : function(el){
10742         return this.elements.indexOf(Roo.get(el));
10743     },
10744
10745
10746     /**
10747     * Removes the specified element(s).
10748     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10749     * or an array of any of those.
10750     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10751     * @return {CompositeElement} this
10752     */
10753     removeElement : function(el, removeDom){
10754         if(el instanceof Array){
10755             for(var i = 0, len = el.length; i < len; i++){
10756                 this.removeElement(el[i]);
10757             }
10758             return this;
10759         }
10760         var index = typeof el == 'number' ? el : this.indexOf(el);
10761         if(index !== -1){
10762             if(removeDom){
10763                 var d = this.elements[index];
10764                 if(d.dom){
10765                     d.remove();
10766                 }else{
10767                     d.parentNode.removeChild(d);
10768                 }
10769             }
10770             this.elements.splice(index, 1);
10771         }
10772         return this;
10773     },
10774
10775     /**
10776     * Replaces the specified element with the passed element.
10777     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10778     * to replace.
10779     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10780     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10781     * @return {CompositeElement} this
10782     */
10783     replaceElement : function(el, replacement, domReplace){
10784         var index = typeof el == 'number' ? el : this.indexOf(el);
10785         if(index !== -1){
10786             if(domReplace){
10787                 this.elements[index].replaceWith(replacement);
10788             }else{
10789                 this.elements.splice(index, 1, Roo.get(replacement))
10790             }
10791         }
10792         return this;
10793     },
10794
10795     /**
10796      * Removes all elements.
10797      */
10798     clear : function(){
10799         this.elements = [];
10800     }
10801 };
10802 (function(){
10803     Roo.CompositeElement.createCall = function(proto, fnName){
10804         if(!proto[fnName]){
10805             proto[fnName] = function(){
10806                 return this.invoke(fnName, arguments);
10807             };
10808         }
10809     };
10810     for(var fnName in Roo.Element.prototype){
10811         if(typeof Roo.Element.prototype[fnName] == "function"){
10812             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10813         }
10814     };
10815 })();
10816 /*
10817  * Based on:
10818  * Ext JS Library 1.1.1
10819  * Copyright(c) 2006-2007, Ext JS, LLC.
10820  *
10821  * Originally Released Under LGPL - original licence link has changed is not relivant.
10822  *
10823  * Fork - LGPL
10824  * <script type="text/javascript">
10825  */
10826
10827 /**
10828  * @class Roo.CompositeElementLite
10829  * @extends Roo.CompositeElement
10830  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10831  <pre><code>
10832  var els = Roo.select("#some-el div.some-class");
10833  // or select directly from an existing element
10834  var el = Roo.get('some-el');
10835  el.select('div.some-class');
10836
10837  els.setWidth(100); // all elements become 100 width
10838  els.hide(true); // all elements fade out and hide
10839  // or
10840  els.setWidth(100).hide(true);
10841  </code></pre><br><br>
10842  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10843  * actions will be performed on all the elements in this collection.</b>
10844  */
10845 Roo.CompositeElementLite = function(els){
10846     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10847     this.el = new Roo.Element.Flyweight();
10848 };
10849 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10850     addElements : function(els){
10851         if(els){
10852             if(els instanceof Array){
10853                 this.elements = this.elements.concat(els);
10854             }else{
10855                 var yels = this.elements;
10856                 var index = yels.length-1;
10857                 for(var i = 0, len = els.length; i < len; i++) {
10858                     yels[++index] = els[i];
10859                 }
10860             }
10861         }
10862         return this;
10863     },
10864     invoke : function(fn, args){
10865         var els = this.elements;
10866         var el = this.el;
10867         for(var i = 0, len = els.length; i < len; i++) {
10868             el.dom = els[i];
10869                 Roo.Element.prototype[fn].apply(el, args);
10870         }
10871         return this;
10872     },
10873     /**
10874      * Returns a flyweight Element of the dom element object at the specified index
10875      * @param {Number} index
10876      * @return {Roo.Element}
10877      */
10878     item : function(index){
10879         if(!this.elements[index]){
10880             return null;
10881         }
10882         this.el.dom = this.elements[index];
10883         return this.el;
10884     },
10885
10886     // fixes scope with flyweight
10887     addListener : function(eventName, handler, scope, opt){
10888         var els = this.elements;
10889         for(var i = 0, len = els.length; i < len; i++) {
10890             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10891         }
10892         return this;
10893     },
10894
10895     /**
10896     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10897     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10898     * a reference to the dom node, use el.dom.</b>
10899     * @param {Function} fn The function to call
10900     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10901     * @return {CompositeElement} this
10902     */
10903     each : function(fn, scope){
10904         var els = this.elements;
10905         var el = this.el;
10906         for(var i = 0, len = els.length; i < len; i++){
10907             el.dom = els[i];
10908                 if(fn.call(scope || el, el, this, i) === false){
10909                 break;
10910             }
10911         }
10912         return this;
10913     },
10914
10915     indexOf : function(el){
10916         return this.elements.indexOf(Roo.getDom(el));
10917     },
10918
10919     replaceElement : function(el, replacement, domReplace){
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             replacement = Roo.getDom(replacement);
10923             if(domReplace){
10924                 var d = this.elements[index];
10925                 d.parentNode.insertBefore(replacement, d);
10926                 d.parentNode.removeChild(d);
10927             }
10928             this.elements.splice(index, 1, replacement);
10929         }
10930         return this;
10931     }
10932 });
10933 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10934
10935 /*
10936  * Based on:
10937  * Ext JS Library 1.1.1
10938  * Copyright(c) 2006-2007, Ext JS, LLC.
10939  *
10940  * Originally Released Under LGPL - original licence link has changed is not relivant.
10941  *
10942  * Fork - LGPL
10943  * <script type="text/javascript">
10944  */
10945
10946  
10947
10948 /**
10949  * @class Roo.data.Connection
10950  * @extends Roo.util.Observable
10951  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10952  * either to a configured URL, or to a URL specified at request time.<br><br>
10953  * <p>
10954  * Requests made by this class are asynchronous, and will return immediately. No data from
10955  * the server will be available to the statement immediately following the {@link #request} call.
10956  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10957  * <p>
10958  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10959  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10960  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10961  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10962  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10963  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10964  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10965  * standard DOM methods.
10966  * @constructor
10967  * @param {Object} config a configuration object.
10968  */
10969 Roo.data.Connection = function(config){
10970     Roo.apply(this, config);
10971     this.addEvents({
10972         /**
10973          * @event beforerequest
10974          * Fires before a network request is made to retrieve a data object.
10975          * @param {Connection} conn This Connection object.
10976          * @param {Object} options The options config object passed to the {@link #request} method.
10977          */
10978         "beforerequest" : true,
10979         /**
10980          * @event requestcomplete
10981          * Fires if the request was successfully completed.
10982          * @param {Connection} conn This Connection object.
10983          * @param {Object} response The XHR object containing the response data.
10984          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10985          * @param {Object} options The options config object passed to the {@link #request} method.
10986          */
10987         "requestcomplete" : true,
10988         /**
10989          * @event requestexception
10990          * Fires if an error HTTP status was returned from the server.
10991          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10992          * @param {Connection} conn This Connection object.
10993          * @param {Object} response The XHR object containing the response data.
10994          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10995          * @param {Object} options The options config object passed to the {@link #request} method.
10996          */
10997         "requestexception" : true
10998     });
10999     Roo.data.Connection.superclass.constructor.call(this);
11000 };
11001
11002 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11003     /**
11004      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11005      */
11006     /**
11007      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11008      * extra parameters to each request made by this object. (defaults to undefined)
11009      */
11010     /**
11011      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11012      *  to each request made by this object. (defaults to undefined)
11013      */
11014     /**
11015      * @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)
11016      */
11017     /**
11018      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11019      */
11020     timeout : 30000,
11021     /**
11022      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11023      * @type Boolean
11024      */
11025     autoAbort:false,
11026
11027     /**
11028      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11029      * @type Boolean
11030      */
11031     disableCaching: true,
11032
11033     /**
11034      * Sends an HTTP request to a remote server.
11035      * @param {Object} options An object which may contain the following properties:<ul>
11036      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11037      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11038      * request, a url encoded string or a function to call to get either.</li>
11039      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11040      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11041      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11042      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11043      * <li>options {Object} The parameter to the request call.</li>
11044      * <li>success {Boolean} True if the request succeeded.</li>
11045      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11046      * </ul></li>
11047      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11048      * The callback is passed the following parameters:<ul>
11049      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11050      * <li>options {Object} The parameter to the request call.</li>
11051      * </ul></li>
11052      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11053      * The callback is passed the following parameters:<ul>
11054      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11055      * <li>options {Object} The parameter to the request call.</li>
11056      * </ul></li>
11057      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11058      * for the callback function. Defaults to the browser window.</li>
11059      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11060      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11061      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11062      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11063      * params for the post data. Any params will be appended to the URL.</li>
11064      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11065      * </ul>
11066      * @return {Number} transactionId
11067      */
11068     request : function(o){
11069         if(this.fireEvent("beforerequest", this, o) !== false){
11070             var p = o.params;
11071
11072             if(typeof p == "function"){
11073                 p = p.call(o.scope||window, o);
11074             }
11075             if(typeof p == "object"){
11076                 p = Roo.urlEncode(o.params);
11077             }
11078             if(this.extraParams){
11079                 var extras = Roo.urlEncode(this.extraParams);
11080                 p = p ? (p + '&' + extras) : extras;
11081             }
11082
11083             var url = o.url || this.url;
11084             if(typeof url == 'function'){
11085                 url = url.call(o.scope||window, o);
11086             }
11087
11088             if(o.form){
11089                 var form = Roo.getDom(o.form);
11090                 url = url || form.action;
11091
11092                 var enctype = form.getAttribute("enctype");
11093                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11094                     return this.doFormUpload(o, p, url);
11095                 }
11096                 var f = Roo.lib.Ajax.serializeForm(form);
11097                 p = p ? (p + '&' + f) : f;
11098             }
11099
11100             var hs = o.headers;
11101             if(this.defaultHeaders){
11102                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11103                 if(!o.headers){
11104                     o.headers = hs;
11105                 }
11106             }
11107
11108             var cb = {
11109                 success: this.handleResponse,
11110                 failure: this.handleFailure,
11111                 scope: this,
11112                 argument: {options: o},
11113                 timeout : this.timeout
11114             };
11115
11116             var method = o.method||this.method||(p ? "POST" : "GET");
11117
11118             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11119                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11120             }
11121
11122             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11123                 if(o.autoAbort){
11124                     this.abort();
11125                 }
11126             }else if(this.autoAbort !== false){
11127                 this.abort();
11128             }
11129
11130             if((method == 'GET' && p) || o.xmlData){
11131                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11132                 p = '';
11133             }
11134             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11135             return this.transId;
11136         }else{
11137             Roo.callback(o.callback, o.scope, [o, null, null]);
11138             return null;
11139         }
11140     },
11141
11142     /**
11143      * Determine whether this object has a request outstanding.
11144      * @param {Number} transactionId (Optional) defaults to the last transaction
11145      * @return {Boolean} True if there is an outstanding request.
11146      */
11147     isLoading : function(transId){
11148         if(transId){
11149             return Roo.lib.Ajax.isCallInProgress(transId);
11150         }else{
11151             return this.transId ? true : false;
11152         }
11153     },
11154
11155     /**
11156      * Aborts any outstanding request.
11157      * @param {Number} transactionId (Optional) defaults to the last transaction
11158      */
11159     abort : function(transId){
11160         if(transId || this.isLoading()){
11161             Roo.lib.Ajax.abort(transId || this.transId);
11162         }
11163     },
11164
11165     // private
11166     handleResponse : function(response){
11167         this.transId = false;
11168         var options = response.argument.options;
11169         response.argument = options ? options.argument : null;
11170         this.fireEvent("requestcomplete", this, response, options);
11171         Roo.callback(options.success, options.scope, [response, options]);
11172         Roo.callback(options.callback, options.scope, [options, true, response]);
11173     },
11174
11175     // private
11176     handleFailure : function(response, e){
11177         this.transId = false;
11178         var options = response.argument.options;
11179         response.argument = options ? options.argument : null;
11180         this.fireEvent("requestexception", this, response, options, e);
11181         Roo.callback(options.failure, options.scope, [response, options]);
11182         Roo.callback(options.callback, options.scope, [options, false, response]);
11183     },
11184
11185     // private
11186     doFormUpload : function(o, ps, url){
11187         var id = Roo.id();
11188         var frame = document.createElement('iframe');
11189         frame.id = id;
11190         frame.name = id;
11191         frame.className = 'x-hidden';
11192         if(Roo.isIE){
11193             frame.src = Roo.SSL_SECURE_URL;
11194         }
11195         document.body.appendChild(frame);
11196
11197         if(Roo.isIE){
11198            document.frames[id].name = id;
11199         }
11200
11201         var form = Roo.getDom(o.form);
11202         form.target = id;
11203         form.method = 'POST';
11204         form.enctype = form.encoding = 'multipart/form-data';
11205         if(url){
11206             form.action = url;
11207         }
11208
11209         var hiddens, hd;
11210         if(ps){ // add dynamic params
11211             hiddens = [];
11212             ps = Roo.urlDecode(ps, false);
11213             for(var k in ps){
11214                 if(ps.hasOwnProperty(k)){
11215                     hd = document.createElement('input');
11216                     hd.type = 'hidden';
11217                     hd.name = k;
11218                     hd.value = ps[k];
11219                     form.appendChild(hd);
11220                     hiddens.push(hd);
11221                 }
11222             }
11223         }
11224
11225         function cb(){
11226             var r = {  // bogus response object
11227                 responseText : '',
11228                 responseXML : null
11229             };
11230
11231             r.argument = o ? o.argument : null;
11232
11233             try { //
11234                 var doc;
11235                 if(Roo.isIE){
11236                     doc = frame.contentWindow.document;
11237                 }else {
11238                     doc = (frame.contentDocument || window.frames[id].document);
11239                 }
11240                 if(doc && doc.body){
11241                     r.responseText = doc.body.innerHTML;
11242                 }
11243                 if(doc && doc.XMLDocument){
11244                     r.responseXML = doc.XMLDocument;
11245                 }else {
11246                     r.responseXML = doc;
11247                 }
11248             }
11249             catch(e) {
11250                 // ignore
11251             }
11252
11253             Roo.EventManager.removeListener(frame, 'load', cb, this);
11254
11255             this.fireEvent("requestcomplete", this, r, o);
11256             Roo.callback(o.success, o.scope, [r, o]);
11257             Roo.callback(o.callback, o.scope, [o, true, r]);
11258
11259             setTimeout(function(){document.body.removeChild(frame);}, 100);
11260         }
11261
11262         Roo.EventManager.on(frame, 'load', cb, this);
11263         form.submit();
11264
11265         if(hiddens){ // remove dynamic params
11266             for(var i = 0, len = hiddens.length; i < len; i++){
11267                 form.removeChild(hiddens[i]);
11268             }
11269         }
11270     }
11271 });
11272
11273 /**
11274  * @class Roo.Ajax
11275  * @extends Roo.data.Connection
11276  * Global Ajax request class.
11277  *
11278  * @singleton
11279  */
11280 Roo.Ajax = new Roo.data.Connection({
11281     // fix up the docs
11282    /**
11283      * @cfg {String} url @hide
11284      */
11285     /**
11286      * @cfg {Object} extraParams @hide
11287      */
11288     /**
11289      * @cfg {Object} defaultHeaders @hide
11290      */
11291     /**
11292      * @cfg {String} method (Optional) @hide
11293      */
11294     /**
11295      * @cfg {Number} timeout (Optional) @hide
11296      */
11297     /**
11298      * @cfg {Boolean} autoAbort (Optional) @hide
11299      */
11300
11301     /**
11302      * @cfg {Boolean} disableCaching (Optional) @hide
11303      */
11304
11305     /**
11306      * @property  disableCaching
11307      * True to add a unique cache-buster param to GET requests. (defaults to true)
11308      * @type Boolean
11309      */
11310     /**
11311      * @property  url
11312      * The default URL to be used for requests to the server. (defaults to undefined)
11313      * @type String
11314      */
11315     /**
11316      * @property  extraParams
11317      * An object containing properties which are used as
11318      * extra parameters to each request made by this object. (defaults to undefined)
11319      * @type Object
11320      */
11321     /**
11322      * @property  defaultHeaders
11323      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11324      * @type Object
11325      */
11326     /**
11327      * @property  method
11328      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11329      * @type String
11330      */
11331     /**
11332      * @property  timeout
11333      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11334      * @type Number
11335      */
11336
11337     /**
11338      * @property  autoAbort
11339      * Whether a new request should abort any pending requests. (defaults to false)
11340      * @type Boolean
11341      */
11342     autoAbort : false,
11343
11344     /**
11345      * Serialize the passed form into a url encoded string
11346      * @param {String/HTMLElement} form
11347      * @return {String}
11348      */
11349     serializeForm : function(form){
11350         return Roo.lib.Ajax.serializeForm(form);
11351     }
11352 });/*
11353  * Based on:
11354  * Ext JS Library 1.1.1
11355  * Copyright(c) 2006-2007, Ext JS, LLC.
11356  *
11357  * Originally Released Under LGPL - original licence link has changed is not relivant.
11358  *
11359  * Fork - LGPL
11360  * <script type="text/javascript">
11361  */
11362  
11363 /**
11364  * @class Roo.Ajax
11365  * @extends Roo.data.Connection
11366  * Global Ajax request class.
11367  *
11368  * @instanceOf  Roo.data.Connection
11369  */
11370 Roo.Ajax = new Roo.data.Connection({
11371     // fix up the docs
11372     
11373     /**
11374      * fix up scoping
11375      * @scope Roo.Ajax
11376      */
11377     
11378    /**
11379      * @cfg {String} url @hide
11380      */
11381     /**
11382      * @cfg {Object} extraParams @hide
11383      */
11384     /**
11385      * @cfg {Object} defaultHeaders @hide
11386      */
11387     /**
11388      * @cfg {String} method (Optional) @hide
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) @hide
11392      */
11393     /**
11394      * @cfg {Boolean} autoAbort (Optional) @hide
11395      */
11396
11397     /**
11398      * @cfg {Boolean} disableCaching (Optional) @hide
11399      */
11400
11401     /**
11402      * @property  disableCaching
11403      * True to add a unique cache-buster param to GET requests. (defaults to true)
11404      * @type Boolean
11405      */
11406     /**
11407      * @property  url
11408      * The default URL to be used for requests to the server. (defaults to undefined)
11409      * @type String
11410      */
11411     /**
11412      * @property  extraParams
11413      * An object containing properties which are used as
11414      * extra parameters to each request made by this object. (defaults to undefined)
11415      * @type Object
11416      */
11417     /**
11418      * @property  defaultHeaders
11419      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11420      * @type Object
11421      */
11422     /**
11423      * @property  method
11424      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11425      * @type String
11426      */
11427     /**
11428      * @property  timeout
11429      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11430      * @type Number
11431      */
11432
11433     /**
11434      * @property  autoAbort
11435      * Whether a new request should abort any pending requests. (defaults to false)
11436      * @type Boolean
11437      */
11438     autoAbort : false,
11439
11440     /**
11441      * Serialize the passed form into a url encoded string
11442      * @param {String/HTMLElement} form
11443      * @return {String}
11444      */
11445     serializeForm : function(form){
11446         return Roo.lib.Ajax.serializeForm(form);
11447     }
11448 });/*
11449  * Based on:
11450  * Ext JS Library 1.1.1
11451  * Copyright(c) 2006-2007, Ext JS, LLC.
11452  *
11453  * Originally Released Under LGPL - original licence link has changed is not relivant.
11454  *
11455  * Fork - LGPL
11456  * <script type="text/javascript">
11457  */
11458
11459  
11460 /**
11461  * @class Roo.UpdateManager
11462  * @extends Roo.util.Observable
11463  * Provides AJAX-style update for Element object.<br><br>
11464  * Usage:<br>
11465  * <pre><code>
11466  * // Get it from a Roo.Element object
11467  * var el = Roo.get("foo");
11468  * var mgr = el.getUpdateManager();
11469  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11470  * ...
11471  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11472  * <br>
11473  * // or directly (returns the same UpdateManager instance)
11474  * var mgr = new Roo.UpdateManager("myElementId");
11475  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11476  * mgr.on("update", myFcnNeedsToKnow);
11477  * <br>
11478    // short handed call directly from the element object
11479    Roo.get("foo").load({
11480         url: "bar.php",
11481         scripts:true,
11482         params: "for=bar",
11483         text: "Loading Foo..."
11484    });
11485  * </code></pre>
11486  * @constructor
11487  * Create new UpdateManager directly.
11488  * @param {String/HTMLElement/Roo.Element} el The element to update
11489  * @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).
11490  */
11491 Roo.UpdateManager = function(el, forceNew){
11492     el = Roo.get(el);
11493     if(!forceNew && el.updateManager){
11494         return el.updateManager;
11495     }
11496     /**
11497      * The Element object
11498      * @type Roo.Element
11499      */
11500     this.el = el;
11501     /**
11502      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11503      * @type String
11504      */
11505     this.defaultUrl = null;
11506
11507     this.addEvents({
11508         /**
11509          * @event beforeupdate
11510          * Fired before an update is made, return false from your handler and the update is cancelled.
11511          * @param {Roo.Element} el
11512          * @param {String/Object/Function} url
11513          * @param {String/Object} params
11514          */
11515         "beforeupdate": true,
11516         /**
11517          * @event update
11518          * Fired after successful update is made.
11519          * @param {Roo.Element} el
11520          * @param {Object} oResponseObject The response Object
11521          */
11522         "update": true,
11523         /**
11524          * @event failure
11525          * Fired on update failure.
11526          * @param {Roo.Element} el
11527          * @param {Object} oResponseObject The response Object
11528          */
11529         "failure": true
11530     });
11531     var d = Roo.UpdateManager.defaults;
11532     /**
11533      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11534      * @type String
11535      */
11536     this.sslBlankUrl = d.sslBlankUrl;
11537     /**
11538      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11539      * @type Boolean
11540      */
11541     this.disableCaching = d.disableCaching;
11542     /**
11543      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11544      * @type String
11545      */
11546     this.indicatorText = d.indicatorText;
11547     /**
11548      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11549      * @type String
11550      */
11551     this.showLoadIndicator = d.showLoadIndicator;
11552     /**
11553      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11554      * @type Number
11555      */
11556     this.timeout = d.timeout;
11557
11558     /**
11559      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11560      * @type Boolean
11561      */
11562     this.loadScripts = d.loadScripts;
11563
11564     /**
11565      * Transaction object of current executing transaction
11566      */
11567     this.transaction = null;
11568
11569     /**
11570      * @private
11571      */
11572     this.autoRefreshProcId = null;
11573     /**
11574      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11575      * @type Function
11576      */
11577     this.refreshDelegate = this.refresh.createDelegate(this);
11578     /**
11579      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11580      * @type Function
11581      */
11582     this.updateDelegate = this.update.createDelegate(this);
11583     /**
11584      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11585      * @type Function
11586      */
11587     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11588     /**
11589      * @private
11590      */
11591     this.successDelegate = this.processSuccess.createDelegate(this);
11592     /**
11593      * @private
11594      */
11595     this.failureDelegate = this.processFailure.createDelegate(this);
11596
11597     if(!this.renderer){
11598      /**
11599       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11600       */
11601     this.renderer = new Roo.UpdateManager.BasicRenderer();
11602     }
11603     
11604     Roo.UpdateManager.superclass.constructor.call(this);
11605 };
11606
11607 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11608     /**
11609      * Get the Element this UpdateManager is bound to
11610      * @return {Roo.Element} The element
11611      */
11612     getEl : function(){
11613         return this.el;
11614     },
11615     /**
11616      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11617      * @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:
11618 <pre><code>
11619 um.update({<br/>
11620     url: "your-url.php",<br/>
11621     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11622     callback: yourFunction,<br/>
11623     scope: yourObject, //(optional scope)  <br/>
11624     discardUrl: false, <br/>
11625     nocache: false,<br/>
11626     text: "Loading...",<br/>
11627     timeout: 30,<br/>
11628     scripts: false<br/>
11629 });
11630 </code></pre>
11631      * The only required property is url. The optional properties nocache, text and scripts
11632      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11633      * @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}
11634      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11635      * @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.
11636      */
11637     update : function(url, params, callback, discardUrl){
11638         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11639             var method = this.method, cfg;
11640             if(typeof url == "object"){ // must be config object
11641                 cfg = url;
11642                 url = cfg.url;
11643                 params = params || cfg.params;
11644                 callback = callback || cfg.callback;
11645                 discardUrl = discardUrl || cfg.discardUrl;
11646                 if(callback && cfg.scope){
11647                     callback = callback.createDelegate(cfg.scope);
11648                 }
11649                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11650                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11651                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11652                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11653                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11654             }
11655             this.showLoading();
11656             if(!discardUrl){
11657                 this.defaultUrl = url;
11658             }
11659             if(typeof url == "function"){
11660                 url = url.call(this);
11661             }
11662
11663             method = method || (params ? "POST" : "GET");
11664             if(method == "GET"){
11665                 url = this.prepareUrl(url);
11666             }
11667
11668             var o = Roo.apply(cfg ||{}, {
11669                 url : url,
11670                 params: params,
11671                 success: this.successDelegate,
11672                 failure: this.failureDelegate,
11673                 callback: undefined,
11674                 timeout: (this.timeout*1000),
11675                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11676             });
11677
11678             this.transaction = Roo.Ajax.request(o);
11679         }
11680     },
11681
11682     /**
11683      * 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.
11684      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11685      * @param {String/HTMLElement} form The form Id or form element
11686      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11687      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11688      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11689      */
11690     formUpdate : function(form, url, reset, callback){
11691         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11692             if(typeof url == "function"){
11693                 url = url.call(this);
11694             }
11695             form = Roo.getDom(form);
11696             this.transaction = Roo.Ajax.request({
11697                 form: form,
11698                 url:url,
11699                 success: this.successDelegate,
11700                 failure: this.failureDelegate,
11701                 timeout: (this.timeout*1000),
11702                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11703             });
11704             this.showLoading.defer(1, this);
11705         }
11706     },
11707
11708     /**
11709      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11710      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11711      */
11712     refresh : function(callback){
11713         if(this.defaultUrl == null){
11714             return;
11715         }
11716         this.update(this.defaultUrl, null, callback, true);
11717     },
11718
11719     /**
11720      * Set this element to auto refresh.
11721      * @param {Number} interval How often to update (in seconds).
11722      * @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)
11723      * @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}
11724      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11725      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11726      */
11727     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11728         if(refreshNow){
11729             this.update(url || this.defaultUrl, params, callback, true);
11730         }
11731         if(this.autoRefreshProcId){
11732             clearInterval(this.autoRefreshProcId);
11733         }
11734         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11735     },
11736
11737     /**
11738      * Stop auto refresh on this element.
11739      */
11740      stopAutoRefresh : function(){
11741         if(this.autoRefreshProcId){
11742             clearInterval(this.autoRefreshProcId);
11743             delete this.autoRefreshProcId;
11744         }
11745     },
11746
11747     isAutoRefreshing : function(){
11748        return this.autoRefreshProcId ? true : false;
11749     },
11750     /**
11751      * Called to update the element to "Loading" state. Override to perform custom action.
11752      */
11753     showLoading : function(){
11754         if(this.showLoadIndicator){
11755             this.el.update(this.indicatorText);
11756         }
11757     },
11758
11759     /**
11760      * Adds unique parameter to query string if disableCaching = true
11761      * @private
11762      */
11763     prepareUrl : function(url){
11764         if(this.disableCaching){
11765             var append = "_dc=" + (new Date().getTime());
11766             if(url.indexOf("?") !== -1){
11767                 url += "&" + append;
11768             }else{
11769                 url += "?" + append;
11770             }
11771         }
11772         return url;
11773     },
11774
11775     /**
11776      * @private
11777      */
11778     processSuccess : function(response){
11779         this.transaction = null;
11780         if(response.argument.form && response.argument.reset){
11781             try{ // put in try/catch since some older FF releases had problems with this
11782                 response.argument.form.reset();
11783             }catch(e){}
11784         }
11785         if(this.loadScripts){
11786             this.renderer.render(this.el, response, this,
11787                 this.updateComplete.createDelegate(this, [response]));
11788         }else{
11789             this.renderer.render(this.el, response, this);
11790             this.updateComplete(response);
11791         }
11792     },
11793
11794     updateComplete : function(response){
11795         this.fireEvent("update", this.el, response);
11796         if(typeof response.argument.callback == "function"){
11797             response.argument.callback(this.el, true, response);
11798         }
11799     },
11800
11801     /**
11802      * @private
11803      */
11804     processFailure : function(response){
11805         this.transaction = null;
11806         this.fireEvent("failure", this.el, response);
11807         if(typeof response.argument.callback == "function"){
11808             response.argument.callback(this.el, false, response);
11809         }
11810     },
11811
11812     /**
11813      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11814      * @param {Object} renderer The object implementing the render() method
11815      */
11816     setRenderer : function(renderer){
11817         this.renderer = renderer;
11818     },
11819
11820     getRenderer : function(){
11821        return this.renderer;
11822     },
11823
11824     /**
11825      * Set the defaultUrl used for updates
11826      * @param {String/Function} defaultUrl The url or a function to call to get the url
11827      */
11828     setDefaultUrl : function(defaultUrl){
11829         this.defaultUrl = defaultUrl;
11830     },
11831
11832     /**
11833      * Aborts the executing transaction
11834      */
11835     abort : function(){
11836         if(this.transaction){
11837             Roo.Ajax.abort(this.transaction);
11838         }
11839     },
11840
11841     /**
11842      * Returns true if an update is in progress
11843      * @return {Boolean}
11844      */
11845     isUpdating : function(){
11846         if(this.transaction){
11847             return Roo.Ajax.isLoading(this.transaction);
11848         }
11849         return false;
11850     }
11851 });
11852
11853 /**
11854  * @class Roo.UpdateManager.defaults
11855  * @static (not really - but it helps the doc tool)
11856  * The defaults collection enables customizing the default properties of UpdateManager
11857  */
11858    Roo.UpdateManager.defaults = {
11859        /**
11860          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11861          * @type Number
11862          */
11863          timeout : 30,
11864
11865          /**
11866          * True to process scripts by default (Defaults to false).
11867          * @type Boolean
11868          */
11869         loadScripts : false,
11870
11871         /**
11872         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11873         * @type String
11874         */
11875         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11876         /**
11877          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11878          * @type Boolean
11879          */
11880         disableCaching : false,
11881         /**
11882          * Whether to show indicatorText when loading (Defaults to true).
11883          * @type Boolean
11884          */
11885         showLoadIndicator : true,
11886         /**
11887          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11888          * @type String
11889          */
11890         indicatorText : '<div class="loading-indicator">Loading...</div>'
11891    };
11892
11893 /**
11894  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11895  *Usage:
11896  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11897  * @param {String/HTMLElement/Roo.Element} el The element to update
11898  * @param {String} url The url
11899  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11900  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11901  * @static
11902  * @deprecated
11903  * @member Roo.UpdateManager
11904  */
11905 Roo.UpdateManager.updateElement = function(el, url, params, options){
11906     var um = Roo.get(el, true).getUpdateManager();
11907     Roo.apply(um, options);
11908     um.update(url, params, options ? options.callback : null);
11909 };
11910 // alias for backwards compat
11911 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11912 /**
11913  * @class Roo.UpdateManager.BasicRenderer
11914  * Default Content renderer. Updates the elements innerHTML with the responseText.
11915  */
11916 Roo.UpdateManager.BasicRenderer = function(){};
11917
11918 Roo.UpdateManager.BasicRenderer.prototype = {
11919     /**
11920      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11921      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11922      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11923      * @param {Roo.Element} el The element being rendered
11924      * @param {Object} response The YUI Connect response object
11925      * @param {UpdateManager} updateManager The calling update manager
11926      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11927      */
11928      render : function(el, response, updateManager, callback){
11929         el.update(response.responseText, updateManager.loadScripts, callback);
11930     }
11931 };
11932 /*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942
11943 /**
11944  * @class Roo.util.DelayedTask
11945  * Provides a convenient method of performing setTimeout where a new
11946  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11947  * You can use this class to buffer
11948  * the keypress events for a certain number of milliseconds, and perform only if they stop
11949  * for that amount of time.
11950  * @constructor The parameters to this constructor serve as defaults and are not required.
11951  * @param {Function} fn (optional) The default function to timeout
11952  * @param {Object} scope (optional) The default scope of that timeout
11953  * @param {Array} args (optional) The default Array of arguments
11954  */
11955 Roo.util.DelayedTask = function(fn, scope, args){
11956     var id = null, d, t;
11957
11958     var call = function(){
11959         var now = new Date().getTime();
11960         if(now - t >= d){
11961             clearInterval(id);
11962             id = null;
11963             fn.apply(scope, args || []);
11964         }
11965     };
11966     /**
11967      * Cancels any pending timeout and queues a new one
11968      * @param {Number} delay The milliseconds to delay
11969      * @param {Function} newFn (optional) Overrides function passed to constructor
11970      * @param {Object} newScope (optional) Overrides scope passed to constructor
11971      * @param {Array} newArgs (optional) Overrides args passed to constructor
11972      */
11973     this.delay = function(delay, newFn, newScope, newArgs){
11974         if(id && delay != d){
11975             this.cancel();
11976         }
11977         d = delay;
11978         t = new Date().getTime();
11979         fn = newFn || fn;
11980         scope = newScope || scope;
11981         args = newArgs || args;
11982         if(!id){
11983             id = setInterval(call, d);
11984         }
11985     };
11986
11987     /**
11988      * Cancel the last queued timeout
11989      */
11990     this.cancel = function(){
11991         if(id){
11992             clearInterval(id);
11993             id = null;
11994         }
11995     };
11996 };/*
11997  * Based on:
11998  * Ext JS Library 1.1.1
11999  * Copyright(c) 2006-2007, Ext JS, LLC.
12000  *
12001  * Originally Released Under LGPL - original licence link has changed is not relivant.
12002  *
12003  * Fork - LGPL
12004  * <script type="text/javascript">
12005  */
12006  
12007  
12008 Roo.util.TaskRunner = function(interval){
12009     interval = interval || 10;
12010     var tasks = [], removeQueue = [];
12011     var id = 0;
12012     var running = false;
12013
12014     var stopThread = function(){
12015         running = false;
12016         clearInterval(id);
12017         id = 0;
12018     };
12019
12020     var startThread = function(){
12021         if(!running){
12022             running = true;
12023             id = setInterval(runTasks, interval);
12024         }
12025     };
12026
12027     var removeTask = function(task){
12028         removeQueue.push(task);
12029         if(task.onStop){
12030             task.onStop();
12031         }
12032     };
12033
12034     var runTasks = function(){
12035         if(removeQueue.length > 0){
12036             for(var i = 0, len = removeQueue.length; i < len; i++){
12037                 tasks.remove(removeQueue[i]);
12038             }
12039             removeQueue = [];
12040             if(tasks.length < 1){
12041                 stopThread();
12042                 return;
12043             }
12044         }
12045         var now = new Date().getTime();
12046         for(var i = 0, len = tasks.length; i < len; ++i){
12047             var t = tasks[i];
12048             var itime = now - t.taskRunTime;
12049             if(t.interval <= itime){
12050                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12051                 t.taskRunTime = now;
12052                 if(rt === false || t.taskRunCount === t.repeat){
12053                     removeTask(t);
12054                     return;
12055                 }
12056             }
12057             if(t.duration && t.duration <= (now - t.taskStartTime)){
12058                 removeTask(t);
12059             }
12060         }
12061     };
12062
12063     /**
12064      * Queues a new task.
12065      * @param {Object} task
12066      */
12067     this.start = function(task){
12068         tasks.push(task);
12069         task.taskStartTime = new Date().getTime();
12070         task.taskRunTime = 0;
12071         task.taskRunCount = 0;
12072         startThread();
12073         return task;
12074     };
12075
12076     this.stop = function(task){
12077         removeTask(task);
12078         return task;
12079     };
12080
12081     this.stopAll = function(){
12082         stopThread();
12083         for(var i = 0, len = tasks.length; i < len; i++){
12084             if(tasks[i].onStop){
12085                 tasks[i].onStop();
12086             }
12087         }
12088         tasks = [];
12089         removeQueue = [];
12090     };
12091 };
12092
12093 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103
12104  
12105 /**
12106  * @class Roo.util.MixedCollection
12107  * @extends Roo.util.Observable
12108  * A Collection class that maintains both numeric indexes and keys and exposes events.
12109  * @constructor
12110  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12111  * collection (defaults to false)
12112  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12113  * and return the key value for that item.  This is used when available to look up the key on items that
12114  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12115  * equivalent to providing an implementation for the {@link #getKey} method.
12116  */
12117 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12118     this.items = [];
12119     this.map = {};
12120     this.keys = [];
12121     this.length = 0;
12122     this.addEvents({
12123         /**
12124          * @event clear
12125          * Fires when the collection is cleared.
12126          */
12127         "clear" : true,
12128         /**
12129          * @event add
12130          * Fires when an item is added to the collection.
12131          * @param {Number} index The index at which the item was added.
12132          * @param {Object} o The item added.
12133          * @param {String} key The key associated with the added item.
12134          */
12135         "add" : true,
12136         /**
12137          * @event replace
12138          * Fires when an item is replaced in the collection.
12139          * @param {String} key he key associated with the new added.
12140          * @param {Object} old The item being replaced.
12141          * @param {Object} new The new item.
12142          */
12143         "replace" : true,
12144         /**
12145          * @event remove
12146          * Fires when an item is removed from the collection.
12147          * @param {Object} o The item being removed.
12148          * @param {String} key (optional) The key associated with the removed item.
12149          */
12150         "remove" : true,
12151         "sort" : true
12152     });
12153     this.allowFunctions = allowFunctions === true;
12154     if(keyFn){
12155         this.getKey = keyFn;
12156     }
12157     Roo.util.MixedCollection.superclass.constructor.call(this);
12158 };
12159
12160 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12161     allowFunctions : false,
12162     
12163 /**
12164  * Adds an item to the collection.
12165  * @param {String} key The key to associate with the item
12166  * @param {Object} o The item to add.
12167  * @return {Object} The item added.
12168  */
12169     add : function(key, o){
12170         if(arguments.length == 1){
12171             o = arguments[0];
12172             key = this.getKey(o);
12173         }
12174         if(typeof key == "undefined" || key === null){
12175             this.length++;
12176             this.items.push(o);
12177             this.keys.push(null);
12178         }else{
12179             var old = this.map[key];
12180             if(old){
12181                 return this.replace(key, o);
12182             }
12183             this.length++;
12184             this.items.push(o);
12185             this.map[key] = o;
12186             this.keys.push(key);
12187         }
12188         this.fireEvent("add", this.length-1, o, key);
12189         return o;
12190     },
12191    
12192 /**
12193   * MixedCollection has a generic way to fetch keys if you implement getKey.
12194 <pre><code>
12195 // normal way
12196 var mc = new Roo.util.MixedCollection();
12197 mc.add(someEl.dom.id, someEl);
12198 mc.add(otherEl.dom.id, otherEl);
12199 //and so on
12200
12201 // using getKey
12202 var mc = new Roo.util.MixedCollection();
12203 mc.getKey = function(el){
12204    return el.dom.id;
12205 };
12206 mc.add(someEl);
12207 mc.add(otherEl);
12208
12209 // or via the constructor
12210 var mc = new Roo.util.MixedCollection(false, function(el){
12211    return el.dom.id;
12212 });
12213 mc.add(someEl);
12214 mc.add(otherEl);
12215 </code></pre>
12216  * @param o {Object} The item for which to find the key.
12217  * @return {Object} The key for the passed item.
12218  */
12219     getKey : function(o){
12220          return o.id; 
12221     },
12222    
12223 /**
12224  * Replaces an item in the collection.
12225  * @param {String} key The key associated with the item to replace, or the item to replace.
12226  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12227  * @return {Object}  The new item.
12228  */
12229     replace : function(key, o){
12230         if(arguments.length == 1){
12231             o = arguments[0];
12232             key = this.getKey(o);
12233         }
12234         var old = this.item(key);
12235         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12236              return this.add(key, o);
12237         }
12238         var index = this.indexOfKey(key);
12239         this.items[index] = o;
12240         this.map[key] = o;
12241         this.fireEvent("replace", key, old, o);
12242         return o;
12243     },
12244    
12245 /**
12246  * Adds all elements of an Array or an Object to the collection.
12247  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12248  * an Array of values, each of which are added to the collection.
12249  */
12250     addAll : function(objs){
12251         if(arguments.length > 1 || objs instanceof Array){
12252             var args = arguments.length > 1 ? arguments : objs;
12253             for(var i = 0, len = args.length; i < len; i++){
12254                 this.add(args[i]);
12255             }
12256         }else{
12257             for(var key in objs){
12258                 if(this.allowFunctions || typeof objs[key] != "function"){
12259                     this.add(key, objs[key]);
12260                 }
12261             }
12262         }
12263     },
12264    
12265 /**
12266  * Executes the specified function once for every item in the collection, passing each
12267  * item as the first and only parameter. returning false from the function will stop the iteration.
12268  * @param {Function} fn The function to execute for each item.
12269  * @param {Object} scope (optional) The scope in which to execute the function.
12270  */
12271     each : function(fn, scope){
12272         var items = [].concat(this.items); // each safe for removal
12273         for(var i = 0, len = items.length; i < len; i++){
12274             if(fn.call(scope || items[i], items[i], i, len) === false){
12275                 break;
12276             }
12277         }
12278     },
12279    
12280 /**
12281  * Executes the specified function once for every key in the collection, passing each
12282  * key, and its associated item as the first two parameters.
12283  * @param {Function} fn The function to execute for each item.
12284  * @param {Object} scope (optional) The scope in which to execute the function.
12285  */
12286     eachKey : function(fn, scope){
12287         for(var i = 0, len = this.keys.length; i < len; i++){
12288             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12289         }
12290     },
12291    
12292 /**
12293  * Returns the first item in the collection which elicits a true return value from the
12294  * passed selection function.
12295  * @param {Function} fn The selection function to execute for each item.
12296  * @param {Object} scope (optional) The scope in which to execute the function.
12297  * @return {Object} The first item in the collection which returned true from the selection function.
12298  */
12299     find : function(fn, scope){
12300         for(var i = 0, len = this.items.length; i < len; i++){
12301             if(fn.call(scope || window, this.items[i], this.keys[i])){
12302                 return this.items[i];
12303             }
12304         }
12305         return null;
12306     },
12307    
12308 /**
12309  * Inserts an item at the specified index in the collection.
12310  * @param {Number} index The index to insert the item at.
12311  * @param {String} key The key to associate with the new item, or the item itself.
12312  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12313  * @return {Object} The item inserted.
12314  */
12315     insert : function(index, key, o){
12316         if(arguments.length == 2){
12317             o = arguments[1];
12318             key = this.getKey(o);
12319         }
12320         if(index >= this.length){
12321             return this.add(key, o);
12322         }
12323         this.length++;
12324         this.items.splice(index, 0, o);
12325         if(typeof key != "undefined" && key != null){
12326             this.map[key] = o;
12327         }
12328         this.keys.splice(index, 0, key);
12329         this.fireEvent("add", index, o, key);
12330         return o;
12331     },
12332    
12333 /**
12334  * Removed an item from the collection.
12335  * @param {Object} o The item to remove.
12336  * @return {Object} The item removed.
12337  */
12338     remove : function(o){
12339         return this.removeAt(this.indexOf(o));
12340     },
12341    
12342 /**
12343  * Remove an item from a specified index in the collection.
12344  * @param {Number} index The index within the collection of the item to remove.
12345  */
12346     removeAt : function(index){
12347         if(index < this.length && index >= 0){
12348             this.length--;
12349             var o = this.items[index];
12350             this.items.splice(index, 1);
12351             var key = this.keys[index];
12352             if(typeof key != "undefined"){
12353                 delete this.map[key];
12354             }
12355             this.keys.splice(index, 1);
12356             this.fireEvent("remove", o, key);
12357         }
12358     },
12359    
12360 /**
12361  * Removed an item associated with the passed key fom the collection.
12362  * @param {String} key The key of the item to remove.
12363  */
12364     removeKey : function(key){
12365         return this.removeAt(this.indexOfKey(key));
12366     },
12367    
12368 /**
12369  * Returns the number of items in the collection.
12370  * @return {Number} the number of items in the collection.
12371  */
12372     getCount : function(){
12373         return this.length; 
12374     },
12375    
12376 /**
12377  * Returns index within the collection of the passed Object.
12378  * @param {Object} o The item to find the index of.
12379  * @return {Number} index of the item.
12380  */
12381     indexOf : function(o){
12382         if(!this.items.indexOf){
12383             for(var i = 0, len = this.items.length; i < len; i++){
12384                 if(this.items[i] == o) return i;
12385             }
12386             return -1;
12387         }else{
12388             return this.items.indexOf(o);
12389         }
12390     },
12391    
12392 /**
12393  * Returns index within the collection of the passed key.
12394  * @param {String} key The key to find the index of.
12395  * @return {Number} index of the key.
12396  */
12397     indexOfKey : function(key){
12398         if(!this.keys.indexOf){
12399             for(var i = 0, len = this.keys.length; i < len; i++){
12400                 if(this.keys[i] == key) return i;
12401             }
12402             return -1;
12403         }else{
12404             return this.keys.indexOf(key);
12405         }
12406     },
12407    
12408 /**
12409  * Returns the item associated with the passed key OR index. Key has priority over index.
12410  * @param {String/Number} key The key or index of the item.
12411  * @return {Object} The item associated with the passed key.
12412  */
12413     item : function(key){
12414         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12415         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12416     },
12417     
12418 /**
12419  * Returns the item at the specified index.
12420  * @param {Number} index The index of the item.
12421  * @return {Object}
12422  */
12423     itemAt : function(index){
12424         return this.items[index];
12425     },
12426     
12427 /**
12428  * Returns the item associated with the passed key.
12429  * @param {String/Number} key The key of the item.
12430  * @return {Object} The item associated with the passed key.
12431  */
12432     key : function(key){
12433         return this.map[key];
12434     },
12435    
12436 /**
12437  * Returns true if the collection contains the passed Object as an item.
12438  * @param {Object} o  The Object to look for in the collection.
12439  * @return {Boolean} True if the collection contains the Object as an item.
12440  */
12441     contains : function(o){
12442         return this.indexOf(o) != -1;
12443     },
12444    
12445 /**
12446  * Returns true if the collection contains the passed Object as a key.
12447  * @param {String} key The key to look for in the collection.
12448  * @return {Boolean} True if the collection contains the Object as a key.
12449  */
12450     containsKey : function(key){
12451         return typeof this.map[key] != "undefined";
12452     },
12453    
12454 /**
12455  * Removes all items from the collection.
12456  */
12457     clear : function(){
12458         this.length = 0;
12459         this.items = [];
12460         this.keys = [];
12461         this.map = {};
12462         this.fireEvent("clear");
12463     },
12464    
12465 /**
12466  * Returns the first item in the collection.
12467  * @return {Object} the first item in the collection..
12468  */
12469     first : function(){
12470         return this.items[0]; 
12471     },
12472    
12473 /**
12474  * Returns the last item in the collection.
12475  * @return {Object} the last item in the collection..
12476  */
12477     last : function(){
12478         return this.items[this.length-1];   
12479     },
12480     
12481     _sort : function(property, dir, fn){
12482         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12483         fn = fn || function(a, b){
12484             return a-b;
12485         };
12486         var c = [], k = this.keys, items = this.items;
12487         for(var i = 0, len = items.length; i < len; i++){
12488             c[c.length] = {key: k[i], value: items[i], index: i};
12489         }
12490         c.sort(function(a, b){
12491             var v = fn(a[property], b[property]) * dsc;
12492             if(v == 0){
12493                 v = (a.index < b.index ? -1 : 1);
12494             }
12495             return v;
12496         });
12497         for(var i = 0, len = c.length; i < len; i++){
12498             items[i] = c[i].value;
12499             k[i] = c[i].key;
12500         }
12501         this.fireEvent("sort", this);
12502     },
12503     
12504     /**
12505      * Sorts this collection with the passed comparison function
12506      * @param {String} direction (optional) "ASC" or "DESC"
12507      * @param {Function} fn (optional) comparison function
12508      */
12509     sort : function(dir, fn){
12510         this._sort("value", dir, fn);
12511     },
12512     
12513     /**
12514      * Sorts this collection by keys
12515      * @param {String} direction (optional) "ASC" or "DESC"
12516      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12517      */
12518     keySort : function(dir, fn){
12519         this._sort("key", dir, fn || function(a, b){
12520             return String(a).toUpperCase()-String(b).toUpperCase();
12521         });
12522     },
12523     
12524     /**
12525      * Returns a range of items in this collection
12526      * @param {Number} startIndex (optional) defaults to 0
12527      * @param {Number} endIndex (optional) default to the last item
12528      * @return {Array} An array of items
12529      */
12530     getRange : function(start, end){
12531         var items = this.items;
12532         if(items.length < 1){
12533             return [];
12534         }
12535         start = start || 0;
12536         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12537         var r = [];
12538         if(start <= end){
12539             for(var i = start; i <= end; i++) {
12540                     r[r.length] = items[i];
12541             }
12542         }else{
12543             for(var i = start; i >= end; i--) {
12544                     r[r.length] = items[i];
12545             }
12546         }
12547         return r;
12548     },
12549         
12550     /**
12551      * Filter the <i>objects</i> in this collection by a specific property. 
12552      * Returns a new collection that has been filtered.
12553      * @param {String} property A property on your objects
12554      * @param {String/RegExp} value Either string that the property values 
12555      * should start with or a RegExp to test against the property
12556      * @return {MixedCollection} The new filtered collection
12557      */
12558     filter : function(property, value){
12559         if(!value.exec){ // not a regex
12560             value = String(value);
12561             if(value.length == 0){
12562                 return this.clone();
12563             }
12564             value = new RegExp("^" + Roo.escapeRe(value), "i");
12565         }
12566         return this.filterBy(function(o){
12567             return o && value.test(o[property]);
12568         });
12569         },
12570     
12571     /**
12572      * Filter by a function. * Returns a new collection that has been filtered.
12573      * The passed function will be called with each 
12574      * object in the collection. If the function returns true, the value is included 
12575      * otherwise it is filtered.
12576      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12577      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12578      * @return {MixedCollection} The new filtered collection
12579      */
12580     filterBy : function(fn, scope){
12581         var r = new Roo.util.MixedCollection();
12582         r.getKey = this.getKey;
12583         var k = this.keys, it = this.items;
12584         for(var i = 0, len = it.length; i < len; i++){
12585             if(fn.call(scope||this, it[i], k[i])){
12586                                 r.add(k[i], it[i]);
12587                         }
12588         }
12589         return r;
12590     },
12591     
12592     /**
12593      * Creates a duplicate of this collection
12594      * @return {MixedCollection}
12595      */
12596     clone : function(){
12597         var r = new Roo.util.MixedCollection();
12598         var k = this.keys, it = this.items;
12599         for(var i = 0, len = it.length; i < len; i++){
12600             r.add(k[i], it[i]);
12601         }
12602         r.getKey = this.getKey;
12603         return r;
12604     }
12605 });
12606 /**
12607  * Returns the item associated with the passed key or index.
12608  * @method
12609  * @param {String/Number} key The key or index of the item.
12610  * @return {Object} The item associated with the passed key.
12611  */
12612 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12613  * Based on:
12614  * Ext JS Library 1.1.1
12615  * Copyright(c) 2006-2007, Ext JS, LLC.
12616  *
12617  * Originally Released Under LGPL - original licence link has changed is not relivant.
12618  *
12619  * Fork - LGPL
12620  * <script type="text/javascript">
12621  */
12622 /**
12623  * @class Roo.util.JSON
12624  * Modified version of Douglas Crockford"s json.js that doesn"t
12625  * mess with the Object prototype 
12626  * http://www.json.org/js.html
12627  * @singleton
12628  */
12629 Roo.util.JSON = new (function(){
12630     var useHasOwn = {}.hasOwnProperty ? true : false;
12631     
12632     // crashes Safari in some instances
12633     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12634     
12635     var pad = function(n) {
12636         return n < 10 ? "0" + n : n;
12637     };
12638     
12639     var m = {
12640         "\b": '\\b',
12641         "\t": '\\t',
12642         "\n": '\\n',
12643         "\f": '\\f',
12644         "\r": '\\r',
12645         '"' : '\\"',
12646         "\\": '\\\\'
12647     };
12648
12649     var encodeString = function(s){
12650         if (/["\\\x00-\x1f]/.test(s)) {
12651             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12652                 var c = m[b];
12653                 if(c){
12654                     return c;
12655                 }
12656                 c = b.charCodeAt();
12657                 return "\\u00" +
12658                     Math.floor(c / 16).toString(16) +
12659                     (c % 16).toString(16);
12660             }) + '"';
12661         }
12662         return '"' + s + '"';
12663     };
12664     
12665     var encodeArray = function(o){
12666         var a = ["["], b, i, l = o.length, v;
12667             for (i = 0; i < l; i += 1) {
12668                 v = o[i];
12669                 switch (typeof v) {
12670                     case "undefined":
12671                     case "function":
12672                     case "unknown":
12673                         break;
12674                     default:
12675                         if (b) {
12676                             a.push(',');
12677                         }
12678                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12679                         b = true;
12680                 }
12681             }
12682             a.push("]");
12683             return a.join("");
12684     };
12685     
12686     var encodeDate = function(o){
12687         return '"' + o.getFullYear() + "-" +
12688                 pad(o.getMonth() + 1) + "-" +
12689                 pad(o.getDate()) + "T" +
12690                 pad(o.getHours()) + ":" +
12691                 pad(o.getMinutes()) + ":" +
12692                 pad(o.getSeconds()) + '"';
12693     };
12694     
12695     /**
12696      * Encodes an Object, Array or other value
12697      * @param {Mixed} o The variable to encode
12698      * @return {String} The JSON string
12699      */
12700     this.encode = function(o){
12701         if(typeof o == "undefined" || o === null){
12702             return "null";
12703         }else if(o instanceof Array){
12704             return encodeArray(o);
12705         }else if(o instanceof Date){
12706             return encodeDate(o);
12707         }else if(typeof o == "string"){
12708             return encodeString(o);
12709         }else if(typeof o == "number"){
12710             return isFinite(o) ? String(o) : "null";
12711         }else if(typeof o == "boolean"){
12712             return String(o);
12713         }else {
12714             var a = ["{"], b, i, v;
12715             for (i in o) {
12716                 if(!useHasOwn || o.hasOwnProperty(i)) {
12717                     v = o[i];
12718                     switch (typeof v) {
12719                     case "undefined":
12720                     case "function":
12721                     case "unknown":
12722                         break;
12723                     default:
12724                         if(b){
12725                             a.push(',');
12726                         }
12727                         a.push(this.encode(i), ":",
12728                                 v === null ? "null" : this.encode(v));
12729                         b = true;
12730                     }
12731                 }
12732             }
12733             a.push("}");
12734             return a.join("");
12735         }
12736     };
12737     
12738     /**
12739      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12740      * @param {String} json The JSON string
12741      * @return {Object} The resulting object
12742      */
12743     this.decode = function(json){
12744         /**
12745          * eval:var:json
12746          */
12747         return eval("(" + json + ')');
12748     };
12749 })();
12750 /** 
12751  * Shorthand for {@link Roo.util.JSON#encode}
12752  * @member Roo encode 
12753  * @method */
12754 Roo.encode = Roo.util.JSON.encode;
12755 /** 
12756  * Shorthand for {@link Roo.util.JSON#decode}
12757  * @member Roo decode 
12758  * @method */
12759 Roo.decode = Roo.util.JSON.decode;
12760 /*
12761  * Based on:
12762  * Ext JS Library 1.1.1
12763  * Copyright(c) 2006-2007, Ext JS, LLC.
12764  *
12765  * Originally Released Under LGPL - original licence link has changed is not relivant.
12766  *
12767  * Fork - LGPL
12768  * <script type="text/javascript">
12769  */
12770  
12771 /**
12772  * @class Roo.util.Format
12773  * Reusable data formatting functions
12774  * @singleton
12775  */
12776 Roo.util.Format = function(){
12777     var trimRe = /^\s+|\s+$/g;
12778     return {
12779         /**
12780          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12781          * @param {String} value The string to truncate
12782          * @param {Number} length The maximum length to allow before truncating
12783          * @return {String} The converted text
12784          */
12785         ellipsis : function(value, len){
12786             if(value && value.length > len){
12787                 return value.substr(0, len-3)+"...";
12788             }
12789             return value;
12790         },
12791
12792         /**
12793          * Checks a reference and converts it to empty string if it is undefined
12794          * @param {Mixed} value Reference to check
12795          * @return {Mixed} Empty string if converted, otherwise the original value
12796          */
12797         undef : function(value){
12798             return typeof value != "undefined" ? value : "";
12799         },
12800
12801         /**
12802          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12803          * @param {String} value The string to encode
12804          * @return {String} The encoded text
12805          */
12806         htmlEncode : function(value){
12807             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12808         },
12809
12810         /**
12811          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12812          * @param {String} value The string to decode
12813          * @return {String} The decoded text
12814          */
12815         htmlDecode : function(value){
12816             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12817         },
12818
12819         /**
12820          * Trims any whitespace from either side of a string
12821          * @param {String} value The text to trim
12822          * @return {String} The trimmed text
12823          */
12824         trim : function(value){
12825             return String(value).replace(trimRe, "");
12826         },
12827
12828         /**
12829          * Returns a substring from within an original string
12830          * @param {String} value The original text
12831          * @param {Number} start The start index of the substring
12832          * @param {Number} length The length of the substring
12833          * @return {String} The substring
12834          */
12835         substr : function(value, start, length){
12836             return String(value).substr(start, length);
12837         },
12838
12839         /**
12840          * Converts a string to all lower case letters
12841          * @param {String} value The text to convert
12842          * @return {String} The converted text
12843          */
12844         lowercase : function(value){
12845             return String(value).toLowerCase();
12846         },
12847
12848         /**
12849          * Converts a string to all upper case letters
12850          * @param {String} value The text to convert
12851          * @return {String} The converted text
12852          */
12853         uppercase : function(value){
12854             return String(value).toUpperCase();
12855         },
12856
12857         /**
12858          * Converts the first character only of a string to upper case
12859          * @param {String} value The text to convert
12860          * @return {String} The converted text
12861          */
12862         capitalize : function(value){
12863             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12864         },
12865
12866         // private
12867         call : function(value, fn){
12868             if(arguments.length > 2){
12869                 var args = Array.prototype.slice.call(arguments, 2);
12870                 args.unshift(value);
12871                  
12872                 return /** eval:var:value */  eval(fn).apply(window, args);
12873             }else{
12874                 /** eval:var:value */
12875                 return /** eval:var:value */ eval(fn).call(window, value);
12876             }
12877         },
12878
12879         /**
12880          * Format a number as US currency
12881          * @param {Number/String} value The numeric value to format
12882          * @return {String} The formatted currency string
12883          */
12884         usMoney : function(v){
12885             v = (Math.round((v-0)*100))/100;
12886             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12887             v = String(v);
12888             var ps = v.split('.');
12889             var whole = ps[0];
12890             var sub = ps[1] ? '.'+ ps[1] : '.00';
12891             var r = /(\d+)(\d{3})/;
12892             while (r.test(whole)) {
12893                 whole = whole.replace(r, '$1' + ',' + '$2');
12894             }
12895             return "$" + whole + sub ;
12896         },
12897
12898         /**
12899          * Parse a value into a formatted date using the specified format pattern.
12900          * @param {Mixed} value The value to format
12901          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12902          * @return {String} The formatted date string
12903          */
12904         date : function(v, format){
12905             if(!v){
12906                 return "";
12907             }
12908             if(!(v instanceof Date)){
12909                 v = new Date(Date.parse(v));
12910             }
12911             return v.dateFormat(format || "m/d/Y");
12912         },
12913
12914         /**
12915          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12916          * @param {String} format Any valid date format string
12917          * @return {Function} The date formatting function
12918          */
12919         dateRenderer : function(format){
12920             return function(v){
12921                 return Roo.util.Format.date(v, format);  
12922             };
12923         },
12924
12925         // private
12926         stripTagsRE : /<\/?[^>]+>/gi,
12927         
12928         /**
12929          * Strips all HTML tags
12930          * @param {Mixed} value The text from which to strip tags
12931          * @return {String} The stripped text
12932          */
12933         stripTags : function(v){
12934             return !v ? v : String(v).replace(this.stripTagsRE, "");
12935         }
12936     };
12937 }();/*
12938  * Based on:
12939  * Ext JS Library 1.1.1
12940  * Copyright(c) 2006-2007, Ext JS, LLC.
12941  *
12942  * Originally Released Under LGPL - original licence link has changed is not relivant.
12943  *
12944  * Fork - LGPL
12945  * <script type="text/javascript">
12946  */
12947
12948
12949  
12950
12951 /**
12952  * @class Roo.MasterTemplate
12953  * @extends Roo.Template
12954  * Provides a template that can have child templates. The syntax is:
12955 <pre><code>
12956 var t = new Roo.MasterTemplate(
12957         '&lt;select name="{name}"&gt;',
12958                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12959         '&lt;/select&gt;'
12960 );
12961 t.add('options', {value: 'foo', text: 'bar'});
12962 // or you can add multiple child elements in one shot
12963 t.addAll('options', [
12964     {value: 'foo', text: 'bar'},
12965     {value: 'foo2', text: 'bar2'},
12966     {value: 'foo3', text: 'bar3'}
12967 ]);
12968 // then append, applying the master template values
12969 t.append('my-form', {name: 'my-select'});
12970 </code></pre>
12971 * A name attribute for the child template is not required if you have only one child
12972 * template or you want to refer to them by index.
12973  */
12974 Roo.MasterTemplate = function(){
12975     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12976     this.originalHtml = this.html;
12977     var st = {};
12978     var m, re = this.subTemplateRe;
12979     re.lastIndex = 0;
12980     var subIndex = 0;
12981     while(m = re.exec(this.html)){
12982         var name = m[1], content = m[2];
12983         st[subIndex] = {
12984             name: name,
12985             index: subIndex,
12986             buffer: [],
12987             tpl : new Roo.Template(content)
12988         };
12989         if(name){
12990             st[name] = st[subIndex];
12991         }
12992         st[subIndex].tpl.compile();
12993         st[subIndex].tpl.call = this.call.createDelegate(this);
12994         subIndex++;
12995     }
12996     this.subCount = subIndex;
12997     this.subs = st;
12998 };
12999 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13000     /**
13001     * The regular expression used to match sub templates
13002     * @type RegExp
13003     * @property
13004     */
13005     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13006
13007     /**
13008      * Applies the passed values to a child template.
13009      * @param {String/Number} name (optional) The name or index of the child template
13010      * @param {Array/Object} values The values to be applied to the template
13011      * @return {MasterTemplate} this
13012      */
13013      add : function(name, values){
13014         if(arguments.length == 1){
13015             values = arguments[0];
13016             name = 0;
13017         }
13018         var s = this.subs[name];
13019         s.buffer[s.buffer.length] = s.tpl.apply(values);
13020         return this;
13021     },
13022
13023     /**
13024      * Applies all the passed values to a child template.
13025      * @param {String/Number} name (optional) The name or index of the child template
13026      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13027      * @param {Boolean} reset (optional) True to reset the template first
13028      * @return {MasterTemplate} this
13029      */
13030     fill : function(name, values, reset){
13031         var a = arguments;
13032         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13033             values = a[0];
13034             name = 0;
13035             reset = a[1];
13036         }
13037         if(reset){
13038             this.reset();
13039         }
13040         for(var i = 0, len = values.length; i < len; i++){
13041             this.add(name, values[i]);
13042         }
13043         return this;
13044     },
13045
13046     /**
13047      * Resets the template for reuse
13048      * @return {MasterTemplate} this
13049      */
13050      reset : function(){
13051         var s = this.subs;
13052         for(var i = 0; i < this.subCount; i++){
13053             s[i].buffer = [];
13054         }
13055         return this;
13056     },
13057
13058     applyTemplate : function(values){
13059         var s = this.subs;
13060         var replaceIndex = -1;
13061         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13062             return s[++replaceIndex].buffer.join("");
13063         });
13064         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13065     },
13066
13067     apply : function(){
13068         return this.applyTemplate.apply(this, arguments);
13069     },
13070
13071     compile : function(){return this;}
13072 });
13073
13074 /**
13075  * Alias for fill().
13076  * @method
13077  */
13078 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13079  /**
13080  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13081  * var tpl = Roo.MasterTemplate.from('element-id');
13082  * @param {String/HTMLElement} el
13083  * @param {Object} config
13084  * @static
13085  */
13086 Roo.MasterTemplate.from = function(el, config){
13087     el = Roo.getDom(el);
13088     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13089 };/*
13090  * Based on:
13091  * Ext JS Library 1.1.1
13092  * Copyright(c) 2006-2007, Ext JS, LLC.
13093  *
13094  * Originally Released Under LGPL - original licence link has changed is not relivant.
13095  *
13096  * Fork - LGPL
13097  * <script type="text/javascript">
13098  */
13099
13100  
13101 /**
13102  * @class Roo.util.CSS
13103  * Utility class for manipulating CSS rules
13104  * @singleton
13105  */
13106 Roo.util.CSS = function(){
13107         var rules = null;
13108         var doc = document;
13109
13110     var camelRe = /(-[a-z])/gi;
13111     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13112
13113    return {
13114    /**
13115     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13116     * tag and appended to the HEAD of the document.
13117     * @param {String} cssText The text containing the css rules
13118     * @param {String} id An id to add to the stylesheet for later removal
13119     * @return {StyleSheet}
13120     */
13121    createStyleSheet : function(cssText, id){
13122        var ss;
13123        var head = doc.getElementsByTagName("head")[0];
13124        var rules = doc.createElement("style");
13125        rules.setAttribute("type", "text/css");
13126        if(id){
13127            rules.setAttribute("id", id);
13128        }
13129        if(Roo.isIE){
13130            head.appendChild(rules);
13131            ss = rules.styleSheet;
13132            ss.cssText = cssText;
13133        }else{
13134            try{
13135                 rules.appendChild(doc.createTextNode(cssText));
13136            }catch(e){
13137                rules.cssText = cssText; 
13138            }
13139            head.appendChild(rules);
13140            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13141        }
13142        this.cacheStyleSheet(ss);
13143        return ss;
13144    },
13145
13146    /**
13147     * Removes a style or link tag by id
13148     * @param {String} id The id of the tag
13149     */
13150    removeStyleSheet : function(id){
13151        var existing = doc.getElementById(id);
13152        if(existing){
13153            existing.parentNode.removeChild(existing);
13154        }
13155    },
13156
13157    /**
13158     * Dynamically swaps an existing stylesheet reference for a new one
13159     * @param {String} id The id of an existing link tag to remove
13160     * @param {String} url The href of the new stylesheet to include
13161     */
13162    swapStyleSheet : function(id, url){
13163        this.removeStyleSheet(id);
13164        var ss = doc.createElement("link");
13165        ss.setAttribute("rel", "stylesheet");
13166        ss.setAttribute("type", "text/css");
13167        ss.setAttribute("id", id);
13168        ss.setAttribute("href", url);
13169        doc.getElementsByTagName("head")[0].appendChild(ss);
13170    },
13171    
13172    /**
13173     * Refresh the rule cache if you have dynamically added stylesheets
13174     * @return {Object} An object (hash) of rules indexed by selector
13175     */
13176    refreshCache : function(){
13177        return this.getRules(true);
13178    },
13179
13180    // private
13181    cacheStyleSheet : function(ss){
13182        if(!rules){
13183            rules = {};
13184        }
13185        try{// try catch for cross domain access issue
13186            var ssRules = ss.cssRules || ss.rules;
13187            for(var j = ssRules.length-1; j >= 0; --j){
13188                rules[ssRules[j].selectorText] = ssRules[j];
13189            }
13190        }catch(e){}
13191    },
13192    
13193    /**
13194     * Gets all css rules for the document
13195     * @param {Boolean} refreshCache true to refresh the internal cache
13196     * @return {Object} An object (hash) of rules indexed by selector
13197     */
13198    getRules : function(refreshCache){
13199                 if(rules == null || refreshCache){
13200                         rules = {};
13201                         var ds = doc.styleSheets;
13202                         for(var i =0, len = ds.length; i < len; i++){
13203                             try{
13204                         this.cacheStyleSheet(ds[i]);
13205                     }catch(e){} 
13206                 }
13207                 }
13208                 return rules;
13209         },
13210         
13211         /**
13212     * Gets an an individual CSS rule by selector(s)
13213     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13214     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13215     * @return {CSSRule} The CSS rule or null if one is not found
13216     */
13217    getRule : function(selector, refreshCache){
13218                 var rs = this.getRules(refreshCache);
13219                 if(!(selector instanceof Array)){
13220                     return rs[selector];
13221                 }
13222                 for(var i = 0; i < selector.length; i++){
13223                         if(rs[selector[i]]){
13224                                 return rs[selector[i]];
13225                         }
13226                 }
13227                 return null;
13228         },
13229         
13230         
13231         /**
13232     * Updates a rule property
13233     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13234     * @param {String} property The css property
13235     * @param {String} value The new value for the property
13236     * @return {Boolean} true If a rule was found and updated
13237     */
13238    updateRule : function(selector, property, value){
13239                 if(!(selector instanceof Array)){
13240                         var rule = this.getRule(selector);
13241                         if(rule){
13242                                 rule.style[property.replace(camelRe, camelFn)] = value;
13243                                 return true;
13244                         }
13245                 }else{
13246                         for(var i = 0; i < selector.length; i++){
13247                                 if(this.updateRule(selector[i], property, value)){
13248                                         return true;
13249                                 }
13250                         }
13251                 }
13252                 return false;
13253         }
13254    };   
13255 }();/*
13256  * Based on:
13257  * Ext JS Library 1.1.1
13258  * Copyright(c) 2006-2007, Ext JS, LLC.
13259  *
13260  * Originally Released Under LGPL - original licence link has changed is not relivant.
13261  *
13262  * Fork - LGPL
13263  * <script type="text/javascript">
13264  */
13265
13266  
13267
13268 /**
13269  * @class Roo.util.ClickRepeater
13270  * @extends Roo.util.Observable
13271  * 
13272  * A wrapper class which can be applied to any element. Fires a "click" event while the
13273  * mouse is pressed. The interval between firings may be specified in the config but
13274  * defaults to 10 milliseconds.
13275  * 
13276  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13277  * 
13278  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13279  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13280  * Similar to an autorepeat key delay.
13281  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13282  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13283  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13284  *           "interval" and "delay" are ignored. "immediate" is honored.
13285  * @cfg {Boolean} preventDefault True to prevent the default click event
13286  * @cfg {Boolean} stopDefault True to stop the default click event
13287  * 
13288  * @history
13289  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13290  *     2007-02-02 jvs Renamed to ClickRepeater
13291  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13292  *
13293  *  @constructor
13294  * @param {String/HTMLElement/Element} el The element to listen on
13295  * @param {Object} config
13296  **/
13297 Roo.util.ClickRepeater = function(el, config)
13298 {
13299     this.el = Roo.get(el);
13300     this.el.unselectable();
13301
13302     Roo.apply(this, config);
13303
13304     this.addEvents({
13305     /**
13306      * @event mousedown
13307      * Fires when the mouse button is depressed.
13308      * @param {Roo.util.ClickRepeater} this
13309      */
13310         "mousedown" : true,
13311     /**
13312      * @event click
13313      * Fires on a specified interval during the time the element is pressed.
13314      * @param {Roo.util.ClickRepeater} this
13315      */
13316         "click" : true,
13317     /**
13318      * @event mouseup
13319      * Fires when the mouse key is released.
13320      * @param {Roo.util.ClickRepeater} this
13321      */
13322         "mouseup" : true
13323     });
13324
13325     this.el.on("mousedown", this.handleMouseDown, this);
13326     if(this.preventDefault || this.stopDefault){
13327         this.el.on("click", function(e){
13328             if(this.preventDefault){
13329                 e.preventDefault();
13330             }
13331             if(this.stopDefault){
13332                 e.stopEvent();
13333             }
13334         }, this);
13335     }
13336
13337     // allow inline handler
13338     if(this.handler){
13339         this.on("click", this.handler,  this.scope || this);
13340     }
13341
13342     Roo.util.ClickRepeater.superclass.constructor.call(this);
13343 };
13344
13345 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13346     interval : 20,
13347     delay: 250,
13348     preventDefault : true,
13349     stopDefault : false,
13350     timer : 0,
13351
13352     // private
13353     handleMouseDown : function(){
13354         clearTimeout(this.timer);
13355         this.el.blur();
13356         if(this.pressClass){
13357             this.el.addClass(this.pressClass);
13358         }
13359         this.mousedownTime = new Date();
13360
13361         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13362         this.el.on("mouseout", this.handleMouseOut, this);
13363
13364         this.fireEvent("mousedown", this);
13365         this.fireEvent("click", this);
13366         
13367         this.timer = this.click.defer(this.delay || this.interval, this);
13368     },
13369
13370     // private
13371     click : function(){
13372         this.fireEvent("click", this);
13373         this.timer = this.click.defer(this.getInterval(), this);
13374     },
13375
13376     // private
13377     getInterval: function(){
13378         if(!this.accelerate){
13379             return this.interval;
13380         }
13381         var pressTime = this.mousedownTime.getElapsed();
13382         if(pressTime < 500){
13383             return 400;
13384         }else if(pressTime < 1700){
13385             return 320;
13386         }else if(pressTime < 2600){
13387             return 250;
13388         }else if(pressTime < 3500){
13389             return 180;
13390         }else if(pressTime < 4400){
13391             return 140;
13392         }else if(pressTime < 5300){
13393             return 80;
13394         }else if(pressTime < 6200){
13395             return 50;
13396         }else{
13397             return 10;
13398         }
13399     },
13400
13401     // private
13402     handleMouseOut : function(){
13403         clearTimeout(this.timer);
13404         if(this.pressClass){
13405             this.el.removeClass(this.pressClass);
13406         }
13407         this.el.on("mouseover", this.handleMouseReturn, this);
13408     },
13409
13410     // private
13411     handleMouseReturn : function(){
13412         this.el.un("mouseover", this.handleMouseReturn);
13413         if(this.pressClass){
13414             this.el.addClass(this.pressClass);
13415         }
13416         this.click();
13417     },
13418
13419     // private
13420     handleMouseUp : function(){
13421         clearTimeout(this.timer);
13422         this.el.un("mouseover", this.handleMouseReturn);
13423         this.el.un("mouseout", this.handleMouseOut);
13424         Roo.get(document).un("mouseup", this.handleMouseUp);
13425         this.el.removeClass(this.pressClass);
13426         this.fireEvent("mouseup", this);
13427     }
13428 });/*
13429  * Based on:
13430  * Ext JS Library 1.1.1
13431  * Copyright(c) 2006-2007, Ext JS, LLC.
13432  *
13433  * Originally Released Under LGPL - original licence link has changed is not relivant.
13434  *
13435  * Fork - LGPL
13436  * <script type="text/javascript">
13437  */
13438
13439  
13440 /**
13441  * @class Roo.KeyNav
13442  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13443  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13444  * way to implement custom navigation schemes for any UI component.</p>
13445  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13446  * pageUp, pageDown, del, home, end.  Usage:</p>
13447  <pre><code>
13448 var nav = new Roo.KeyNav("my-element", {
13449     "left" : function(e){
13450         this.moveLeft(e.ctrlKey);
13451     },
13452     "right" : function(e){
13453         this.moveRight(e.ctrlKey);
13454     },
13455     "enter" : function(e){
13456         this.save();
13457     },
13458     scope : this
13459 });
13460 </code></pre>
13461  * @constructor
13462  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13463  * @param {Object} config The config
13464  */
13465 Roo.KeyNav = function(el, config){
13466     this.el = Roo.get(el);
13467     Roo.apply(this, config);
13468     if(!this.disabled){
13469         this.disabled = true;
13470         this.enable();
13471     }
13472 };
13473
13474 Roo.KeyNav.prototype = {
13475     /**
13476      * @cfg {Boolean} disabled
13477      * True to disable this KeyNav instance (defaults to false)
13478      */
13479     disabled : false,
13480     /**
13481      * @cfg {String} defaultEventAction
13482      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13483      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13484      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13485      */
13486     defaultEventAction: "stopEvent",
13487     /**
13488      * @cfg {Boolean} forceKeyDown
13489      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13490      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13491      * handle keydown instead of keypress.
13492      */
13493     forceKeyDown : false,
13494
13495     // private
13496     prepareEvent : function(e){
13497         var k = e.getKey();
13498         var h = this.keyToHandler[k];
13499         //if(h && this[h]){
13500         //    e.stopPropagation();
13501         //}
13502         if(Roo.isSafari && h && k >= 37 && k <= 40){
13503             e.stopEvent();
13504         }
13505     },
13506
13507     // private
13508     relay : function(e){
13509         var k = e.getKey();
13510         var h = this.keyToHandler[k];
13511         if(h && this[h]){
13512             if(this.doRelay(e, this[h], h) !== true){
13513                 e[this.defaultEventAction]();
13514             }
13515         }
13516     },
13517
13518     // private
13519     doRelay : function(e, h, hname){
13520         return h.call(this.scope || this, e);
13521     },
13522
13523     // possible handlers
13524     enter : false,
13525     left : false,
13526     right : false,
13527     up : false,
13528     down : false,
13529     tab : false,
13530     esc : false,
13531     pageUp : false,
13532     pageDown : false,
13533     del : false,
13534     home : false,
13535     end : false,
13536
13537     // quick lookup hash
13538     keyToHandler : {
13539         37 : "left",
13540         39 : "right",
13541         38 : "up",
13542         40 : "down",
13543         33 : "pageUp",
13544         34 : "pageDown",
13545         46 : "del",
13546         36 : "home",
13547         35 : "end",
13548         13 : "enter",
13549         27 : "esc",
13550         9  : "tab"
13551     },
13552
13553         /**
13554          * Enable this KeyNav
13555          */
13556         enable: function(){
13557                 if(this.disabled){
13558             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13559             // the EventObject will normalize Safari automatically
13560             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13561                 this.el.on("keydown", this.relay,  this);
13562             }else{
13563                 this.el.on("keydown", this.prepareEvent,  this);
13564                 this.el.on("keypress", this.relay,  this);
13565             }
13566                     this.disabled = false;
13567                 }
13568         },
13569
13570         /**
13571          * Disable this KeyNav
13572          */
13573         disable: function(){
13574                 if(!this.disabled){
13575                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13576                 this.el.un("keydown", this.relay);
13577             }else{
13578                 this.el.un("keydown", this.prepareEvent);
13579                 this.el.un("keypress", this.relay);
13580             }
13581                     this.disabled = true;
13582                 }
13583         }
13584 };/*
13585  * Based on:
13586  * Ext JS Library 1.1.1
13587  * Copyright(c) 2006-2007, Ext JS, LLC.
13588  *
13589  * Originally Released Under LGPL - original licence link has changed is not relivant.
13590  *
13591  * Fork - LGPL
13592  * <script type="text/javascript">
13593  */
13594
13595  
13596 /**
13597  * @class Roo.KeyMap
13598  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13599  * The constructor accepts the same config object as defined by {@link #addBinding}.
13600  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13601  * combination it will call the function with this signature (if the match is a multi-key
13602  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13603  * A KeyMap can also handle a string representation of keys.<br />
13604  * Usage:
13605  <pre><code>
13606 // map one key by key code
13607 var map = new Roo.KeyMap("my-element", {
13608     key: 13, // or Roo.EventObject.ENTER
13609     fn: myHandler,
13610     scope: myObject
13611 });
13612
13613 // map multiple keys to one action by string
13614 var map = new Roo.KeyMap("my-element", {
13615     key: "a\r\n\t",
13616     fn: myHandler,
13617     scope: myObject
13618 });
13619
13620 // map multiple keys to multiple actions by strings and array of codes
13621 var map = new Roo.KeyMap("my-element", [
13622     {
13623         key: [10,13],
13624         fn: function(){ alert("Return was pressed"); }
13625     }, {
13626         key: "abc",
13627         fn: function(){ alert('a, b or c was pressed'); }
13628     }, {
13629         key: "\t",
13630         ctrl:true,
13631         shift:true,
13632         fn: function(){ alert('Control + shift + tab was pressed.'); }
13633     }
13634 ]);
13635 </code></pre>
13636  * <b>Note: A KeyMap starts enabled</b>
13637  * @constructor
13638  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13639  * @param {Object} config The config (see {@link #addBinding})
13640  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13641  */
13642 Roo.KeyMap = function(el, config, eventName){
13643     this.el  = Roo.get(el);
13644     this.eventName = eventName || "keydown";
13645     this.bindings = [];
13646     if(config){
13647         this.addBinding(config);
13648     }
13649     this.enable();
13650 };
13651
13652 Roo.KeyMap.prototype = {
13653     /**
13654      * True to stop the event from bubbling and prevent the default browser action if the
13655      * key was handled by the KeyMap (defaults to false)
13656      * @type Boolean
13657      */
13658     stopEvent : false,
13659
13660     /**
13661      * Add a new binding to this KeyMap. The following config object properties are supported:
13662      * <pre>
13663 Property    Type             Description
13664 ----------  ---------------  ----------------------------------------------------------------------
13665 key         String/Array     A single keycode or an array of keycodes to handle
13666 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13667 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13668 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13669 fn          Function         The function to call when KeyMap finds the expected key combination
13670 scope       Object           The scope of the callback function
13671 </pre>
13672      *
13673      * Usage:
13674      * <pre><code>
13675 // Create a KeyMap
13676 var map = new Roo.KeyMap(document, {
13677     key: Roo.EventObject.ENTER,
13678     fn: handleKey,
13679     scope: this
13680 });
13681
13682 //Add a new binding to the existing KeyMap later
13683 map.addBinding({
13684     key: 'abc',
13685     shift: true,
13686     fn: handleKey,
13687     scope: this
13688 });
13689 </code></pre>
13690      * @param {Object/Array} config A single KeyMap config or an array of configs
13691      */
13692         addBinding : function(config){
13693         if(config instanceof Array){
13694             for(var i = 0, len = config.length; i < len; i++){
13695                 this.addBinding(config[i]);
13696             }
13697             return;
13698         }
13699         var keyCode = config.key,
13700             shift = config.shift, 
13701             ctrl = config.ctrl, 
13702             alt = config.alt,
13703             fn = config.fn,
13704             scope = config.scope;
13705         if(typeof keyCode == "string"){
13706             var ks = [];
13707             var keyString = keyCode.toUpperCase();
13708             for(var j = 0, len = keyString.length; j < len; j++){
13709                 ks.push(keyString.charCodeAt(j));
13710             }
13711             keyCode = ks;
13712         }
13713         var keyArray = keyCode instanceof Array;
13714         var handler = function(e){
13715             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13716                 var k = e.getKey();
13717                 if(keyArray){
13718                     for(var i = 0, len = keyCode.length; i < len; i++){
13719                         if(keyCode[i] == k){
13720                           if(this.stopEvent){
13721                               e.stopEvent();
13722                           }
13723                           fn.call(scope || window, k, e);
13724                           return;
13725                         }
13726                     }
13727                 }else{
13728                     if(k == keyCode){
13729                         if(this.stopEvent){
13730                            e.stopEvent();
13731                         }
13732                         fn.call(scope || window, k, e);
13733                     }
13734                 }
13735             }
13736         };
13737         this.bindings.push(handler);  
13738         },
13739
13740     /**
13741      * Shorthand for adding a single key listener
13742      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13743      * following options:
13744      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13745      * @param {Function} fn The function to call
13746      * @param {Object} scope (optional) The scope of the function
13747      */
13748     on : function(key, fn, scope){
13749         var keyCode, shift, ctrl, alt;
13750         if(typeof key == "object" && !(key instanceof Array)){
13751             keyCode = key.key;
13752             shift = key.shift;
13753             ctrl = key.ctrl;
13754             alt = key.alt;
13755         }else{
13756             keyCode = key;
13757         }
13758         this.addBinding({
13759             key: keyCode,
13760             shift: shift,
13761             ctrl: ctrl,
13762             alt: alt,
13763             fn: fn,
13764             scope: scope
13765         })
13766     },
13767
13768     // private
13769     handleKeyDown : function(e){
13770             if(this.enabled){ //just in case
13771             var b = this.bindings;
13772             for(var i = 0, len = b.length; i < len; i++){
13773                 b[i].call(this, e);
13774             }
13775             }
13776         },
13777         
13778         /**
13779          * Returns true if this KeyMap is enabled
13780          * @return {Boolean} 
13781          */
13782         isEnabled : function(){
13783             return this.enabled;  
13784         },
13785         
13786         /**
13787          * Enables this KeyMap
13788          */
13789         enable: function(){
13790                 if(!this.enabled){
13791                     this.el.on(this.eventName, this.handleKeyDown, this);
13792                     this.enabled = true;
13793                 }
13794         },
13795
13796         /**
13797          * Disable this KeyMap
13798          */
13799         disable: function(){
13800                 if(this.enabled){
13801                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13802                     this.enabled = false;
13803                 }
13804         }
13805 };/*
13806  * Based on:
13807  * Ext JS Library 1.1.1
13808  * Copyright(c) 2006-2007, Ext JS, LLC.
13809  *
13810  * Originally Released Under LGPL - original licence link has changed is not relivant.
13811  *
13812  * Fork - LGPL
13813  * <script type="text/javascript">
13814  */
13815
13816  
13817 /**
13818  * @class Roo.util.TextMetrics
13819  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13820  * wide, in pixels, a given block of text will be.
13821  * @singleton
13822  */
13823 Roo.util.TextMetrics = function(){
13824     var shared;
13825     return {
13826         /**
13827          * Measures the size of the specified text
13828          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13829          * that can affect the size of the rendered text
13830          * @param {String} text The text to measure
13831          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13832          * in order to accurately measure the text height
13833          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13834          */
13835         measure : function(el, text, fixedWidth){
13836             if(!shared){
13837                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13838             }
13839             shared.bind(el);
13840             shared.setFixedWidth(fixedWidth || 'auto');
13841             return shared.getSize(text);
13842         },
13843
13844         /**
13845          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13846          * the overhead of multiple calls to initialize the style properties on each measurement.
13847          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13848          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13849          * in order to accurately measure the text height
13850          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13851          */
13852         createInstance : function(el, fixedWidth){
13853             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13854         }
13855     };
13856 }();
13857
13858 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13859     var ml = new Roo.Element(document.createElement('div'));
13860     document.body.appendChild(ml.dom);
13861     ml.position('absolute');
13862     ml.setLeftTop(-1000, -1000);
13863     ml.hide();
13864
13865     if(fixedWidth){
13866         ml.setWidth(fixedWidth);
13867     }
13868
13869     var instance = {
13870         /**
13871          * Returns the size of the specified text based on the internal element's style and width properties
13872          * @param {String} text The text to measure
13873          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13874          */
13875         getSize : function(text){
13876             ml.update(text);
13877             var s = ml.getSize();
13878             ml.update('');
13879             return s;
13880         },
13881
13882         /**
13883          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13884          * that can affect the size of the rendered text
13885          * @param {String/HTMLElement} el The element, dom node or id
13886          */
13887         bind : function(el){
13888             ml.setStyle(
13889                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13890             );
13891         },
13892
13893         /**
13894          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13895          * to set a fixed width in order to accurately measure the text height.
13896          * @param {Number} width The width to set on the element
13897          */
13898         setFixedWidth : function(width){
13899             ml.setWidth(width);
13900         },
13901
13902         /**
13903          * Returns the measured width of the specified text
13904          * @param {String} text The text to measure
13905          * @return {Number} width The width in pixels
13906          */
13907         getWidth : function(text){
13908             ml.dom.style.width = 'auto';
13909             return this.getSize(text).width;
13910         },
13911
13912         /**
13913          * Returns the measured height of the specified text.  For multiline text, be sure to call
13914          * {@link #setFixedWidth} if necessary.
13915          * @param {String} text The text to measure
13916          * @return {Number} height The height in pixels
13917          */
13918         getHeight : function(text){
13919             return this.getSize(text).height;
13920         }
13921     };
13922
13923     instance.bind(bindTo);
13924
13925     return instance;
13926 };
13927
13928 // backwards compat
13929 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13930  * Based on:
13931  * Ext JS Library 1.1.1
13932  * Copyright(c) 2006-2007, Ext JS, LLC.
13933  *
13934  * Originally Released Under LGPL - original licence link has changed is not relivant.
13935  *
13936  * Fork - LGPL
13937  * <script type="text/javascript">
13938  */
13939
13940 /**
13941  * @class Roo.state.Provider
13942  * Abstract base class for state provider implementations. This class provides methods
13943  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13944  * Provider interface.
13945  */
13946 Roo.state.Provider = function(){
13947     /**
13948      * @event statechange
13949      * Fires when a state change occurs.
13950      * @param {Provider} this This state provider
13951      * @param {String} key The state key which was changed
13952      * @param {String} value The encoded value for the state
13953      */
13954     this.addEvents({
13955         "statechange": true
13956     });
13957     this.state = {};
13958     Roo.state.Provider.superclass.constructor.call(this);
13959 };
13960 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13961     /**
13962      * Returns the current value for a key
13963      * @param {String} name The key name
13964      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13965      * @return {Mixed} The state data
13966      */
13967     get : function(name, defaultValue){
13968         return typeof this.state[name] == "undefined" ?
13969             defaultValue : this.state[name];
13970     },
13971     
13972     /**
13973      * Clears a value from the state
13974      * @param {String} name The key name
13975      */
13976     clear : function(name){
13977         delete this.state[name];
13978         this.fireEvent("statechange", this, name, null);
13979     },
13980     
13981     /**
13982      * Sets the value for a key
13983      * @param {String} name The key name
13984      * @param {Mixed} value The value to set
13985      */
13986     set : function(name, value){
13987         this.state[name] = value;
13988         this.fireEvent("statechange", this, name, value);
13989     },
13990     
13991     /**
13992      * Decodes a string previously encoded with {@link #encodeValue}.
13993      * @param {String} value The value to decode
13994      * @return {Mixed} The decoded value
13995      */
13996     decodeValue : function(cookie){
13997         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13998         var matches = re.exec(unescape(cookie));
13999         if(!matches || !matches[1]) return; // non state cookie
14000         var type = matches[1];
14001         var v = matches[2];
14002         switch(type){
14003             case "n":
14004                 return parseFloat(v);
14005             case "d":
14006                 return new Date(Date.parse(v));
14007             case "b":
14008                 return (v == "1");
14009             case "a":
14010                 var all = [];
14011                 var values = v.split("^");
14012                 for(var i = 0, len = values.length; i < len; i++){
14013                     all.push(this.decodeValue(values[i]));
14014                 }
14015                 return all;
14016            case "o":
14017                 var all = {};
14018                 var values = v.split("^");
14019                 for(var i = 0, len = values.length; i < len; i++){
14020                     var kv = values[i].split("=");
14021                     all[kv[0]] = this.decodeValue(kv[1]);
14022                 }
14023                 return all;
14024            default:
14025                 return v;
14026         }
14027     },
14028     
14029     /**
14030      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14031      * @param {Mixed} value The value to encode
14032      * @return {String} The encoded value
14033      */
14034     encodeValue : function(v){
14035         var enc;
14036         if(typeof v == "number"){
14037             enc = "n:" + v;
14038         }else if(typeof v == "boolean"){
14039             enc = "b:" + (v ? "1" : "0");
14040         }else if(v instanceof Date){
14041             enc = "d:" + v.toGMTString();
14042         }else if(v instanceof Array){
14043             var flat = "";
14044             for(var i = 0, len = v.length; i < len; i++){
14045                 flat += this.encodeValue(v[i]);
14046                 if(i != len-1) flat += "^";
14047             }
14048             enc = "a:" + flat;
14049         }else if(typeof v == "object"){
14050             var flat = "";
14051             for(var key in v){
14052                 if(typeof v[key] != "function"){
14053                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14054                 }
14055             }
14056             enc = "o:" + flat.substring(0, flat.length-1);
14057         }else{
14058             enc = "s:" + v;
14059         }
14060         return escape(enc);        
14061     }
14062 });
14063
14064 /*
14065  * Based on:
14066  * Ext JS Library 1.1.1
14067  * Copyright(c) 2006-2007, Ext JS, LLC.
14068  *
14069  * Originally Released Under LGPL - original licence link has changed is not relivant.
14070  *
14071  * Fork - LGPL
14072  * <script type="text/javascript">
14073  */
14074 /**
14075  * @class Roo.state.Manager
14076  * This is the global state manager. By default all components that are "state aware" check this class
14077  * for state information if you don't pass them a custom state provider. In order for this class
14078  * to be useful, it must be initialized with a provider when your application initializes.
14079  <pre><code>
14080 // in your initialization function
14081 init : function(){
14082    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14083    ...
14084    // supposed you have a {@link Roo.BorderLayout}
14085    var layout = new Roo.BorderLayout(...);
14086    layout.restoreState();
14087    // or a {Roo.BasicDialog}
14088    var dialog = new Roo.BasicDialog(...);
14089    dialog.restoreState();
14090  </code></pre>
14091  * @singleton
14092  */
14093 Roo.state.Manager = function(){
14094     var provider = new Roo.state.Provider();
14095     
14096     return {
14097         /**
14098          * Configures the default state provider for your application
14099          * @param {Provider} stateProvider The state provider to set
14100          */
14101         setProvider : function(stateProvider){
14102             provider = stateProvider;
14103         },
14104         
14105         /**
14106          * Returns the current value for a key
14107          * @param {String} name The key name
14108          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14109          * @return {Mixed} The state data
14110          */
14111         get : function(key, defaultValue){
14112             return provider.get(key, defaultValue);
14113         },
14114         
14115         /**
14116          * Sets the value for a key
14117          * @param {String} name The key name
14118          * @param {Mixed} value The state data
14119          */
14120          set : function(key, value){
14121             provider.set(key, value);
14122         },
14123         
14124         /**
14125          * Clears a value from the state
14126          * @param {String} name The key name
14127          */
14128         clear : function(key){
14129             provider.clear(key);
14130         },
14131         
14132         /**
14133          * Gets the currently configured state provider
14134          * @return {Provider} The state provider
14135          */
14136         getProvider : function(){
14137             return provider;
14138         }
14139     };
14140 }();
14141 /*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151 /**
14152  * @class Roo.state.CookieProvider
14153  * @extends Roo.state.Provider
14154  * The default Provider implementation which saves state via cookies.
14155  * <br />Usage:
14156  <pre><code>
14157    var cp = new Roo.state.CookieProvider({
14158        path: "/cgi-bin/",
14159        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14160        domain: "roojs.com"
14161    })
14162    Roo.state.Manager.setProvider(cp);
14163  </code></pre>
14164  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14165  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14166  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14167  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14168  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14169  * domain the page is running on including the 'www' like 'www.roojs.com')
14170  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14171  * @constructor
14172  * Create a new CookieProvider
14173  * @param {Object} config The configuration object
14174  */
14175 Roo.state.CookieProvider = function(config){
14176     Roo.state.CookieProvider.superclass.constructor.call(this);
14177     this.path = "/";
14178     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14179     this.domain = null;
14180     this.secure = false;
14181     Roo.apply(this, config);
14182     this.state = this.readCookies();
14183 };
14184
14185 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14186     // private
14187     set : function(name, value){
14188         if(typeof value == "undefined" || value === null){
14189             this.clear(name);
14190             return;
14191         }
14192         this.setCookie(name, value);
14193         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14194     },
14195
14196     // private
14197     clear : function(name){
14198         this.clearCookie(name);
14199         Roo.state.CookieProvider.superclass.clear.call(this, name);
14200     },
14201
14202     // private
14203     readCookies : function(){
14204         var cookies = {};
14205         var c = document.cookie + ";";
14206         var re = /\s?(.*?)=(.*?);/g;
14207         var matches;
14208         while((matches = re.exec(c)) != null){
14209             var name = matches[1];
14210             var value = matches[2];
14211             if(name && name.substring(0,3) == "ys-"){
14212                 cookies[name.substr(3)] = this.decodeValue(value);
14213             }
14214         }
14215         return cookies;
14216     },
14217
14218     // private
14219     setCookie : function(name, value){
14220         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14221            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14222            ((this.path == null) ? "" : ("; path=" + this.path)) +
14223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14224            ((this.secure == true) ? "; secure" : "");
14225     },
14226
14227     // private
14228     clearCookie : function(name){
14229         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14230            ((this.path == null) ? "" : ("; path=" + this.path)) +
14231            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14232            ((this.secure == true) ? "; secure" : "");
14233     }
14234 });/*
14235  * Based on:
14236  * Ext JS Library 1.1.1
14237  * Copyright(c) 2006-2007, Ext JS, LLC.
14238  *
14239  * Originally Released Under LGPL - original licence link has changed is not relivant.
14240  *
14241  * Fork - LGPL
14242  * <script type="text/javascript">
14243  */
14244
14245
14246
14247 /*
14248  * These classes are derivatives of the similarly named classes in the YUI Library.
14249  * The original license:
14250  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14251  * Code licensed under the BSD License:
14252  * http://developer.yahoo.net/yui/license.txt
14253  */
14254
14255 (function() {
14256
14257 var Event=Roo.EventManager;
14258 var Dom=Roo.lib.Dom;
14259
14260 /**
14261  * @class Roo.dd.DragDrop
14262  * Defines the interface and base operation of items that that can be
14263  * dragged or can be drop targets.  It was designed to be extended, overriding
14264  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14265  * Up to three html elements can be associated with a DragDrop instance:
14266  * <ul>
14267  * <li>linked element: the element that is passed into the constructor.
14268  * This is the element which defines the boundaries for interaction with
14269  * other DragDrop objects.</li>
14270  * <li>handle element(s): The drag operation only occurs if the element that
14271  * was clicked matches a handle element.  By default this is the linked
14272  * element, but there are times that you will want only a portion of the
14273  * linked element to initiate the drag operation, and the setHandleElId()
14274  * method provides a way to define this.</li>
14275  * <li>drag element: this represents the element that would be moved along
14276  * with the cursor during a drag operation.  By default, this is the linked
14277  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14278  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14279  * </li>
14280  * </ul>
14281  * This class should not be instantiated until the onload event to ensure that
14282  * the associated elements are available.
14283  * The following would define a DragDrop obj that would interact with any
14284  * other DragDrop obj in the "group1" group:
14285  * <pre>
14286  *  dd = new Roo.dd.DragDrop("div1", "group1");
14287  * </pre>
14288  * Since none of the event handlers have been implemented, nothing would
14289  * actually happen if you were to run the code above.  Normally you would
14290  * override this class or one of the default implementations, but you can
14291  * also override the methods you want on an instance of the class...
14292  * <pre>
14293  *  dd.onDragDrop = function(e, id) {
14294  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14295  *  }
14296  * </pre>
14297  * @constructor
14298  * @param {String} id of the element that is linked to this instance
14299  * @param {String} sGroup the group of related DragDrop objects
14300  * @param {object} config an object containing configurable attributes
14301  *                Valid properties for DragDrop:
14302  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14303  */
14304 Roo.dd.DragDrop = function(id, sGroup, config) {
14305     if (id) {
14306         this.init(id, sGroup, config);
14307     }
14308 };
14309
14310 Roo.dd.DragDrop.prototype = {
14311
14312     /**
14313      * The id of the element associated with this object.  This is what we
14314      * refer to as the "linked element" because the size and position of
14315      * this element is used to determine when the drag and drop objects have
14316      * interacted.
14317      * @property id
14318      * @type String
14319      */
14320     id: null,
14321
14322     /**
14323      * Configuration attributes passed into the constructor
14324      * @property config
14325      * @type object
14326      */
14327     config: null,
14328
14329     /**
14330      * The id of the element that will be dragged.  By default this is same
14331      * as the linked element , but could be changed to another element. Ex:
14332      * Roo.dd.DDProxy
14333      * @property dragElId
14334      * @type String
14335      * @private
14336      */
14337     dragElId: null,
14338
14339     /**
14340      * the id of the element that initiates the drag operation.  By default
14341      * this is the linked element, but could be changed to be a child of this
14342      * element.  This lets us do things like only starting the drag when the
14343      * header element within the linked html element is clicked.
14344      * @property handleElId
14345      * @type String
14346      * @private
14347      */
14348     handleElId: null,
14349
14350     /**
14351      * An associative array of HTML tags that will be ignored if clicked.
14352      * @property invalidHandleTypes
14353      * @type {string: string}
14354      */
14355     invalidHandleTypes: null,
14356
14357     /**
14358      * An associative array of ids for elements that will be ignored if clicked
14359      * @property invalidHandleIds
14360      * @type {string: string}
14361      */
14362     invalidHandleIds: null,
14363
14364     /**
14365      * An indexted array of css class names for elements that will be ignored
14366      * if clicked.
14367      * @property invalidHandleClasses
14368      * @type string[]
14369      */
14370     invalidHandleClasses: null,
14371
14372     /**
14373      * The linked element's absolute X position at the time the drag was
14374      * started
14375      * @property startPageX
14376      * @type int
14377      * @private
14378      */
14379     startPageX: 0,
14380
14381     /**
14382      * The linked element's absolute X position at the time the drag was
14383      * started
14384      * @property startPageY
14385      * @type int
14386      * @private
14387      */
14388     startPageY: 0,
14389
14390     /**
14391      * The group defines a logical collection of DragDrop objects that are
14392      * related.  Instances only get events when interacting with other
14393      * DragDrop object in the same group.  This lets us define multiple
14394      * groups using a single DragDrop subclass if we want.
14395      * @property groups
14396      * @type {string: string}
14397      */
14398     groups: null,
14399
14400     /**
14401      * Individual drag/drop instances can be locked.  This will prevent
14402      * onmousedown start drag.
14403      * @property locked
14404      * @type boolean
14405      * @private
14406      */
14407     locked: false,
14408
14409     /**
14410      * Lock this instance
14411      * @method lock
14412      */
14413     lock: function() { this.locked = true; },
14414
14415     /**
14416      * Unlock this instace
14417      * @method unlock
14418      */
14419     unlock: function() { this.locked = false; },
14420
14421     /**
14422      * By default, all insances can be a drop target.  This can be disabled by
14423      * setting isTarget to false.
14424      * @method isTarget
14425      * @type boolean
14426      */
14427     isTarget: true,
14428
14429     /**
14430      * The padding configured for this drag and drop object for calculating
14431      * the drop zone intersection with this object.
14432      * @method padding
14433      * @type int[]
14434      */
14435     padding: null,
14436
14437     /**
14438      * Cached reference to the linked element
14439      * @property _domRef
14440      * @private
14441      */
14442     _domRef: null,
14443
14444     /**
14445      * Internal typeof flag
14446      * @property __ygDragDrop
14447      * @private
14448      */
14449     __ygDragDrop: true,
14450
14451     /**
14452      * Set to true when horizontal contraints are applied
14453      * @property constrainX
14454      * @type boolean
14455      * @private
14456      */
14457     constrainX: false,
14458
14459     /**
14460      * Set to true when vertical contraints are applied
14461      * @property constrainY
14462      * @type boolean
14463      * @private
14464      */
14465     constrainY: false,
14466
14467     /**
14468      * The left constraint
14469      * @property minX
14470      * @type int
14471      * @private
14472      */
14473     minX: 0,
14474
14475     /**
14476      * The right constraint
14477      * @property maxX
14478      * @type int
14479      * @private
14480      */
14481     maxX: 0,
14482
14483     /**
14484      * The up constraint
14485      * @property minY
14486      * @type int
14487      * @type int
14488      * @private
14489      */
14490     minY: 0,
14491
14492     /**
14493      * The down constraint
14494      * @property maxY
14495      * @type int
14496      * @private
14497      */
14498     maxY: 0,
14499
14500     /**
14501      * Maintain offsets when we resetconstraints.  Set to true when you want
14502      * the position of the element relative to its parent to stay the same
14503      * when the page changes
14504      *
14505      * @property maintainOffset
14506      * @type boolean
14507      */
14508     maintainOffset: false,
14509
14510     /**
14511      * Array of pixel locations the element will snap to if we specified a
14512      * horizontal graduation/interval.  This array is generated automatically
14513      * when you define a tick interval.
14514      * @property xTicks
14515      * @type int[]
14516      */
14517     xTicks: null,
14518
14519     /**
14520      * Array of pixel locations the element will snap to if we specified a
14521      * vertical graduation/interval.  This array is generated automatically
14522      * when you define a tick interval.
14523      * @property yTicks
14524      * @type int[]
14525      */
14526     yTicks: null,
14527
14528     /**
14529      * By default the drag and drop instance will only respond to the primary
14530      * button click (left button for a right-handed mouse).  Set to true to
14531      * allow drag and drop to start with any mouse click that is propogated
14532      * by the browser
14533      * @property primaryButtonOnly
14534      * @type boolean
14535      */
14536     primaryButtonOnly: true,
14537
14538     /**
14539      * The availabe property is false until the linked dom element is accessible.
14540      * @property available
14541      * @type boolean
14542      */
14543     available: false,
14544
14545     /**
14546      * By default, drags can only be initiated if the mousedown occurs in the
14547      * region the linked element is.  This is done in part to work around a
14548      * bug in some browsers that mis-report the mousedown if the previous
14549      * mouseup happened outside of the window.  This property is set to true
14550      * if outer handles are defined.
14551      *
14552      * @property hasOuterHandles
14553      * @type boolean
14554      * @default false
14555      */
14556     hasOuterHandles: false,
14557
14558     /**
14559      * Code that executes immediately before the startDrag event
14560      * @method b4StartDrag
14561      * @private
14562      */
14563     b4StartDrag: function(x, y) { },
14564
14565     /**
14566      * Abstract method called after a drag/drop object is clicked
14567      * and the drag or mousedown time thresholds have beeen met.
14568      * @method startDrag
14569      * @param {int} X click location
14570      * @param {int} Y click location
14571      */
14572     startDrag: function(x, y) { /* override this */ },
14573
14574     /**
14575      * Code that executes immediately before the onDrag event
14576      * @method b4Drag
14577      * @private
14578      */
14579     b4Drag: function(e) { },
14580
14581     /**
14582      * Abstract method called during the onMouseMove event while dragging an
14583      * object.
14584      * @method onDrag
14585      * @param {Event} e the mousemove event
14586      */
14587     onDrag: function(e) { /* override this */ },
14588
14589     /**
14590      * Abstract method called when this element fist begins hovering over
14591      * another DragDrop obj
14592      * @method onDragEnter
14593      * @param {Event} e the mousemove event
14594      * @param {String|DragDrop[]} id In POINT mode, the element
14595      * id this is hovering over.  In INTERSECT mode, an array of one or more
14596      * dragdrop items being hovered over.
14597      */
14598     onDragEnter: function(e, id) { /* override this */ },
14599
14600     /**
14601      * Code that executes immediately before the onDragOver event
14602      * @method b4DragOver
14603      * @private
14604      */
14605     b4DragOver: function(e) { },
14606
14607     /**
14608      * Abstract method called when this element is hovering over another
14609      * DragDrop obj
14610      * @method onDragOver
14611      * @param {Event} e the mousemove event
14612      * @param {String|DragDrop[]} id In POINT mode, the element
14613      * id this is hovering over.  In INTERSECT mode, an array of dd items
14614      * being hovered over.
14615      */
14616     onDragOver: function(e, id) { /* override this */ },
14617
14618     /**
14619      * Code that executes immediately before the onDragOut event
14620      * @method b4DragOut
14621      * @private
14622      */
14623     b4DragOut: function(e) { },
14624
14625     /**
14626      * Abstract method called when we are no longer hovering over an element
14627      * @method onDragOut
14628      * @param {Event} e the mousemove event
14629      * @param {String|DragDrop[]} id In POINT mode, the element
14630      * id this was hovering over.  In INTERSECT mode, an array of dd items
14631      * that the mouse is no longer over.
14632      */
14633     onDragOut: function(e, id) { /* override this */ },
14634
14635     /**
14636      * Code that executes immediately before the onDragDrop event
14637      * @method b4DragDrop
14638      * @private
14639      */
14640     b4DragDrop: function(e) { },
14641
14642     /**
14643      * Abstract method called when this item is dropped on another DragDrop
14644      * obj
14645      * @method onDragDrop
14646      * @param {Event} e the mouseup event
14647      * @param {String|DragDrop[]} id In POINT mode, the element
14648      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14649      * was dropped on.
14650      */
14651     onDragDrop: function(e, id) { /* override this */ },
14652
14653     /**
14654      * Abstract method called when this item is dropped on an area with no
14655      * drop target
14656      * @method onInvalidDrop
14657      * @param {Event} e the mouseup event
14658      */
14659     onInvalidDrop: function(e) { /* override this */ },
14660
14661     /**
14662      * Code that executes immediately before the endDrag event
14663      * @method b4EndDrag
14664      * @private
14665      */
14666     b4EndDrag: function(e) { },
14667
14668     /**
14669      * Fired when we are done dragging the object
14670      * @method endDrag
14671      * @param {Event} e the mouseup event
14672      */
14673     endDrag: function(e) { /* override this */ },
14674
14675     /**
14676      * Code executed immediately before the onMouseDown event
14677      * @method b4MouseDown
14678      * @param {Event} e the mousedown event
14679      * @private
14680      */
14681     b4MouseDown: function(e) {  },
14682
14683     /**
14684      * Event handler that fires when a drag/drop obj gets a mousedown
14685      * @method onMouseDown
14686      * @param {Event} e the mousedown event
14687      */
14688     onMouseDown: function(e) { /* override this */ },
14689
14690     /**
14691      * Event handler that fires when a drag/drop obj gets a mouseup
14692      * @method onMouseUp
14693      * @param {Event} e the mouseup event
14694      */
14695     onMouseUp: function(e) { /* override this */ },
14696
14697     /**
14698      * Override the onAvailable method to do what is needed after the initial
14699      * position was determined.
14700      * @method onAvailable
14701      */
14702     onAvailable: function () {
14703     },
14704
14705     /*
14706      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14707      * @type Object
14708      */
14709     defaultPadding : {left:0, right:0, top:0, bottom:0},
14710
14711     /*
14712      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14713  *
14714  * Usage:
14715  <pre><code>
14716  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14717                 { dragElId: "existingProxyDiv" });
14718  dd.startDrag = function(){
14719      this.constrainTo("parent-id");
14720  };
14721  </code></pre>
14722  * Or you can initalize it using the {@link Roo.Element} object:
14723  <pre><code>
14724  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14725      startDrag : function(){
14726          this.constrainTo("parent-id");
14727      }
14728  });
14729  </code></pre>
14730      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14731      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14732      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14733      * an object containing the sides to pad. For example: {right:10, bottom:10}
14734      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14735      */
14736     constrainTo : function(constrainTo, pad, inContent){
14737         if(typeof pad == "number"){
14738             pad = {left: pad, right:pad, top:pad, bottom:pad};
14739         }
14740         pad = pad || this.defaultPadding;
14741         var b = Roo.get(this.getEl()).getBox();
14742         var ce = Roo.get(constrainTo);
14743         var s = ce.getScroll();
14744         var c, cd = ce.dom;
14745         if(cd == document.body){
14746             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14747         }else{
14748             xy = ce.getXY();
14749             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14750         }
14751
14752
14753         var topSpace = b.y - c.y;
14754         var leftSpace = b.x - c.x;
14755
14756         this.resetConstraints();
14757         this.setXConstraint(leftSpace - (pad.left||0), // left
14758                 c.width - leftSpace - b.width - (pad.right||0) //right
14759         );
14760         this.setYConstraint(topSpace - (pad.top||0), //top
14761                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14762         );
14763     },
14764
14765     /**
14766      * Returns a reference to the linked element
14767      * @method getEl
14768      * @return {HTMLElement} the html element
14769      */
14770     getEl: function() {
14771         if (!this._domRef) {
14772             this._domRef = Roo.getDom(this.id);
14773         }
14774
14775         return this._domRef;
14776     },
14777
14778     /**
14779      * Returns a reference to the actual element to drag.  By default this is
14780      * the same as the html element, but it can be assigned to another
14781      * element. An example of this can be found in Roo.dd.DDProxy
14782      * @method getDragEl
14783      * @return {HTMLElement} the html element
14784      */
14785     getDragEl: function() {
14786         return Roo.getDom(this.dragElId);
14787     },
14788
14789     /**
14790      * Sets up the DragDrop object.  Must be called in the constructor of any
14791      * Roo.dd.DragDrop subclass
14792      * @method init
14793      * @param id the id of the linked element
14794      * @param {String} sGroup the group of related items
14795      * @param {object} config configuration attributes
14796      */
14797     init: function(id, sGroup, config) {
14798         this.initTarget(id, sGroup, config);
14799         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14800         // Event.on(this.id, "selectstart", Event.preventDefault);
14801     },
14802
14803     /**
14804      * Initializes Targeting functionality only... the object does not
14805      * get a mousedown handler.
14806      * @method initTarget
14807      * @param id the id of the linked element
14808      * @param {String} sGroup the group of related items
14809      * @param {object} config configuration attributes
14810      */
14811     initTarget: function(id, sGroup, config) {
14812
14813         // configuration attributes
14814         this.config = config || {};
14815
14816         // create a local reference to the drag and drop manager
14817         this.DDM = Roo.dd.DDM;
14818         // initialize the groups array
14819         this.groups = {};
14820
14821         // assume that we have an element reference instead of an id if the
14822         // parameter is not a string
14823         if (typeof id !== "string") {
14824             id = Roo.id(id);
14825         }
14826
14827         // set the id
14828         this.id = id;
14829
14830         // add to an interaction group
14831         this.addToGroup((sGroup) ? sGroup : "default");
14832
14833         // We don't want to register this as the handle with the manager
14834         // so we just set the id rather than calling the setter.
14835         this.handleElId = id;
14836
14837         // the linked element is the element that gets dragged by default
14838         this.setDragElId(id);
14839
14840         // by default, clicked anchors will not start drag operations.
14841         this.invalidHandleTypes = { A: "A" };
14842         this.invalidHandleIds = {};
14843         this.invalidHandleClasses = [];
14844
14845         this.applyConfig();
14846
14847         this.handleOnAvailable();
14848     },
14849
14850     /**
14851      * Applies the configuration parameters that were passed into the constructor.
14852      * This is supposed to happen at each level through the inheritance chain.  So
14853      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14854      * DragDrop in order to get all of the parameters that are available in
14855      * each object.
14856      * @method applyConfig
14857      */
14858     applyConfig: function() {
14859
14860         // configurable properties:
14861         //    padding, isTarget, maintainOffset, primaryButtonOnly
14862         this.padding           = this.config.padding || [0, 0, 0, 0];
14863         this.isTarget          = (this.config.isTarget !== false);
14864         this.maintainOffset    = (this.config.maintainOffset);
14865         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14866
14867     },
14868
14869     /**
14870      * Executed when the linked element is available
14871      * @method handleOnAvailable
14872      * @private
14873      */
14874     handleOnAvailable: function() {
14875         this.available = true;
14876         this.resetConstraints();
14877         this.onAvailable();
14878     },
14879
14880      /**
14881      * Configures the padding for the target zone in px.  Effectively expands
14882      * (or reduces) the virtual object size for targeting calculations.
14883      * Supports css-style shorthand; if only one parameter is passed, all sides
14884      * will have that padding, and if only two are passed, the top and bottom
14885      * will have the first param, the left and right the second.
14886      * @method setPadding
14887      * @param {int} iTop    Top pad
14888      * @param {int} iRight  Right pad
14889      * @param {int} iBot    Bot pad
14890      * @param {int} iLeft   Left pad
14891      */
14892     setPadding: function(iTop, iRight, iBot, iLeft) {
14893         // this.padding = [iLeft, iRight, iTop, iBot];
14894         if (!iRight && 0 !== iRight) {
14895             this.padding = [iTop, iTop, iTop, iTop];
14896         } else if (!iBot && 0 !== iBot) {
14897             this.padding = [iTop, iRight, iTop, iRight];
14898         } else {
14899             this.padding = [iTop, iRight, iBot, iLeft];
14900         }
14901     },
14902
14903     /**
14904      * Stores the initial placement of the linked element.
14905      * @method setInitialPosition
14906      * @param {int} diffX   the X offset, default 0
14907      * @param {int} diffY   the Y offset, default 0
14908      */
14909     setInitPosition: function(diffX, diffY) {
14910         var el = this.getEl();
14911
14912         if (!this.DDM.verifyEl(el)) {
14913             return;
14914         }
14915
14916         var dx = diffX || 0;
14917         var dy = diffY || 0;
14918
14919         var p = Dom.getXY( el );
14920
14921         this.initPageX = p[0] - dx;
14922         this.initPageY = p[1] - dy;
14923
14924         this.lastPageX = p[0];
14925         this.lastPageY = p[1];
14926
14927
14928         this.setStartPosition(p);
14929     },
14930
14931     /**
14932      * Sets the start position of the element.  This is set when the obj
14933      * is initialized, the reset when a drag is started.
14934      * @method setStartPosition
14935      * @param pos current position (from previous lookup)
14936      * @private
14937      */
14938     setStartPosition: function(pos) {
14939         var p = pos || Dom.getXY( this.getEl() );
14940         this.deltaSetXY = null;
14941
14942         this.startPageX = p[0];
14943         this.startPageY = p[1];
14944     },
14945
14946     /**
14947      * Add this instance to a group of related drag/drop objects.  All
14948      * instances belong to at least one group, and can belong to as many
14949      * groups as needed.
14950      * @method addToGroup
14951      * @param sGroup {string} the name of the group
14952      */
14953     addToGroup: function(sGroup) {
14954         this.groups[sGroup] = true;
14955         this.DDM.regDragDrop(this, sGroup);
14956     },
14957
14958     /**
14959      * Remove's this instance from the supplied interaction group
14960      * @method removeFromGroup
14961      * @param {string}  sGroup  The group to drop
14962      */
14963     removeFromGroup: function(sGroup) {
14964         if (this.groups[sGroup]) {
14965             delete this.groups[sGroup];
14966         }
14967
14968         this.DDM.removeDDFromGroup(this, sGroup);
14969     },
14970
14971     /**
14972      * Allows you to specify that an element other than the linked element
14973      * will be moved with the cursor during a drag
14974      * @method setDragElId
14975      * @param id {string} the id of the element that will be used to initiate the drag
14976      */
14977     setDragElId: function(id) {
14978         this.dragElId = id;
14979     },
14980
14981     /**
14982      * Allows you to specify a child of the linked element that should be
14983      * used to initiate the drag operation.  An example of this would be if
14984      * you have a content div with text and links.  Clicking anywhere in the
14985      * content area would normally start the drag operation.  Use this method
14986      * to specify that an element inside of the content div is the element
14987      * that starts the drag operation.
14988      * @method setHandleElId
14989      * @param id {string} the id of the element that will be used to
14990      * initiate the drag.
14991      */
14992     setHandleElId: function(id) {
14993         if (typeof id !== "string") {
14994             id = Roo.id(id);
14995         }
14996         this.handleElId = id;
14997         this.DDM.regHandle(this.id, id);
14998     },
14999
15000     /**
15001      * Allows you to set an element outside of the linked element as a drag
15002      * handle
15003      * @method setOuterHandleElId
15004      * @param id the id of the element that will be used to initiate the drag
15005      */
15006     setOuterHandleElId: function(id) {
15007         if (typeof id !== "string") {
15008             id = Roo.id(id);
15009         }
15010         Event.on(id, "mousedown",
15011                 this.handleMouseDown, this);
15012         this.setHandleElId(id);
15013
15014         this.hasOuterHandles = true;
15015     },
15016
15017     /**
15018      * Remove all drag and drop hooks for this element
15019      * @method unreg
15020      */
15021     unreg: function() {
15022         Event.un(this.id, "mousedown",
15023                 this.handleMouseDown);
15024         this._domRef = null;
15025         this.DDM._remove(this);
15026     },
15027
15028     destroy : function(){
15029         this.unreg();
15030     },
15031
15032     /**
15033      * Returns true if this instance is locked, or the drag drop mgr is locked
15034      * (meaning that all drag/drop is disabled on the page.)
15035      * @method isLocked
15036      * @return {boolean} true if this obj or all drag/drop is locked, else
15037      * false
15038      */
15039     isLocked: function() {
15040         return (this.DDM.isLocked() || this.locked);
15041     },
15042
15043     /**
15044      * Fired when this object is clicked
15045      * @method handleMouseDown
15046      * @param {Event} e
15047      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15048      * @private
15049      */
15050     handleMouseDown: function(e, oDD){
15051         if (this.primaryButtonOnly && e.button != 0) {
15052             return;
15053         }
15054
15055         if (this.isLocked()) {
15056             return;
15057         }
15058
15059         this.DDM.refreshCache(this.groups);
15060
15061         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15062         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15063         } else {
15064             if (this.clickValidator(e)) {
15065
15066                 // set the initial element position
15067                 this.setStartPosition();
15068
15069
15070                 this.b4MouseDown(e);
15071                 this.onMouseDown(e);
15072
15073                 this.DDM.handleMouseDown(e, this);
15074
15075                 this.DDM.stopEvent(e);
15076             } else {
15077
15078
15079             }
15080         }
15081     },
15082
15083     clickValidator: function(e) {
15084         var target = e.getTarget();
15085         return ( this.isValidHandleChild(target) &&
15086                     (this.id == this.handleElId ||
15087                         this.DDM.handleWasClicked(target, this.id)) );
15088     },
15089
15090     /**
15091      * Allows you to specify a tag name that should not start a drag operation
15092      * when clicked.  This is designed to facilitate embedding links within a
15093      * drag handle that do something other than start the drag.
15094      * @method addInvalidHandleType
15095      * @param {string} tagName the type of element to exclude
15096      */
15097     addInvalidHandleType: function(tagName) {
15098         var type = tagName.toUpperCase();
15099         this.invalidHandleTypes[type] = type;
15100     },
15101
15102     /**
15103      * Lets you to specify an element id for a child of a drag handle
15104      * that should not initiate a drag
15105      * @method addInvalidHandleId
15106      * @param {string} id the element id of the element you wish to ignore
15107      */
15108     addInvalidHandleId: function(id) {
15109         if (typeof id !== "string") {
15110             id = Roo.id(id);
15111         }
15112         this.invalidHandleIds[id] = id;
15113     },
15114
15115     /**
15116      * Lets you specify a css class of elements that will not initiate a drag
15117      * @method addInvalidHandleClass
15118      * @param {string} cssClass the class of the elements you wish to ignore
15119      */
15120     addInvalidHandleClass: function(cssClass) {
15121         this.invalidHandleClasses.push(cssClass);
15122     },
15123
15124     /**
15125      * Unsets an excluded tag name set by addInvalidHandleType
15126      * @method removeInvalidHandleType
15127      * @param {string} tagName the type of element to unexclude
15128      */
15129     removeInvalidHandleType: function(tagName) {
15130         var type = tagName.toUpperCase();
15131         // this.invalidHandleTypes[type] = null;
15132         delete this.invalidHandleTypes[type];
15133     },
15134
15135     /**
15136      * Unsets an invalid handle id
15137      * @method removeInvalidHandleId
15138      * @param {string} id the id of the element to re-enable
15139      */
15140     removeInvalidHandleId: function(id) {
15141         if (typeof id !== "string") {
15142             id = Roo.id(id);
15143         }
15144         delete this.invalidHandleIds[id];
15145     },
15146
15147     /**
15148      * Unsets an invalid css class
15149      * @method removeInvalidHandleClass
15150      * @param {string} cssClass the class of the element(s) you wish to
15151      * re-enable
15152      */
15153     removeInvalidHandleClass: function(cssClass) {
15154         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15155             if (this.invalidHandleClasses[i] == cssClass) {
15156                 delete this.invalidHandleClasses[i];
15157             }
15158         }
15159     },
15160
15161     /**
15162      * Checks the tag exclusion list to see if this click should be ignored
15163      * @method isValidHandleChild
15164      * @param {HTMLElement} node the HTMLElement to evaluate
15165      * @return {boolean} true if this is a valid tag type, false if not
15166      */
15167     isValidHandleChild: function(node) {
15168
15169         var valid = true;
15170         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15171         var nodeName;
15172         try {
15173             nodeName = node.nodeName.toUpperCase();
15174         } catch(e) {
15175             nodeName = node.nodeName;
15176         }
15177         valid = valid && !this.invalidHandleTypes[nodeName];
15178         valid = valid && !this.invalidHandleIds[node.id];
15179
15180         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15181             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15182         }
15183
15184
15185         return valid;
15186
15187     },
15188
15189     /**
15190      * Create the array of horizontal tick marks if an interval was specified
15191      * in setXConstraint().
15192      * @method setXTicks
15193      * @private
15194      */
15195     setXTicks: function(iStartX, iTickSize) {
15196         this.xTicks = [];
15197         this.xTickSize = iTickSize;
15198
15199         var tickMap = {};
15200
15201         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15202             if (!tickMap[i]) {
15203                 this.xTicks[this.xTicks.length] = i;
15204                 tickMap[i] = true;
15205             }
15206         }
15207
15208         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15209             if (!tickMap[i]) {
15210                 this.xTicks[this.xTicks.length] = i;
15211                 tickMap[i] = true;
15212             }
15213         }
15214
15215         this.xTicks.sort(this.DDM.numericSort) ;
15216     },
15217
15218     /**
15219      * Create the array of vertical tick marks if an interval was specified in
15220      * setYConstraint().
15221      * @method setYTicks
15222      * @private
15223      */
15224     setYTicks: function(iStartY, iTickSize) {
15225         this.yTicks = [];
15226         this.yTickSize = iTickSize;
15227
15228         var tickMap = {};
15229
15230         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15231             if (!tickMap[i]) {
15232                 this.yTicks[this.yTicks.length] = i;
15233                 tickMap[i] = true;
15234             }
15235         }
15236
15237         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15238             if (!tickMap[i]) {
15239                 this.yTicks[this.yTicks.length] = i;
15240                 tickMap[i] = true;
15241             }
15242         }
15243
15244         this.yTicks.sort(this.DDM.numericSort) ;
15245     },
15246
15247     /**
15248      * By default, the element can be dragged any place on the screen.  Use
15249      * this method to limit the horizontal travel of the element.  Pass in
15250      * 0,0 for the parameters if you want to lock the drag to the y axis.
15251      * @method setXConstraint
15252      * @param {int} iLeft the number of pixels the element can move to the left
15253      * @param {int} iRight the number of pixels the element can move to the
15254      * right
15255      * @param {int} iTickSize optional parameter for specifying that the
15256      * element
15257      * should move iTickSize pixels at a time.
15258      */
15259     setXConstraint: function(iLeft, iRight, iTickSize) {
15260         this.leftConstraint = iLeft;
15261         this.rightConstraint = iRight;
15262
15263         this.minX = this.initPageX - iLeft;
15264         this.maxX = this.initPageX + iRight;
15265         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15266
15267         this.constrainX = true;
15268     },
15269
15270     /**
15271      * Clears any constraints applied to this instance.  Also clears ticks
15272      * since they can't exist independent of a constraint at this time.
15273      * @method clearConstraints
15274      */
15275     clearConstraints: function() {
15276         this.constrainX = false;
15277         this.constrainY = false;
15278         this.clearTicks();
15279     },
15280
15281     /**
15282      * Clears any tick interval defined for this instance
15283      * @method clearTicks
15284      */
15285     clearTicks: function() {
15286         this.xTicks = null;
15287         this.yTicks = null;
15288         this.xTickSize = 0;
15289         this.yTickSize = 0;
15290     },
15291
15292     /**
15293      * By default, the element can be dragged any place on the screen.  Set
15294      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15295      * parameters if you want to lock the drag to the x axis.
15296      * @method setYConstraint
15297      * @param {int} iUp the number of pixels the element can move up
15298      * @param {int} iDown the number of pixels the element can move down
15299      * @param {int} iTickSize optional parameter for specifying that the
15300      * element should move iTickSize pixels at a time.
15301      */
15302     setYConstraint: function(iUp, iDown, iTickSize) {
15303         this.topConstraint = iUp;
15304         this.bottomConstraint = iDown;
15305
15306         this.minY = this.initPageY - iUp;
15307         this.maxY = this.initPageY + iDown;
15308         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15309
15310         this.constrainY = true;
15311
15312     },
15313
15314     /**
15315      * resetConstraints must be called if you manually reposition a dd element.
15316      * @method resetConstraints
15317      * @param {boolean} maintainOffset
15318      */
15319     resetConstraints: function() {
15320
15321
15322         // Maintain offsets if necessary
15323         if (this.initPageX || this.initPageX === 0) {
15324             // figure out how much this thing has moved
15325             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15326             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15327
15328             this.setInitPosition(dx, dy);
15329
15330         // This is the first time we have detected the element's position
15331         } else {
15332             this.setInitPosition();
15333         }
15334
15335         if (this.constrainX) {
15336             this.setXConstraint( this.leftConstraint,
15337                                  this.rightConstraint,
15338                                  this.xTickSize        );
15339         }
15340
15341         if (this.constrainY) {
15342             this.setYConstraint( this.topConstraint,
15343                                  this.bottomConstraint,
15344                                  this.yTickSize         );
15345         }
15346     },
15347
15348     /**
15349      * Normally the drag element is moved pixel by pixel, but we can specify
15350      * that it move a number of pixels at a time.  This method resolves the
15351      * location when we have it set up like this.
15352      * @method getTick
15353      * @param {int} val where we want to place the object
15354      * @param {int[]} tickArray sorted array of valid points
15355      * @return {int} the closest tick
15356      * @private
15357      */
15358     getTick: function(val, tickArray) {
15359
15360         if (!tickArray) {
15361             // If tick interval is not defined, it is effectively 1 pixel,
15362             // so we return the value passed to us.
15363             return val;
15364         } else if (tickArray[0] >= val) {
15365             // The value is lower than the first tick, so we return the first
15366             // tick.
15367             return tickArray[0];
15368         } else {
15369             for (var i=0, len=tickArray.length; i<len; ++i) {
15370                 var next = i + 1;
15371                 if (tickArray[next] && tickArray[next] >= val) {
15372                     var diff1 = val - tickArray[i];
15373                     var diff2 = tickArray[next] - val;
15374                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15375                 }
15376             }
15377
15378             // The value is larger than the last tick, so we return the last
15379             // tick.
15380             return tickArray[tickArray.length - 1];
15381         }
15382     },
15383
15384     /**
15385      * toString method
15386      * @method toString
15387      * @return {string} string representation of the dd obj
15388      */
15389     toString: function() {
15390         return ("DragDrop " + this.id);
15391     }
15392
15393 };
15394
15395 })();
15396 /*
15397  * Based on:
15398  * Ext JS Library 1.1.1
15399  * Copyright(c) 2006-2007, Ext JS, LLC.
15400  *
15401  * Originally Released Under LGPL - original licence link has changed is not relivant.
15402  *
15403  * Fork - LGPL
15404  * <script type="text/javascript">
15405  */
15406
15407
15408 /**
15409  * The drag and drop utility provides a framework for building drag and drop
15410  * applications.  In addition to enabling drag and drop for specific elements,
15411  * the drag and drop elements are tracked by the manager class, and the
15412  * interactions between the various elements are tracked during the drag and
15413  * the implementing code is notified about these important moments.
15414  */
15415
15416 // Only load the library once.  Rewriting the manager class would orphan
15417 // existing drag and drop instances.
15418 if (!Roo.dd.DragDropMgr) {
15419
15420 /**
15421  * @class Roo.dd.DragDropMgr
15422  * DragDropMgr is a singleton that tracks the element interaction for
15423  * all DragDrop items in the window.  Generally, you will not call
15424  * this class directly, but it does have helper methods that could
15425  * be useful in your DragDrop implementations.
15426  * @singleton
15427  */
15428 Roo.dd.DragDropMgr = function() {
15429
15430     var Event = Roo.EventManager;
15431
15432     return {
15433
15434         /**
15435          * Two dimensional Array of registered DragDrop objects.  The first
15436          * dimension is the DragDrop item group, the second the DragDrop
15437          * object.
15438          * @property ids
15439          * @type {string: string}
15440          * @private
15441          * @static
15442          */
15443         ids: {},
15444
15445         /**
15446          * Array of element ids defined as drag handles.  Used to determine
15447          * if the element that generated the mousedown event is actually the
15448          * handle and not the html element itself.
15449          * @property handleIds
15450          * @type {string: string}
15451          * @private
15452          * @static
15453          */
15454         handleIds: {},
15455
15456         /**
15457          * the DragDrop object that is currently being dragged
15458          * @property dragCurrent
15459          * @type DragDrop
15460          * @private
15461          * @static
15462          **/
15463         dragCurrent: null,
15464
15465         /**
15466          * the DragDrop object(s) that are being hovered over
15467          * @property dragOvers
15468          * @type Array
15469          * @private
15470          * @static
15471          */
15472         dragOvers: {},
15473
15474         /**
15475          * the X distance between the cursor and the object being dragged
15476          * @property deltaX
15477          * @type int
15478          * @private
15479          * @static
15480          */
15481         deltaX: 0,
15482
15483         /**
15484          * the Y distance between the cursor and the object being dragged
15485          * @property deltaY
15486          * @type int
15487          * @private
15488          * @static
15489          */
15490         deltaY: 0,
15491
15492         /**
15493          * Flag to determine if we should prevent the default behavior of the
15494          * events we define. By default this is true, but this can be set to
15495          * false if you need the default behavior (not recommended)
15496          * @property preventDefault
15497          * @type boolean
15498          * @static
15499          */
15500         preventDefault: true,
15501
15502         /**
15503          * Flag to determine if we should stop the propagation of the events
15504          * we generate. This is true by default but you may want to set it to
15505          * false if the html element contains other features that require the
15506          * mouse click.
15507          * @property stopPropagation
15508          * @type boolean
15509          * @static
15510          */
15511         stopPropagation: true,
15512
15513         /**
15514          * Internal flag that is set to true when drag and drop has been
15515          * intialized
15516          * @property initialized
15517          * @private
15518          * @static
15519          */
15520         initalized: false,
15521
15522         /**
15523          * All drag and drop can be disabled.
15524          * @property locked
15525          * @private
15526          * @static
15527          */
15528         locked: false,
15529
15530         /**
15531          * Called the first time an element is registered.
15532          * @method init
15533          * @private
15534          * @static
15535          */
15536         init: function() {
15537             this.initialized = true;
15538         },
15539
15540         /**
15541          * In point mode, drag and drop interaction is defined by the
15542          * location of the cursor during the drag/drop
15543          * @property POINT
15544          * @type int
15545          * @static
15546          */
15547         POINT: 0,
15548
15549         /**
15550          * In intersect mode, drag and drop interactio nis defined by the
15551          * overlap of two or more drag and drop objects.
15552          * @property INTERSECT
15553          * @type int
15554          * @static
15555          */
15556         INTERSECT: 1,
15557
15558         /**
15559          * The current drag and drop mode.  Default: POINT
15560          * @property mode
15561          * @type int
15562          * @static
15563          */
15564         mode: 0,
15565
15566         /**
15567          * Runs method on all drag and drop objects
15568          * @method _execOnAll
15569          * @private
15570          * @static
15571          */
15572         _execOnAll: function(sMethod, args) {
15573             for (var i in this.ids) {
15574                 for (var j in this.ids[i]) {
15575                     var oDD = this.ids[i][j];
15576                     if (! this.isTypeOfDD(oDD)) {
15577                         continue;
15578                     }
15579                     oDD[sMethod].apply(oDD, args);
15580                 }
15581             }
15582         },
15583
15584         /**
15585          * Drag and drop initialization.  Sets up the global event handlers
15586          * @method _onLoad
15587          * @private
15588          * @static
15589          */
15590         _onLoad: function() {
15591
15592             this.init();
15593
15594
15595             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15596             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15597             Event.on(window,   "unload",    this._onUnload, this, true);
15598             Event.on(window,   "resize",    this._onResize, this, true);
15599             // Event.on(window,   "mouseout",    this._test);
15600
15601         },
15602
15603         /**
15604          * Reset constraints on all drag and drop objs
15605          * @method _onResize
15606          * @private
15607          * @static
15608          */
15609         _onResize: function(e) {
15610             this._execOnAll("resetConstraints", []);
15611         },
15612
15613         /**
15614          * Lock all drag and drop functionality
15615          * @method lock
15616          * @static
15617          */
15618         lock: function() { this.locked = true; },
15619
15620         /**
15621          * Unlock all drag and drop functionality
15622          * @method unlock
15623          * @static
15624          */
15625         unlock: function() { this.locked = false; },
15626
15627         /**
15628          * Is drag and drop locked?
15629          * @method isLocked
15630          * @return {boolean} True if drag and drop is locked, false otherwise.
15631          * @static
15632          */
15633         isLocked: function() { return this.locked; },
15634
15635         /**
15636          * Location cache that is set for all drag drop objects when a drag is
15637          * initiated, cleared when the drag is finished.
15638          * @property locationCache
15639          * @private
15640          * @static
15641          */
15642         locationCache: {},
15643
15644         /**
15645          * Set useCache to false if you want to force object the lookup of each
15646          * drag and drop linked element constantly during a drag.
15647          * @property useCache
15648          * @type boolean
15649          * @static
15650          */
15651         useCache: true,
15652
15653         /**
15654          * The number of pixels that the mouse needs to move after the
15655          * mousedown before the drag is initiated.  Default=3;
15656          * @property clickPixelThresh
15657          * @type int
15658          * @static
15659          */
15660         clickPixelThresh: 3,
15661
15662         /**
15663          * The number of milliseconds after the mousedown event to initiate the
15664          * drag if we don't get a mouseup event. Default=1000
15665          * @property clickTimeThresh
15666          * @type int
15667          * @static
15668          */
15669         clickTimeThresh: 350,
15670
15671         /**
15672          * Flag that indicates that either the drag pixel threshold or the
15673          * mousdown time threshold has been met
15674          * @property dragThreshMet
15675          * @type boolean
15676          * @private
15677          * @static
15678          */
15679         dragThreshMet: false,
15680
15681         /**
15682          * Timeout used for the click time threshold
15683          * @property clickTimeout
15684          * @type Object
15685          * @private
15686          * @static
15687          */
15688         clickTimeout: null,
15689
15690         /**
15691          * The X position of the mousedown event stored for later use when a
15692          * drag threshold is met.
15693          * @property startX
15694          * @type int
15695          * @private
15696          * @static
15697          */
15698         startX: 0,
15699
15700         /**
15701          * The Y position of the mousedown event stored for later use when a
15702          * drag threshold is met.
15703          * @property startY
15704          * @type int
15705          * @private
15706          * @static
15707          */
15708         startY: 0,
15709
15710         /**
15711          * Each DragDrop instance must be registered with the DragDropMgr.
15712          * This is executed in DragDrop.init()
15713          * @method regDragDrop
15714          * @param {DragDrop} oDD the DragDrop object to register
15715          * @param {String} sGroup the name of the group this element belongs to
15716          * @static
15717          */
15718         regDragDrop: function(oDD, sGroup) {
15719             if (!this.initialized) { this.init(); }
15720
15721             if (!this.ids[sGroup]) {
15722                 this.ids[sGroup] = {};
15723             }
15724             this.ids[sGroup][oDD.id] = oDD;
15725         },
15726
15727         /**
15728          * Removes the supplied dd instance from the supplied group. Executed
15729          * by DragDrop.removeFromGroup, so don't call this function directly.
15730          * @method removeDDFromGroup
15731          * @private
15732          * @static
15733          */
15734         removeDDFromGroup: function(oDD, sGroup) {
15735             if (!this.ids[sGroup]) {
15736                 this.ids[sGroup] = {};
15737             }
15738
15739             var obj = this.ids[sGroup];
15740             if (obj && obj[oDD.id]) {
15741                 delete obj[oDD.id];
15742             }
15743         },
15744
15745         /**
15746          * Unregisters a drag and drop item.  This is executed in
15747          * DragDrop.unreg, use that method instead of calling this directly.
15748          * @method _remove
15749          * @private
15750          * @static
15751          */
15752         _remove: function(oDD) {
15753             for (var g in oDD.groups) {
15754                 if (g && this.ids[g][oDD.id]) {
15755                     delete this.ids[g][oDD.id];
15756                 }
15757             }
15758             delete this.handleIds[oDD.id];
15759         },
15760
15761         /**
15762          * Each DragDrop handle element must be registered.  This is done
15763          * automatically when executing DragDrop.setHandleElId()
15764          * @method regHandle
15765          * @param {String} sDDId the DragDrop id this element is a handle for
15766          * @param {String} sHandleId the id of the element that is the drag
15767          * handle
15768          * @static
15769          */
15770         regHandle: function(sDDId, sHandleId) {
15771             if (!this.handleIds[sDDId]) {
15772                 this.handleIds[sDDId] = {};
15773             }
15774             this.handleIds[sDDId][sHandleId] = sHandleId;
15775         },
15776
15777         /**
15778          * Utility function to determine if a given element has been
15779          * registered as a drag drop item.
15780          * @method isDragDrop
15781          * @param {String} id the element id to check
15782          * @return {boolean} true if this element is a DragDrop item,
15783          * false otherwise
15784          * @static
15785          */
15786         isDragDrop: function(id) {
15787             return ( this.getDDById(id) ) ? true : false;
15788         },
15789
15790         /**
15791          * Returns the drag and drop instances that are in all groups the
15792          * passed in instance belongs to.
15793          * @method getRelated
15794          * @param {DragDrop} p_oDD the obj to get related data for
15795          * @param {boolean} bTargetsOnly if true, only return targetable objs
15796          * @return {DragDrop[]} the related instances
15797          * @static
15798          */
15799         getRelated: function(p_oDD, bTargetsOnly) {
15800             var oDDs = [];
15801             for (var i in p_oDD.groups) {
15802                 for (j in this.ids[i]) {
15803                     var dd = this.ids[i][j];
15804                     if (! this.isTypeOfDD(dd)) {
15805                         continue;
15806                     }
15807                     if (!bTargetsOnly || dd.isTarget) {
15808                         oDDs[oDDs.length] = dd;
15809                     }
15810                 }
15811             }
15812
15813             return oDDs;
15814         },
15815
15816         /**
15817          * Returns true if the specified dd target is a legal target for
15818          * the specifice drag obj
15819          * @method isLegalTarget
15820          * @param {DragDrop} the drag obj
15821          * @param {DragDrop} the target
15822          * @return {boolean} true if the target is a legal target for the
15823          * dd obj
15824          * @static
15825          */
15826         isLegalTarget: function (oDD, oTargetDD) {
15827             var targets = this.getRelated(oDD, true);
15828             for (var i=0, len=targets.length;i<len;++i) {
15829                 if (targets[i].id == oTargetDD.id) {
15830                     return true;
15831                 }
15832             }
15833
15834             return false;
15835         },
15836
15837         /**
15838          * My goal is to be able to transparently determine if an object is
15839          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15840          * returns "object", oDD.constructor.toString() always returns
15841          * "DragDrop" and not the name of the subclass.  So for now it just
15842          * evaluates a well-known variable in DragDrop.
15843          * @method isTypeOfDD
15844          * @param {Object} the object to evaluate
15845          * @return {boolean} true if typeof oDD = DragDrop
15846          * @static
15847          */
15848         isTypeOfDD: function (oDD) {
15849             return (oDD && oDD.__ygDragDrop);
15850         },
15851
15852         /**
15853          * Utility function to determine if a given element has been
15854          * registered as a drag drop handle for the given Drag Drop object.
15855          * @method isHandle
15856          * @param {String} id the element id to check
15857          * @return {boolean} true if this element is a DragDrop handle, false
15858          * otherwise
15859          * @static
15860          */
15861         isHandle: function(sDDId, sHandleId) {
15862             return ( this.handleIds[sDDId] &&
15863                             this.handleIds[sDDId][sHandleId] );
15864         },
15865
15866         /**
15867          * Returns the DragDrop instance for a given id
15868          * @method getDDById
15869          * @param {String} id the id of the DragDrop object
15870          * @return {DragDrop} the drag drop object, null if it is not found
15871          * @static
15872          */
15873         getDDById: function(id) {
15874             for (var i in this.ids) {
15875                 if (this.ids[i][id]) {
15876                     return this.ids[i][id];
15877                 }
15878             }
15879             return null;
15880         },
15881
15882         /**
15883          * Fired after a registered DragDrop object gets the mousedown event.
15884          * Sets up the events required to track the object being dragged
15885          * @method handleMouseDown
15886          * @param {Event} e the event
15887          * @param oDD the DragDrop object being dragged
15888          * @private
15889          * @static
15890          */
15891         handleMouseDown: function(e, oDD) {
15892             if(Roo.QuickTips){
15893                 Roo.QuickTips.disable();
15894             }
15895             this.currentTarget = e.getTarget();
15896
15897             this.dragCurrent = oDD;
15898
15899             var el = oDD.getEl();
15900
15901             // track start position
15902             this.startX = e.getPageX();
15903             this.startY = e.getPageY();
15904
15905             this.deltaX = this.startX - el.offsetLeft;
15906             this.deltaY = this.startY - el.offsetTop;
15907
15908             this.dragThreshMet = false;
15909
15910             this.clickTimeout = setTimeout(
15911                     function() {
15912                         var DDM = Roo.dd.DDM;
15913                         DDM.startDrag(DDM.startX, DDM.startY);
15914                     },
15915                     this.clickTimeThresh );
15916         },
15917
15918         /**
15919          * Fired when either the drag pixel threshol or the mousedown hold
15920          * time threshold has been met.
15921          * @method startDrag
15922          * @param x {int} the X position of the original mousedown
15923          * @param y {int} the Y position of the original mousedown
15924          * @static
15925          */
15926         startDrag: function(x, y) {
15927             clearTimeout(this.clickTimeout);
15928             if (this.dragCurrent) {
15929                 this.dragCurrent.b4StartDrag(x, y);
15930                 this.dragCurrent.startDrag(x, y);
15931             }
15932             this.dragThreshMet = true;
15933         },
15934
15935         /**
15936          * Internal function to handle the mouseup event.  Will be invoked
15937          * from the context of the document.
15938          * @method handleMouseUp
15939          * @param {Event} e the event
15940          * @private
15941          * @static
15942          */
15943         handleMouseUp: function(e) {
15944
15945             if(Roo.QuickTips){
15946                 Roo.QuickTips.enable();
15947             }
15948             if (! this.dragCurrent) {
15949                 return;
15950             }
15951
15952             clearTimeout(this.clickTimeout);
15953
15954             if (this.dragThreshMet) {
15955                 this.fireEvents(e, true);
15956             } else {
15957             }
15958
15959             this.stopDrag(e);
15960
15961             this.stopEvent(e);
15962         },
15963
15964         /**
15965          * Utility to stop event propagation and event default, if these
15966          * features are turned on.
15967          * @method stopEvent
15968          * @param {Event} e the event as returned by this.getEvent()
15969          * @static
15970          */
15971         stopEvent: function(e){
15972             if(this.stopPropagation) {
15973                 e.stopPropagation();
15974             }
15975
15976             if (this.preventDefault) {
15977                 e.preventDefault();
15978             }
15979         },
15980
15981         /**
15982          * Internal function to clean up event handlers after the drag
15983          * operation is complete
15984          * @method stopDrag
15985          * @param {Event} e the event
15986          * @private
15987          * @static
15988          */
15989         stopDrag: function(e) {
15990             // Fire the drag end event for the item that was dragged
15991             if (this.dragCurrent) {
15992                 if (this.dragThreshMet) {
15993                     this.dragCurrent.b4EndDrag(e);
15994                     this.dragCurrent.endDrag(e);
15995                 }
15996
15997                 this.dragCurrent.onMouseUp(e);
15998             }
15999
16000             this.dragCurrent = null;
16001             this.dragOvers = {};
16002         },
16003
16004         /**
16005          * Internal function to handle the mousemove event.  Will be invoked
16006          * from the context of the html element.
16007          *
16008          * @TODO figure out what we can do about mouse events lost when the
16009          * user drags objects beyond the window boundary.  Currently we can
16010          * detect this in internet explorer by verifying that the mouse is
16011          * down during the mousemove event.  Firefox doesn't give us the
16012          * button state on the mousemove event.
16013          * @method handleMouseMove
16014          * @param {Event} e the event
16015          * @private
16016          * @static
16017          */
16018         handleMouseMove: function(e) {
16019             if (! this.dragCurrent) {
16020                 return true;
16021             }
16022
16023             // var button = e.which || e.button;
16024
16025             // check for IE mouseup outside of page boundary
16026             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16027                 this.stopEvent(e);
16028                 return this.handleMouseUp(e);
16029             }
16030
16031             if (!this.dragThreshMet) {
16032                 var diffX = Math.abs(this.startX - e.getPageX());
16033                 var diffY = Math.abs(this.startY - e.getPageY());
16034                 if (diffX > this.clickPixelThresh ||
16035                             diffY > this.clickPixelThresh) {
16036                     this.startDrag(this.startX, this.startY);
16037                 }
16038             }
16039
16040             if (this.dragThreshMet) {
16041                 this.dragCurrent.b4Drag(e);
16042                 this.dragCurrent.onDrag(e);
16043                 if(!this.dragCurrent.moveOnly){
16044                     this.fireEvents(e, false);
16045                 }
16046             }
16047
16048             this.stopEvent(e);
16049
16050             return true;
16051         },
16052
16053         /**
16054          * Iterates over all of the DragDrop elements to find ones we are
16055          * hovering over or dropping on
16056          * @method fireEvents
16057          * @param {Event} e the event
16058          * @param {boolean} isDrop is this a drop op or a mouseover op?
16059          * @private
16060          * @static
16061          */
16062         fireEvents: function(e, isDrop) {
16063             var dc = this.dragCurrent;
16064
16065             // If the user did the mouse up outside of the window, we could
16066             // get here even though we have ended the drag.
16067             if (!dc || dc.isLocked()) {
16068                 return;
16069             }
16070
16071             var pt = e.getPoint();
16072
16073             // cache the previous dragOver array
16074             var oldOvers = [];
16075
16076             var outEvts   = [];
16077             var overEvts  = [];
16078             var dropEvts  = [];
16079             var enterEvts = [];
16080
16081             // Check to see if the object(s) we were hovering over is no longer
16082             // being hovered over so we can fire the onDragOut event
16083             for (var i in this.dragOvers) {
16084
16085                 var ddo = this.dragOvers[i];
16086
16087                 if (! this.isTypeOfDD(ddo)) {
16088                     continue;
16089                 }
16090
16091                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16092                     outEvts.push( ddo );
16093                 }
16094
16095                 oldOvers[i] = true;
16096                 delete this.dragOvers[i];
16097             }
16098
16099             for (var sGroup in dc.groups) {
16100
16101                 if ("string" != typeof sGroup) {
16102                     continue;
16103                 }
16104
16105                 for (i in this.ids[sGroup]) {
16106                     var oDD = this.ids[sGroup][i];
16107                     if (! this.isTypeOfDD(oDD)) {
16108                         continue;
16109                     }
16110
16111                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16112                         if (this.isOverTarget(pt, oDD, this.mode)) {
16113                             // look for drop interactions
16114                             if (isDrop) {
16115                                 dropEvts.push( oDD );
16116                             // look for drag enter and drag over interactions
16117                             } else {
16118
16119                                 // initial drag over: dragEnter fires
16120                                 if (!oldOvers[oDD.id]) {
16121                                     enterEvts.push( oDD );
16122                                 // subsequent drag overs: dragOver fires
16123                                 } else {
16124                                     overEvts.push( oDD );
16125                                 }
16126
16127                                 this.dragOvers[oDD.id] = oDD;
16128                             }
16129                         }
16130                     }
16131                 }
16132             }
16133
16134             if (this.mode) {
16135                 if (outEvts.length) {
16136                     dc.b4DragOut(e, outEvts);
16137                     dc.onDragOut(e, outEvts);
16138                 }
16139
16140                 if (enterEvts.length) {
16141                     dc.onDragEnter(e, enterEvts);
16142                 }
16143
16144                 if (overEvts.length) {
16145                     dc.b4DragOver(e, overEvts);
16146                     dc.onDragOver(e, overEvts);
16147                 }
16148
16149                 if (dropEvts.length) {
16150                     dc.b4DragDrop(e, dropEvts);
16151                     dc.onDragDrop(e, dropEvts);
16152                 }
16153
16154             } else {
16155                 // fire dragout events
16156                 var len = 0;
16157                 for (i=0, len=outEvts.length; i<len; ++i) {
16158                     dc.b4DragOut(e, outEvts[i].id);
16159                     dc.onDragOut(e, outEvts[i].id);
16160                 }
16161
16162                 // fire enter events
16163                 for (i=0,len=enterEvts.length; i<len; ++i) {
16164                     // dc.b4DragEnter(e, oDD.id);
16165                     dc.onDragEnter(e, enterEvts[i].id);
16166                 }
16167
16168                 // fire over events
16169                 for (i=0,len=overEvts.length; i<len; ++i) {
16170                     dc.b4DragOver(e, overEvts[i].id);
16171                     dc.onDragOver(e, overEvts[i].id);
16172                 }
16173
16174                 // fire drop events
16175                 for (i=0, len=dropEvts.length; i<len; ++i) {
16176                     dc.b4DragDrop(e, dropEvts[i].id);
16177                     dc.onDragDrop(e, dropEvts[i].id);
16178                 }
16179
16180             }
16181
16182             // notify about a drop that did not find a target
16183             if (isDrop && !dropEvts.length) {
16184                 dc.onInvalidDrop(e);
16185             }
16186
16187         },
16188
16189         /**
16190          * Helper function for getting the best match from the list of drag
16191          * and drop objects returned by the drag and drop events when we are
16192          * in INTERSECT mode.  It returns either the first object that the
16193          * cursor is over, or the object that has the greatest overlap with
16194          * the dragged element.
16195          * @method getBestMatch
16196          * @param  {DragDrop[]} dds The array of drag and drop objects
16197          * targeted
16198          * @return {DragDrop}       The best single match
16199          * @static
16200          */
16201         getBestMatch: function(dds) {
16202             var winner = null;
16203             // Return null if the input is not what we expect
16204             //if (!dds || !dds.length || dds.length == 0) {
16205                // winner = null;
16206             // If there is only one item, it wins
16207             //} else if (dds.length == 1) {
16208
16209             var len = dds.length;
16210
16211             if (len == 1) {
16212                 winner = dds[0];
16213             } else {
16214                 // Loop through the targeted items
16215                 for (var i=0; i<len; ++i) {
16216                     var dd = dds[i];
16217                     // If the cursor is over the object, it wins.  If the
16218                     // cursor is over multiple matches, the first one we come
16219                     // to wins.
16220                     if (dd.cursorIsOver) {
16221                         winner = dd;
16222                         break;
16223                     // Otherwise the object with the most overlap wins
16224                     } else {
16225                         if (!winner ||
16226                             winner.overlap.getArea() < dd.overlap.getArea()) {
16227                             winner = dd;
16228                         }
16229                     }
16230                 }
16231             }
16232
16233             return winner;
16234         },
16235
16236         /**
16237          * Refreshes the cache of the top-left and bottom-right points of the
16238          * drag and drop objects in the specified group(s).  This is in the
16239          * format that is stored in the drag and drop instance, so typical
16240          * usage is:
16241          * <code>
16242          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16243          * </code>
16244          * Alternatively:
16245          * <code>
16246          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16247          * </code>
16248          * @TODO this really should be an indexed array.  Alternatively this
16249          * method could accept both.
16250          * @method refreshCache
16251          * @param {Object} groups an associative array of groups to refresh
16252          * @static
16253          */
16254         refreshCache: function(groups) {
16255             for (var sGroup in groups) {
16256                 if ("string" != typeof sGroup) {
16257                     continue;
16258                 }
16259                 for (var i in this.ids[sGroup]) {
16260                     var oDD = this.ids[sGroup][i];
16261
16262                     if (this.isTypeOfDD(oDD)) {
16263                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16264                         var loc = this.getLocation(oDD);
16265                         if (loc) {
16266                             this.locationCache[oDD.id] = loc;
16267                         } else {
16268                             delete this.locationCache[oDD.id];
16269                             // this will unregister the drag and drop object if
16270                             // the element is not in a usable state
16271                             // oDD.unreg();
16272                         }
16273                     }
16274                 }
16275             }
16276         },
16277
16278         /**
16279          * This checks to make sure an element exists and is in the DOM.  The
16280          * main purpose is to handle cases where innerHTML is used to remove
16281          * drag and drop objects from the DOM.  IE provides an 'unspecified
16282          * error' when trying to access the offsetParent of such an element
16283          * @method verifyEl
16284          * @param {HTMLElement} el the element to check
16285          * @return {boolean} true if the element looks usable
16286          * @static
16287          */
16288         verifyEl: function(el) {
16289             if (el) {
16290                 var parent;
16291                 if(Roo.isIE){
16292                     try{
16293                         parent = el.offsetParent;
16294                     }catch(e){}
16295                 }else{
16296                     parent = el.offsetParent;
16297                 }
16298                 if (parent) {
16299                     return true;
16300                 }
16301             }
16302
16303             return false;
16304         },
16305
16306         /**
16307          * Returns a Region object containing the drag and drop element's position
16308          * and size, including the padding configured for it
16309          * @method getLocation
16310          * @param {DragDrop} oDD the drag and drop object to get the
16311          *                       location for
16312          * @return {Roo.lib.Region} a Region object representing the total area
16313          *                             the element occupies, including any padding
16314          *                             the instance is configured for.
16315          * @static
16316          */
16317         getLocation: function(oDD) {
16318             if (! this.isTypeOfDD(oDD)) {
16319                 return null;
16320             }
16321
16322             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16323
16324             try {
16325                 pos= Roo.lib.Dom.getXY(el);
16326             } catch (e) { }
16327
16328             if (!pos) {
16329                 return null;
16330             }
16331
16332             x1 = pos[0];
16333             x2 = x1 + el.offsetWidth;
16334             y1 = pos[1];
16335             y2 = y1 + el.offsetHeight;
16336
16337             t = y1 - oDD.padding[0];
16338             r = x2 + oDD.padding[1];
16339             b = y2 + oDD.padding[2];
16340             l = x1 - oDD.padding[3];
16341
16342             return new Roo.lib.Region( t, r, b, l );
16343         },
16344
16345         /**
16346          * Checks the cursor location to see if it over the target
16347          * @method isOverTarget
16348          * @param {Roo.lib.Point} pt The point to evaluate
16349          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16350          * @return {boolean} true if the mouse is over the target
16351          * @private
16352          * @static
16353          */
16354         isOverTarget: function(pt, oTarget, intersect) {
16355             // use cache if available
16356             var loc = this.locationCache[oTarget.id];
16357             if (!loc || !this.useCache) {
16358                 loc = this.getLocation(oTarget);
16359                 this.locationCache[oTarget.id] = loc;
16360
16361             }
16362
16363             if (!loc) {
16364                 return false;
16365             }
16366
16367             oTarget.cursorIsOver = loc.contains( pt );
16368
16369             // DragDrop is using this as a sanity check for the initial mousedown
16370             // in this case we are done.  In POINT mode, if the drag obj has no
16371             // contraints, we are also done. Otherwise we need to evaluate the
16372             // location of the target as related to the actual location of the
16373             // dragged element.
16374             var dc = this.dragCurrent;
16375             if (!dc || !dc.getTargetCoord ||
16376                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16377                 return oTarget.cursorIsOver;
16378             }
16379
16380             oTarget.overlap = null;
16381
16382             // Get the current location of the drag element, this is the
16383             // location of the mouse event less the delta that represents
16384             // where the original mousedown happened on the element.  We
16385             // need to consider constraints and ticks as well.
16386             var pos = dc.getTargetCoord(pt.x, pt.y);
16387
16388             var el = dc.getDragEl();
16389             var curRegion = new Roo.lib.Region( pos.y,
16390                                                    pos.x + el.offsetWidth,
16391                                                    pos.y + el.offsetHeight,
16392                                                    pos.x );
16393
16394             var overlap = curRegion.intersect(loc);
16395
16396             if (overlap) {
16397                 oTarget.overlap = overlap;
16398                 return (intersect) ? true : oTarget.cursorIsOver;
16399             } else {
16400                 return false;
16401             }
16402         },
16403
16404         /**
16405          * unload event handler
16406          * @method _onUnload
16407          * @private
16408          * @static
16409          */
16410         _onUnload: function(e, me) {
16411             Roo.dd.DragDropMgr.unregAll();
16412         },
16413
16414         /**
16415          * Cleans up the drag and drop events and objects.
16416          * @method unregAll
16417          * @private
16418          * @static
16419          */
16420         unregAll: function() {
16421
16422             if (this.dragCurrent) {
16423                 this.stopDrag();
16424                 this.dragCurrent = null;
16425             }
16426
16427             this._execOnAll("unreg", []);
16428
16429             for (i in this.elementCache) {
16430                 delete this.elementCache[i];
16431             }
16432
16433             this.elementCache = {};
16434             this.ids = {};
16435         },
16436
16437         /**
16438          * A cache of DOM elements
16439          * @property elementCache
16440          * @private
16441          * @static
16442          */
16443         elementCache: {},
16444
16445         /**
16446          * Get the wrapper for the DOM element specified
16447          * @method getElWrapper
16448          * @param {String} id the id of the element to get
16449          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16450          * @private
16451          * @deprecated This wrapper isn't that useful
16452          * @static
16453          */
16454         getElWrapper: function(id) {
16455             var oWrapper = this.elementCache[id];
16456             if (!oWrapper || !oWrapper.el) {
16457                 oWrapper = this.elementCache[id] =
16458                     new this.ElementWrapper(Roo.getDom(id));
16459             }
16460             return oWrapper;
16461         },
16462
16463         /**
16464          * Returns the actual DOM element
16465          * @method getElement
16466          * @param {String} id the id of the elment to get
16467          * @return {Object} The element
16468          * @deprecated use Roo.getDom instead
16469          * @static
16470          */
16471         getElement: function(id) {
16472             return Roo.getDom(id);
16473         },
16474
16475         /**
16476          * Returns the style property for the DOM element (i.e.,
16477          * document.getElById(id).style)
16478          * @method getCss
16479          * @param {String} id the id of the elment to get
16480          * @return {Object} The style property of the element
16481          * @deprecated use Roo.getDom instead
16482          * @static
16483          */
16484         getCss: function(id) {
16485             var el = Roo.getDom(id);
16486             return (el) ? el.style : null;
16487         },
16488
16489         /**
16490          * Inner class for cached elements
16491          * @class DragDropMgr.ElementWrapper
16492          * @for DragDropMgr
16493          * @private
16494          * @deprecated
16495          */
16496         ElementWrapper: function(el) {
16497                 /**
16498                  * The element
16499                  * @property el
16500                  */
16501                 this.el = el || null;
16502                 /**
16503                  * The element id
16504                  * @property id
16505                  */
16506                 this.id = this.el && el.id;
16507                 /**
16508                  * A reference to the style property
16509                  * @property css
16510                  */
16511                 this.css = this.el && el.style;
16512             },
16513
16514         /**
16515          * Returns the X position of an html element
16516          * @method getPosX
16517          * @param el the element for which to get the position
16518          * @return {int} the X coordinate
16519          * @for DragDropMgr
16520          * @deprecated use Roo.lib.Dom.getX instead
16521          * @static
16522          */
16523         getPosX: function(el) {
16524             return Roo.lib.Dom.getX(el);
16525         },
16526
16527         /**
16528          * Returns the Y position of an html element
16529          * @method getPosY
16530          * @param el the element for which to get the position
16531          * @return {int} the Y coordinate
16532          * @deprecated use Roo.lib.Dom.getY instead
16533          * @static
16534          */
16535         getPosY: function(el) {
16536             return Roo.lib.Dom.getY(el);
16537         },
16538
16539         /**
16540          * Swap two nodes.  In IE, we use the native method, for others we
16541          * emulate the IE behavior
16542          * @method swapNode
16543          * @param n1 the first node to swap
16544          * @param n2 the other node to swap
16545          * @static
16546          */
16547         swapNode: function(n1, n2) {
16548             if (n1.swapNode) {
16549                 n1.swapNode(n2);
16550             } else {
16551                 var p = n2.parentNode;
16552                 var s = n2.nextSibling;
16553
16554                 if (s == n1) {
16555                     p.insertBefore(n1, n2);
16556                 } else if (n2 == n1.nextSibling) {
16557                     p.insertBefore(n2, n1);
16558                 } else {
16559                     n1.parentNode.replaceChild(n2, n1);
16560                     p.insertBefore(n1, s);
16561                 }
16562             }
16563         },
16564
16565         /**
16566          * Returns the current scroll position
16567          * @method getScroll
16568          * @private
16569          * @static
16570          */
16571         getScroll: function () {
16572             var t, l, dde=document.documentElement, db=document.body;
16573             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16574                 t = dde.scrollTop;
16575                 l = dde.scrollLeft;
16576             } else if (db) {
16577                 t = db.scrollTop;
16578                 l = db.scrollLeft;
16579             } else {
16580
16581             }
16582             return { top: t, left: l };
16583         },
16584
16585         /**
16586          * Returns the specified element style property
16587          * @method getStyle
16588          * @param {HTMLElement} el          the element
16589          * @param {string}      styleProp   the style property
16590          * @return {string} The value of the style property
16591          * @deprecated use Roo.lib.Dom.getStyle
16592          * @static
16593          */
16594         getStyle: function(el, styleProp) {
16595             return Roo.fly(el).getStyle(styleProp);
16596         },
16597
16598         /**
16599          * Gets the scrollTop
16600          * @method getScrollTop
16601          * @return {int} the document's scrollTop
16602          * @static
16603          */
16604         getScrollTop: function () { return this.getScroll().top; },
16605
16606         /**
16607          * Gets the scrollLeft
16608          * @method getScrollLeft
16609          * @return {int} the document's scrollTop
16610          * @static
16611          */
16612         getScrollLeft: function () { return this.getScroll().left; },
16613
16614         /**
16615          * Sets the x/y position of an element to the location of the
16616          * target element.
16617          * @method moveToEl
16618          * @param {HTMLElement} moveEl      The element to move
16619          * @param {HTMLElement} targetEl    The position reference element
16620          * @static
16621          */
16622         moveToEl: function (moveEl, targetEl) {
16623             var aCoord = Roo.lib.Dom.getXY(targetEl);
16624             Roo.lib.Dom.setXY(moveEl, aCoord);
16625         },
16626
16627         /**
16628          * Numeric array sort function
16629          * @method numericSort
16630          * @static
16631          */
16632         numericSort: function(a, b) { return (a - b); },
16633
16634         /**
16635          * Internal counter
16636          * @property _timeoutCount
16637          * @private
16638          * @static
16639          */
16640         _timeoutCount: 0,
16641
16642         /**
16643          * Trying to make the load order less important.  Without this we get
16644          * an error if this file is loaded before the Event Utility.
16645          * @method _addListeners
16646          * @private
16647          * @static
16648          */
16649         _addListeners: function() {
16650             var DDM = Roo.dd.DDM;
16651             if ( Roo.lib.Event && document ) {
16652                 DDM._onLoad();
16653             } else {
16654                 if (DDM._timeoutCount > 2000) {
16655                 } else {
16656                     setTimeout(DDM._addListeners, 10);
16657                     if (document && document.body) {
16658                         DDM._timeoutCount += 1;
16659                     }
16660                 }
16661             }
16662         },
16663
16664         /**
16665          * Recursively searches the immediate parent and all child nodes for
16666          * the handle element in order to determine wheter or not it was
16667          * clicked.
16668          * @method handleWasClicked
16669          * @param node the html element to inspect
16670          * @static
16671          */
16672         handleWasClicked: function(node, id) {
16673             if (this.isHandle(id, node.id)) {
16674                 return true;
16675             } else {
16676                 // check to see if this is a text node child of the one we want
16677                 var p = node.parentNode;
16678
16679                 while (p) {
16680                     if (this.isHandle(id, p.id)) {
16681                         return true;
16682                     } else {
16683                         p = p.parentNode;
16684                     }
16685                 }
16686             }
16687
16688             return false;
16689         }
16690
16691     };
16692
16693 }();
16694
16695 // shorter alias, save a few bytes
16696 Roo.dd.DDM = Roo.dd.DragDropMgr;
16697 Roo.dd.DDM._addListeners();
16698
16699 }