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      * Map (JS 1.6 compatibility)
849      * @param {Function} function  to call
850      */
851     map : function(fun /*, thisp*/)
852     {
853         var len = this.length >>> 0;
854         if (typeof fun != "function")
855             throw new TypeError();
856
857         var res = new Array(len);
858         var thisp = arguments[1];
859         for (var i = 0; i < len; i++)
860         {
861             if (i in this)
862                 res[i] = fun.call(thisp, this[i], i, this);
863         }
864
865         return res;
866     }
867     
868 });
869
870
871
872 if (!Array.prototype.map)
873 {
874   Array.prototype.
875 }/*
876  * Based on:
877  * Ext JS Library 1.1.1
878  * Copyright(c) 2006-2007, Ext JS, LLC.
879  *
880  * Originally Released Under LGPL - original licence link has changed is not relivant.
881  *
882  * Fork - LGPL
883  * <script type="text/javascript">
884  */
885
886 /**
887  * @class Date
888  *
889  * The date parsing and format syntax is a subset of
890  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
891  * supported will provide results equivalent to their PHP versions.
892  *
893  * Following is the list of all currently supported formats:
894  *<pre>
895 Sample date:
896 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
897
898 Format  Output      Description
899 ------  ----------  --------------------------------------------------------------
900   d      10         Day of the month, 2 digits with leading zeros
901   D      Wed        A textual representation of a day, three letters
902   j      10         Day of the month without leading zeros
903   l      Wednesday  A full textual representation of the day of the week
904   S      th         English ordinal day of month suffix, 2 chars (use with j)
905   w      3          Numeric representation of the day of the week
906   z      9          The julian date, or day of the year (0-365)
907   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
908   F      January    A full textual representation of the month
909   m      01         Numeric representation of a month, with leading zeros
910   M      Jan        Month name abbreviation, three letters
911   n      1          Numeric representation of a month, without leading zeros
912   t      31         Number of days in the given month
913   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
914   Y      2007       A full numeric representation of a year, 4 digits
915   y      07         A two digit representation of a year
916   a      pm         Lowercase Ante meridiem and Post meridiem
917   A      PM         Uppercase Ante meridiem and Post meridiem
918   g      3          12-hour format of an hour without leading zeros
919   G      15         24-hour format of an hour without leading zeros
920   h      03         12-hour format of an hour with leading zeros
921   H      15         24-hour format of an hour with leading zeros
922   i      05         Minutes with leading zeros
923   s      01         Seconds, with leading zeros
924   O      -0600      Difference to Greenwich time (GMT) in hours
925   T      CST        Timezone setting of the machine running the code
926   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
927 </pre>
928  *
929  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
930  * <pre><code>
931 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
932 document.write(dt.format('Y-m-d'));                         //2007-01-10
933 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
934 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
935  </code></pre>
936  *
937  * Here are some standard date/time patterns that you might find helpful.  They
938  * are not part of the source of Date.js, but to use them you can simply copy this
939  * block of code into any script that is included after Date.js and they will also become
940  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
941  * <pre><code>
942 Date.patterns = {
943     ISO8601Long:"Y-m-d H:i:s",
944     ISO8601Short:"Y-m-d",
945     ShortDate: "n/j/Y",
946     LongDate: "l, F d, Y",
947     FullDateTime: "l, F d, Y g:i:s A",
948     MonthDay: "F d",
949     ShortTime: "g:i A",
950     LongTime: "g:i:s A",
951     SortableDateTime: "Y-m-d\\TH:i:s",
952     UniversalSortableDateTime: "Y-m-d H:i:sO",
953     YearMonth: "F, Y"
954 };
955 </code></pre>
956  *
957  * Example usage:
958  * <pre><code>
959 var dt = new Date();
960 document.write(dt.format(Date.patterns.ShortDate));
961  </code></pre>
962  */
963
964 /*
965  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
966  * They generate precompiled functions from date formats instead of parsing and
967  * processing the pattern every time you format a date.  These functions are available
968  * on every Date object (any javascript function).
969  *
970  * The original article and download are here:
971  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
972  *
973  */
974  
975  
976  // was in core
977 /**
978  Returns the number of milliseconds between this date and date
979  @param {Date} date (optional) Defaults to now
980  @return {Number} The diff in milliseconds
981  @member Date getElapsed
982  */
983 Date.prototype.getElapsed = function(date) {
984         return Math.abs((date || new Date()).getTime()-this.getTime());
985 };
986 // was in date file..
987
988
989 // private
990 Date.parseFunctions = {count:0};
991 // private
992 Date.parseRegexes = [];
993 // private
994 Date.formatFunctions = {count:0};
995
996 // private
997 Date.prototype.dateFormat = function(format) {
998     if (Date.formatFunctions[format] == null) {
999         Date.createNewFormat(format);
1000     }
1001     var func = Date.formatFunctions[format];
1002     return this[func]();
1003 };
1004
1005
1006 /**
1007  * Formats a date given the supplied format string
1008  * @param {String} format The format string
1009  * @return {String} The formatted date
1010  * @method
1011  */
1012 Date.prototype.format = Date.prototype.dateFormat;
1013
1014 // private
1015 Date.createNewFormat = function(format) {
1016     var funcName = "format" + Date.formatFunctions.count++;
1017     Date.formatFunctions[format] = funcName;
1018     var code = "Date.prototype." + funcName + " = function(){return ";
1019     var special = false;
1020     var ch = '';
1021     for (var i = 0; i < format.length; ++i) {
1022         ch = format.charAt(i);
1023         if (!special && ch == "\\") {
1024             special = true;
1025         }
1026         else if (special) {
1027             special = false;
1028             code += "'" + String.escape(ch) + "' + ";
1029         }
1030         else {
1031             code += Date.getFormatCode(ch);
1032         }
1033     }
1034     /** eval:var:zzzzzzzzzzzzz */
1035     eval(code.substring(0, code.length - 3) + ";}");
1036 };
1037
1038 // private
1039 Date.getFormatCode = function(character) {
1040     switch (character) {
1041     case "d":
1042         return "String.leftPad(this.getDate(), 2, '0') + ";
1043     case "D":
1044         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1045     case "j":
1046         return "this.getDate() + ";
1047     case "l":
1048         return "Date.dayNames[this.getDay()] + ";
1049     case "S":
1050         return "this.getSuffix() + ";
1051     case "w":
1052         return "this.getDay() + ";
1053     case "z":
1054         return "this.getDayOfYear() + ";
1055     case "W":
1056         return "this.getWeekOfYear() + ";
1057     case "F":
1058         return "Date.monthNames[this.getMonth()] + ";
1059     case "m":
1060         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1061     case "M":
1062         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1063     case "n":
1064         return "(this.getMonth() + 1) + ";
1065     case "t":
1066         return "this.getDaysInMonth() + ";
1067     case "L":
1068         return "(this.isLeapYear() ? 1 : 0) + ";
1069     case "Y":
1070         return "this.getFullYear() + ";
1071     case "y":
1072         return "('' + this.getFullYear()).substring(2, 4) + ";
1073     case "a":
1074         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1075     case "A":
1076         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1077     case "g":
1078         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1079     case "G":
1080         return "this.getHours() + ";
1081     case "h":
1082         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1083     case "H":
1084         return "String.leftPad(this.getHours(), 2, '0') + ";
1085     case "i":
1086         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1087     case "s":
1088         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1089     case "O":
1090         return "this.getGMTOffset() + ";
1091     case "T":
1092         return "this.getTimezone() + ";
1093     case "Z":
1094         return "(this.getTimezoneOffset() * -60) + ";
1095     default:
1096         return "'" + String.escape(character) + "' + ";
1097     }
1098 };
1099
1100 /**
1101  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1102  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1103  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1104  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1105  * string or the parse operation will fail.
1106  * Example Usage:
1107 <pre><code>
1108 //dt = Fri May 25 2007 (current date)
1109 var dt = new Date();
1110
1111 //dt = Thu May 25 2006 (today's month/day in 2006)
1112 dt = Date.parseDate("2006", "Y");
1113
1114 //dt = Sun Jan 15 2006 (all date parts specified)
1115 dt = Date.parseDate("2006-1-15", "Y-m-d");
1116
1117 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1118 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1119 </code></pre>
1120  * @param {String} input The unparsed date as a string
1121  * @param {String} format The format the date is in
1122  * @return {Date} The parsed date
1123  * @static
1124  */
1125 Date.parseDate = function(input, format) {
1126     if (Date.parseFunctions[format] == null) {
1127         Date.createParser(format);
1128     }
1129     var func = Date.parseFunctions[format];
1130     return Date[func](input);
1131 };
1132 /**
1133  * @private
1134  */
1135 Date.createParser = function(format) {
1136     var funcName = "parse" + Date.parseFunctions.count++;
1137     var regexNum = Date.parseRegexes.length;
1138     var currentGroup = 1;
1139     Date.parseFunctions[format] = funcName;
1140
1141     var code = "Date." + funcName + " = function(input){\n"
1142         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1143         + "var d = new Date();\n"
1144         + "y = d.getFullYear();\n"
1145         + "m = d.getMonth();\n"
1146         + "d = d.getDate();\n"
1147         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1148         + "if (results && results.length > 0) {";
1149     var regex = "";
1150
1151     var special = false;
1152     var ch = '';
1153     for (var i = 0; i < format.length; ++i) {
1154         ch = format.charAt(i);
1155         if (!special && ch == "\\") {
1156             special = true;
1157         }
1158         else if (special) {
1159             special = false;
1160             regex += String.escape(ch);
1161         }
1162         else {
1163             var obj = Date.formatCodeToRegex(ch, currentGroup);
1164             currentGroup += obj.g;
1165             regex += obj.s;
1166             if (obj.g && obj.c) {
1167                 code += obj.c;
1168             }
1169         }
1170     }
1171
1172     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1173         + "{v = new Date(y, m, d, h, i, s);}\n"
1174         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1175         + "{v = new Date(y, m, d, h, i);}\n"
1176         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1177         + "{v = new Date(y, m, d, h);}\n"
1178         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1179         + "{v = new Date(y, m, d);}\n"
1180         + "else if (y >= 0 && m >= 0)\n"
1181         + "{v = new Date(y, m);}\n"
1182         + "else if (y >= 0)\n"
1183         + "{v = new Date(y);}\n"
1184         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1185         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1186         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1187         + ";}";
1188
1189     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1190     /** eval:var:zzzzzzzzzzzzz */
1191     eval(code);
1192 };
1193
1194 // private
1195 Date.formatCodeToRegex = function(character, currentGroup) {
1196     switch (character) {
1197     case "D":
1198         return {g:0,
1199         c:null,
1200         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1201     case "j":
1202         return {g:1,
1203             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1204             s:"(\\d{1,2})"}; // day of month without leading zeroes
1205     case "d":
1206         return {g:1,
1207             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1208             s:"(\\d{2})"}; // day of month with leading zeroes
1209     case "l":
1210         return {g:0,
1211             c:null,
1212             s:"(?:" + Date.dayNames.join("|") + ")"};
1213     case "S":
1214         return {g:0,
1215             c:null,
1216             s:"(?:st|nd|rd|th)"};
1217     case "w":
1218         return {g:0,
1219             c:null,
1220             s:"\\d"};
1221     case "z":
1222         return {g:0,
1223             c:null,
1224             s:"(?:\\d{1,3})"};
1225     case "W":
1226         return {g:0,
1227             c:null,
1228             s:"(?:\\d{2})"};
1229     case "F":
1230         return {g:1,
1231             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1232             s:"(" + Date.monthNames.join("|") + ")"};
1233     case "M":
1234         return {g:1,
1235             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1236             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1237     case "n":
1238         return {g:1,
1239             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1240             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1241     case "m":
1242         return {g:1,
1243             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1244             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1245     case "t":
1246         return {g:0,
1247             c:null,
1248             s:"\\d{1,2}"};
1249     case "L":
1250         return {g:0,
1251             c:null,
1252             s:"(?:1|0)"};
1253     case "Y":
1254         return {g:1,
1255             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1256             s:"(\\d{4})"};
1257     case "y":
1258         return {g:1,
1259             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1260                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1261             s:"(\\d{1,2})"};
1262     case "a":
1263         return {g:1,
1264             c:"if (results[" + currentGroup + "] == 'am') {\n"
1265                 + "if (h == 12) { h = 0; }\n"
1266                 + "} else { if (h < 12) { h += 12; }}",
1267             s:"(am|pm)"};
1268     case "A":
1269         return {g:1,
1270             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1271                 + "if (h == 12) { h = 0; }\n"
1272                 + "} else { if (h < 12) { h += 12; }}",
1273             s:"(AM|PM)"};
1274     case "g":
1275     case "G":
1276         return {g:1,
1277             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1278             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1279     case "h":
1280     case "H":
1281         return {g:1,
1282             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1283             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1284     case "i":
1285         return {g:1,
1286             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{2})"};
1288     case "s":
1289         return {g:1,
1290             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1291             s:"(\\d{2})"};
1292     case "O":
1293         return {g:1,
1294             c:[
1295                 "o = results[", currentGroup, "];\n",
1296                 "var sn = o.substring(0,1);\n", // get + / - sign
1297                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1298                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1299                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1300                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1301             ].join(""),
1302             s:"([+\-]\\d{4})"};
1303     case "T":
1304         return {g:0,
1305             c:null,
1306             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1307     case "Z":
1308         return {g:1,
1309             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1310                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1311             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1312     default:
1313         return {g:0,
1314             c:null,
1315             s:String.escape(character)};
1316     }
1317 };
1318
1319 /**
1320  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1321  * @return {String} The abbreviated timezone name (e.g. 'CST')
1322  */
1323 Date.prototype.getTimezone = function() {
1324     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1325 };
1326
1327 /**
1328  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1329  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1330  */
1331 Date.prototype.getGMTOffset = function() {
1332     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1333         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1334         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1335 };
1336
1337 /**
1338  * Get the numeric day number of the year, adjusted for leap year.
1339  * @return {Number} 0 through 364 (365 in leap years)
1340  */
1341 Date.prototype.getDayOfYear = function() {
1342     var num = 0;
1343     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1344     for (var i = 0; i < this.getMonth(); ++i) {
1345         num += Date.daysInMonth[i];
1346     }
1347     return num + this.getDate() - 1;
1348 };
1349
1350 /**
1351  * Get the string representation of the numeric week number of the year
1352  * (equivalent to the format specifier 'W').
1353  * @return {String} '00' through '52'
1354  */
1355 Date.prototype.getWeekOfYear = function() {
1356     // Skip to Thursday of this week
1357     var now = this.getDayOfYear() + (4 - this.getDay());
1358     // Find the first Thursday of the year
1359     var jan1 = new Date(this.getFullYear(), 0, 1);
1360     var then = (7 - jan1.getDay() + 4);
1361     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1362 };
1363
1364 /**
1365  * Whether or not the current date is in a leap year.
1366  * @return {Boolean} True if the current date is in a leap year, else false
1367  */
1368 Date.prototype.isLeapYear = function() {
1369     var year = this.getFullYear();
1370     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1371 };
1372
1373 /**
1374  * Get the first day of the current month, adjusted for leap year.  The returned value
1375  * is the numeric day index within the week (0-6) which can be used in conjunction with
1376  * the {@link #monthNames} array to retrieve the textual day name.
1377  * Example:
1378  *<pre><code>
1379 var dt = new Date('1/10/2007');
1380 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1381 </code></pre>
1382  * @return {Number} The day number (0-6)
1383  */
1384 Date.prototype.getFirstDayOfMonth = function() {
1385     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1386     return (day < 0) ? (day + 7) : day;
1387 };
1388
1389 /**
1390  * Get the last day of the current month, adjusted for leap year.  The returned value
1391  * is the numeric day index within the week (0-6) which can be used in conjunction with
1392  * the {@link #monthNames} array to retrieve the textual day name.
1393  * Example:
1394  *<pre><code>
1395 var dt = new Date('1/10/2007');
1396 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1397 </code></pre>
1398  * @return {Number} The day number (0-6)
1399  */
1400 Date.prototype.getLastDayOfMonth = function() {
1401     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1402     return (day < 0) ? (day + 7) : day;
1403 };
1404
1405
1406 /**
1407  * Get the first date of this date's month
1408  * @return {Date}
1409  */
1410 Date.prototype.getFirstDateOfMonth = function() {
1411     return new Date(this.getFullYear(), this.getMonth(), 1);
1412 };
1413
1414 /**
1415  * Get the last date of this date's month
1416  * @return {Date}
1417  */
1418 Date.prototype.getLastDateOfMonth = function() {
1419     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1420 };
1421 /**
1422  * Get the number of days in the current month, adjusted for leap year.
1423  * @return {Number} The number of days in the month
1424  */
1425 Date.prototype.getDaysInMonth = function() {
1426     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1427     return Date.daysInMonth[this.getMonth()];
1428 };
1429
1430 /**
1431  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1432  * @return {String} 'st, 'nd', 'rd' or 'th'
1433  */
1434 Date.prototype.getSuffix = function() {
1435     switch (this.getDate()) {
1436         case 1:
1437         case 21:
1438         case 31:
1439             return "st";
1440         case 2:
1441         case 22:
1442             return "nd";
1443         case 3:
1444         case 23:
1445             return "rd";
1446         default:
1447             return "th";
1448     }
1449 };
1450
1451 // private
1452 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1453
1454 /**
1455  * An array of textual month names.
1456  * Override these values for international dates, for example...
1457  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1458  * @type Array
1459  * @static
1460  */
1461 Date.monthNames =
1462    ["January",
1463     "February",
1464     "March",
1465     "April",
1466     "May",
1467     "June",
1468     "July",
1469     "August",
1470     "September",
1471     "October",
1472     "November",
1473     "December"];
1474
1475 /**
1476  * An array of textual day names.
1477  * Override these values for international dates, for example...
1478  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1479  * @type Array
1480  * @static
1481  */
1482 Date.dayNames =
1483    ["Sunday",
1484     "Monday",
1485     "Tuesday",
1486     "Wednesday",
1487     "Thursday",
1488     "Friday",
1489     "Saturday"];
1490
1491 // private
1492 Date.y2kYear = 50;
1493 // private
1494 Date.monthNumbers = {
1495     Jan:0,
1496     Feb:1,
1497     Mar:2,
1498     Apr:3,
1499     May:4,
1500     Jun:5,
1501     Jul:6,
1502     Aug:7,
1503     Sep:8,
1504     Oct:9,
1505     Nov:10,
1506     Dec:11};
1507
1508 /**
1509  * Creates and returns a new Date instance with the exact same date value as the called instance.
1510  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1511  * variable will also be changed.  When the intention is to create a new variable that will not
1512  * modify the original instance, you should create a clone.
1513  *
1514  * Example of correctly cloning a date:
1515  * <pre><code>
1516 //wrong way:
1517 var orig = new Date('10/1/2006');
1518 var copy = orig;
1519 copy.setDate(5);
1520 document.write(orig);  //returns 'Thu Oct 05 2006'!
1521
1522 //correct way:
1523 var orig = new Date('10/1/2006');
1524 var copy = orig.clone();
1525 copy.setDate(5);
1526 document.write(orig);  //returns 'Thu Oct 01 2006'
1527 </code></pre>
1528  * @return {Date} The new Date instance
1529  */
1530 Date.prototype.clone = function() {
1531         return new Date(this.getTime());
1532 };
1533
1534 /**
1535  * Clears any time information from this date
1536  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1537  @return {Date} this or the clone
1538  */
1539 Date.prototype.clearTime = function(clone){
1540     if(clone){
1541         return this.clone().clearTime();
1542     }
1543     this.setHours(0);
1544     this.setMinutes(0);
1545     this.setSeconds(0);
1546     this.setMilliseconds(0);
1547     return this;
1548 };
1549
1550 // private
1551 // safari setMonth is broken
1552 if(Roo.isSafari){
1553     Date.brokenSetMonth = Date.prototype.setMonth;
1554         Date.prototype.setMonth = function(num){
1555                 if(num <= -1){
1556                         var n = Math.ceil(-num);
1557                         var back_year = Math.ceil(n/12);
1558                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1559                         this.setFullYear(this.getFullYear() - back_year);
1560                         return Date.brokenSetMonth.call(this, month);
1561                 } else {
1562                         return Date.brokenSetMonth.apply(this, arguments);
1563                 }
1564         };
1565 }
1566
1567 /** Date interval constant 
1568 * @static 
1569 * @type String */
1570 Date.MILLI = "ms";
1571 /** Date interval constant 
1572 * @static 
1573 * @type String */
1574 Date.SECOND = "s";
1575 /** Date interval constant 
1576 * @static 
1577 * @type String */
1578 Date.MINUTE = "mi";
1579 /** Date interval constant 
1580 * @static 
1581 * @type String */
1582 Date.HOUR = "h";
1583 /** Date interval constant 
1584 * @static 
1585 * @type String */
1586 Date.DAY = "d";
1587 /** Date interval constant 
1588 * @static 
1589 * @type String */
1590 Date.MONTH = "mo";
1591 /** Date interval constant 
1592 * @static 
1593 * @type String */
1594 Date.YEAR = "y";
1595
1596 /**
1597  * Provides a convenient method of performing basic date arithmetic.  This method
1598  * does not modify the Date instance being called - it creates and returns
1599  * a new Date instance containing the resulting date value.
1600  *
1601  * Examples:
1602  * <pre><code>
1603 //Basic usage:
1604 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1605 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1606
1607 //Negative values will subtract correctly:
1608 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1609 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1610
1611 //You can even chain several calls together in one line!
1612 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1613 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1614  </code></pre>
1615  *
1616  * @param {String} interval   A valid date interval enum value
1617  * @param {Number} value      The amount to add to the current date
1618  * @return {Date} The new Date instance
1619  */
1620 Date.prototype.add = function(interval, value){
1621   var d = this.clone();
1622   if (!interval || value === 0) return d;
1623   switch(interval.toLowerCase()){
1624     case Date.MILLI:
1625       d.setMilliseconds(this.getMilliseconds() + value);
1626       break;
1627     case Date.SECOND:
1628       d.setSeconds(this.getSeconds() + value);
1629       break;
1630     case Date.MINUTE:
1631       d.setMinutes(this.getMinutes() + value);
1632       break;
1633     case Date.HOUR:
1634       d.setHours(this.getHours() + value);
1635       break;
1636     case Date.DAY:
1637       d.setDate(this.getDate() + value);
1638       break;
1639     case Date.MONTH:
1640       var day = this.getDate();
1641       if(day > 28){
1642           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1643       }
1644       d.setDate(day);
1645       d.setMonth(this.getMonth() + value);
1646       break;
1647     case Date.YEAR:
1648       d.setFullYear(this.getFullYear() + value);
1649       break;
1650   }
1651   return d;
1652 };/*
1653  * Based on:
1654  * Ext JS Library 1.1.1
1655  * Copyright(c) 2006-2007, Ext JS, LLC.
1656  *
1657  * Originally Released Under LGPL - original licence link has changed is not relivant.
1658  *
1659  * Fork - LGPL
1660  * <script type="text/javascript">
1661  */
1662
1663 Roo.lib.Dom = {
1664     getViewWidth : function(full) {
1665         return full ? this.getDocumentWidth() : this.getViewportWidth();
1666     },
1667
1668     getViewHeight : function(full) {
1669         return full ? this.getDocumentHeight() : this.getViewportHeight();
1670     },
1671
1672     getDocumentHeight: function() {
1673         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1674         return Math.max(scrollHeight, this.getViewportHeight());
1675     },
1676
1677     getDocumentWidth: function() {
1678         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1679         return Math.max(scrollWidth, this.getViewportWidth());
1680     },
1681
1682     getViewportHeight: function() {
1683         var height = self.innerHeight;
1684         var mode = document.compatMode;
1685
1686         if ((mode || Roo.isIE) && !Roo.isOpera) {
1687             height = (mode == "CSS1Compat") ?
1688                      document.documentElement.clientHeight :
1689                      document.body.clientHeight;
1690         }
1691
1692         return height;
1693     },
1694
1695     getViewportWidth: function() {
1696         var width = self.innerWidth;
1697         var mode = document.compatMode;
1698
1699         if (mode || Roo.isIE) {
1700             width = (mode == "CSS1Compat") ?
1701                     document.documentElement.clientWidth :
1702                     document.body.clientWidth;
1703         }
1704         return width;
1705     },
1706
1707     isAncestor : function(p, c) {
1708         p = Roo.getDom(p);
1709         c = Roo.getDom(c);
1710         if (!p || !c) {
1711             return false;
1712         }
1713
1714         if (p.contains && !Roo.isSafari) {
1715             return p.contains(c);
1716         } else if (p.compareDocumentPosition) {
1717             return !!(p.compareDocumentPosition(c) & 16);
1718         } else {
1719             var parent = c.parentNode;
1720             while (parent) {
1721                 if (parent == p) {
1722                     return true;
1723                 }
1724                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1725                     return false;
1726                 }
1727                 parent = parent.parentNode;
1728             }
1729             return false;
1730         }
1731     },
1732
1733     getRegion : function(el) {
1734         return Roo.lib.Region.getRegion(el);
1735     },
1736
1737     getY : function(el) {
1738         return this.getXY(el)[1];
1739     },
1740
1741     getX : function(el) {
1742         return this.getXY(el)[0];
1743     },
1744
1745     getXY : function(el) {
1746         var p, pe, b, scroll, bd = document.body;
1747         el = Roo.getDom(el);
1748         var fly = Roo.lib.AnimBase.fly;
1749         if (el.getBoundingClientRect) {
1750             b = el.getBoundingClientRect();
1751             scroll = fly(document).getScroll();
1752             return [b.left + scroll.left, b.top + scroll.top];
1753         }
1754         var x = 0, y = 0;
1755
1756         p = el;
1757
1758         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1759
1760         while (p) {
1761
1762             x += p.offsetLeft;
1763             y += p.offsetTop;
1764
1765             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1766                 hasAbsolute = true;
1767             }
1768
1769             if (Roo.isGecko) {
1770                 pe = fly(p);
1771
1772                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1773                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1774
1775
1776                 x += bl;
1777                 y += bt;
1778
1779
1780                 if (p != el && pe.getStyle('overflow') != 'visible') {
1781                     x += bl;
1782                     y += bt;
1783                 }
1784             }
1785             p = p.offsetParent;
1786         }
1787
1788         if (Roo.isSafari && hasAbsolute) {
1789             x -= bd.offsetLeft;
1790             y -= bd.offsetTop;
1791         }
1792
1793         if (Roo.isGecko && !hasAbsolute) {
1794             var dbd = fly(bd);
1795             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1796             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1797         }
1798
1799         p = el.parentNode;
1800         while (p && p != bd) {
1801             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1802                 x -= p.scrollLeft;
1803                 y -= p.scrollTop;
1804             }
1805             p = p.parentNode;
1806         }
1807         return [x, y];
1808     },
1809  
1810   
1811
1812
1813     setXY : function(el, xy) {
1814         el = Roo.fly(el, '_setXY');
1815         el.position();
1816         var pts = el.translatePoints(xy);
1817         if (xy[0] !== false) {
1818             el.dom.style.left = pts.left + "px";
1819         }
1820         if (xy[1] !== false) {
1821             el.dom.style.top = pts.top + "px";
1822         }
1823     },
1824
1825     setX : function(el, x) {
1826         this.setXY(el, [x, false]);
1827     },
1828
1829     setY : function(el, y) {
1830         this.setXY(el, [false, y]);
1831     }
1832 };
1833 /*
1834  * Portions of this file are based on pieces of Yahoo User Interface Library
1835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1836  * YUI licensed under the BSD License:
1837  * http://developer.yahoo.net/yui/license.txt
1838  * <script type="text/javascript">
1839  *
1840  */
1841
1842 Roo.lib.Event = function() {
1843     var loadComplete = false;
1844     var listeners = [];
1845     var unloadListeners = [];
1846     var retryCount = 0;
1847     var onAvailStack = [];
1848     var counter = 0;
1849     var lastError = null;
1850
1851     return {
1852         POLL_RETRYS: 200,
1853         POLL_INTERVAL: 20,
1854         EL: 0,
1855         TYPE: 1,
1856         FN: 2,
1857         WFN: 3,
1858         OBJ: 3,
1859         ADJ_SCOPE: 4,
1860         _interval: null,
1861
1862         startInterval: function() {
1863             if (!this._interval) {
1864                 var self = this;
1865                 var callback = function() {
1866                     self._tryPreloadAttach();
1867                 };
1868                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1869
1870             }
1871         },
1872
1873         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1874             onAvailStack.push({ id:         p_id,
1875                 fn:         p_fn,
1876                 obj:        p_obj,
1877                 override:   p_override,
1878                 checkReady: false    });
1879
1880             retryCount = this.POLL_RETRYS;
1881             this.startInterval();
1882         },
1883
1884
1885         addListener: function(el, eventName, fn) {
1886             el = Roo.getDom(el);
1887             if (!el || !fn) {
1888                 return false;
1889             }
1890
1891             if ("unload" == eventName) {
1892                 unloadListeners[unloadListeners.length] =
1893                 [el, eventName, fn];
1894                 return true;
1895             }
1896
1897             var wrappedFn = function(e) {
1898                 return fn(Roo.lib.Event.getEvent(e));
1899             };
1900
1901             var li = [el, eventName, fn, wrappedFn];
1902
1903             var index = listeners.length;
1904             listeners[index] = li;
1905
1906             this.doAdd(el, eventName, wrappedFn, false);
1907             return true;
1908
1909         },
1910
1911
1912         removeListener: function(el, eventName, fn) {
1913             var i, len;
1914
1915             el = Roo.getDom(el);
1916
1917             if(!fn) {
1918                 return this.purgeElement(el, false, eventName);
1919             }
1920
1921
1922             if ("unload" == eventName) {
1923
1924                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1925                     var li = unloadListeners[i];
1926                     if (li &&
1927                         li[0] == el &&
1928                         li[1] == eventName &&
1929                         li[2] == fn) {
1930                         unloadListeners.splice(i, 1);
1931                         return true;
1932                     }
1933                 }
1934
1935                 return false;
1936             }
1937
1938             var cacheItem = null;
1939
1940
1941             var index = arguments[3];
1942
1943             if ("undefined" == typeof index) {
1944                 index = this._getCacheIndex(el, eventName, fn);
1945             }
1946
1947             if (index >= 0) {
1948                 cacheItem = listeners[index];
1949             }
1950
1951             if (!el || !cacheItem) {
1952                 return false;
1953             }
1954
1955             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1956
1957             delete listeners[index][this.WFN];
1958             delete listeners[index][this.FN];
1959             listeners.splice(index, 1);
1960
1961             return true;
1962
1963         },
1964
1965
1966         getTarget: function(ev, resolveTextNode) {
1967             ev = ev.browserEvent || ev;
1968             var t = ev.target || ev.srcElement;
1969             return this.resolveTextNode(t);
1970         },
1971
1972
1973         resolveTextNode: function(node) {
1974             if (Roo.isSafari && node && 3 == node.nodeType) {
1975                 return node.parentNode;
1976             } else {
1977                 return node;
1978             }
1979         },
1980
1981
1982         getPageX: function(ev) {
1983             ev = ev.browserEvent || ev;
1984             var x = ev.pageX;
1985             if (!x && 0 !== x) {
1986                 x = ev.clientX || 0;
1987
1988                 if (Roo.isIE) {
1989                     x += this.getScroll()[1];
1990                 }
1991             }
1992
1993             return x;
1994         },
1995
1996
1997         getPageY: function(ev) {
1998             ev = ev.browserEvent || ev;
1999             var y = ev.pageY;
2000             if (!y && 0 !== y) {
2001                 y = ev.clientY || 0;
2002
2003                 if (Roo.isIE) {
2004                     y += this.getScroll()[0];
2005                 }
2006             }
2007
2008
2009             return y;
2010         },
2011
2012
2013         getXY: function(ev) {
2014             ev = ev.browserEvent || ev;
2015             return [this.getPageX(ev), this.getPageY(ev)];
2016         },
2017
2018
2019         getRelatedTarget: function(ev) {
2020             ev = ev.browserEvent || ev;
2021             var t = ev.relatedTarget;
2022             if (!t) {
2023                 if (ev.type == "mouseout") {
2024                     t = ev.toElement;
2025                 } else if (ev.type == "mouseover") {
2026                     t = ev.fromElement;
2027                 }
2028             }
2029
2030             return this.resolveTextNode(t);
2031         },
2032
2033
2034         getTime: function(ev) {
2035             ev = ev.browserEvent || ev;
2036             if (!ev.time) {
2037                 var t = new Date().getTime();
2038                 try {
2039                     ev.time = t;
2040                 } catch(ex) {
2041                     this.lastError = ex;
2042                     return t;
2043                 }
2044             }
2045
2046             return ev.time;
2047         },
2048
2049
2050         stopEvent: function(ev) {
2051             this.stopPropagation(ev);
2052             this.preventDefault(ev);
2053         },
2054
2055
2056         stopPropagation: function(ev) {
2057             ev = ev.browserEvent || ev;
2058             if (ev.stopPropagation) {
2059                 ev.stopPropagation();
2060             } else {
2061                 ev.cancelBubble = true;
2062             }
2063         },
2064
2065
2066         preventDefault: function(ev) {
2067             ev = ev.browserEvent || ev;
2068             if(ev.preventDefault) {
2069                 ev.preventDefault();
2070             } else {
2071                 ev.returnValue = false;
2072             }
2073         },
2074
2075
2076         getEvent: function(e) {
2077             var ev = e || window.event;
2078             if (!ev) {
2079                 var c = this.getEvent.caller;
2080                 while (c) {
2081                     ev = c.arguments[0];
2082                     if (ev && Event == ev.constructor) {
2083                         break;
2084                     }
2085                     c = c.caller;
2086                 }
2087             }
2088             return ev;
2089         },
2090
2091
2092         getCharCode: function(ev) {
2093             ev = ev.browserEvent || ev;
2094             return ev.charCode || ev.keyCode || 0;
2095         },
2096
2097
2098         _getCacheIndex: function(el, eventName, fn) {
2099             for (var i = 0,len = listeners.length; i < len; ++i) {
2100                 var li = listeners[i];
2101                 if (li &&
2102                     li[this.FN] == fn &&
2103                     li[this.EL] == el &&
2104                     li[this.TYPE] == eventName) {
2105                     return i;
2106                 }
2107             }
2108
2109             return -1;
2110         },
2111
2112
2113         elCache: {},
2114
2115
2116         getEl: function(id) {
2117             return document.getElementById(id);
2118         },
2119
2120
2121         clearCache: function() {
2122         },
2123
2124
2125         _load: function(e) {
2126             loadComplete = true;
2127             var EU = Roo.lib.Event;
2128
2129
2130             if (Roo.isIE) {
2131                 EU.doRemove(window, "load", EU._load);
2132             }
2133         },
2134
2135
2136         _tryPreloadAttach: function() {
2137
2138             if (this.locked) {
2139                 return false;
2140             }
2141
2142             this.locked = true;
2143
2144
2145             var tryAgain = !loadComplete;
2146             if (!tryAgain) {
2147                 tryAgain = (retryCount > 0);
2148             }
2149
2150
2151             var notAvail = [];
2152             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2153                 var item = onAvailStack[i];
2154                 if (item) {
2155                     var el = this.getEl(item.id);
2156
2157                     if (el) {
2158                         if (!item.checkReady ||
2159                             loadComplete ||
2160                             el.nextSibling ||
2161                             (document && document.body)) {
2162
2163                             var scope = el;
2164                             if (item.override) {
2165                                 if (item.override === true) {
2166                                     scope = item.obj;
2167                                 } else {
2168                                     scope = item.override;
2169                                 }
2170                             }
2171                             item.fn.call(scope, item.obj);
2172                             onAvailStack[i] = null;
2173                         }
2174                     } else {
2175                         notAvail.push(item);
2176                     }
2177                 }
2178             }
2179
2180             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2181
2182             if (tryAgain) {
2183
2184                 this.startInterval();
2185             } else {
2186                 clearInterval(this._interval);
2187                 this._interval = null;
2188             }
2189
2190             this.locked = false;
2191
2192             return true;
2193
2194         },
2195
2196
2197         purgeElement: function(el, recurse, eventName) {
2198             var elListeners = this.getListeners(el, eventName);
2199             if (elListeners) {
2200                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2201                     var l = elListeners[i];
2202                     this.removeListener(el, l.type, l.fn);
2203                 }
2204             }
2205
2206             if (recurse && el && el.childNodes) {
2207                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2208                     this.purgeElement(el.childNodes[i], recurse, eventName);
2209                 }
2210             }
2211         },
2212
2213
2214         getListeners: function(el, eventName) {
2215             var results = [], searchLists;
2216             if (!eventName) {
2217                 searchLists = [listeners, unloadListeners];
2218             } else if (eventName == "unload") {
2219                 searchLists = [unloadListeners];
2220             } else {
2221                 searchLists = [listeners];
2222             }
2223
2224             for (var j = 0; j < searchLists.length; ++j) {
2225                 var searchList = searchLists[j];
2226                 if (searchList && searchList.length > 0) {
2227                     for (var i = 0,len = searchList.length; i < len; ++i) {
2228                         var l = searchList[i];
2229                         if (l && l[this.EL] === el &&
2230                             (!eventName || eventName === l[this.TYPE])) {
2231                             results.push({
2232                                 type:   l[this.TYPE],
2233                                 fn:     l[this.FN],
2234                                 obj:    l[this.OBJ],
2235                                 adjust: l[this.ADJ_SCOPE],
2236                                 index:  i
2237                             });
2238                         }
2239                     }
2240                 }
2241             }
2242
2243             return (results.length) ? results : null;
2244         },
2245
2246
2247         _unload: function(e) {
2248
2249             var EU = Roo.lib.Event, i, j, l, len, index;
2250
2251             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2252                 l = unloadListeners[i];
2253                 if (l) {
2254                     var scope = window;
2255                     if (l[EU.ADJ_SCOPE]) {
2256                         if (l[EU.ADJ_SCOPE] === true) {
2257                             scope = l[EU.OBJ];
2258                         } else {
2259                             scope = l[EU.ADJ_SCOPE];
2260                         }
2261                     }
2262                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2263                     unloadListeners[i] = null;
2264                     l = null;
2265                     scope = null;
2266                 }
2267             }
2268
2269             unloadListeners = null;
2270
2271             if (listeners && listeners.length > 0) {
2272                 j = listeners.length;
2273                 while (j) {
2274                     index = j - 1;
2275                     l = listeners[index];
2276                     if (l) {
2277                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2278                                 l[EU.FN], index);
2279                     }
2280                     j = j - 1;
2281                 }
2282                 l = null;
2283
2284                 EU.clearCache();
2285             }
2286
2287             EU.doRemove(window, "unload", EU._unload);
2288
2289         },
2290
2291
2292         getScroll: function() {
2293             var dd = document.documentElement, db = document.body;
2294             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2295                 return [dd.scrollTop, dd.scrollLeft];
2296             } else if (db) {
2297                 return [db.scrollTop, db.scrollLeft];
2298             } else {
2299                 return [0, 0];
2300             }
2301         },
2302
2303
2304         doAdd: function () {
2305             if (window.addEventListener) {
2306                 return function(el, eventName, fn, capture) {
2307                     el.addEventListener(eventName, fn, (capture));
2308                 };
2309             } else if (window.attachEvent) {
2310                 return function(el, eventName, fn, capture) {
2311                     el.attachEvent("on" + eventName, fn);
2312                 };
2313             } else {
2314                 return function() {
2315                 };
2316             }
2317         }(),
2318
2319
2320         doRemove: function() {
2321             if (window.removeEventListener) {
2322                 return function (el, eventName, fn, capture) {
2323                     el.removeEventListener(eventName, fn, (capture));
2324                 };
2325             } else if (window.detachEvent) {
2326                 return function (el, eventName, fn) {
2327                     el.detachEvent("on" + eventName, fn);
2328                 };
2329             } else {
2330                 return function() {
2331                 };
2332             }
2333         }()
2334     };
2335     
2336 }();
2337 (function() {     
2338    
2339     var E = Roo.lib.Event;
2340     E.on = E.addListener;
2341     E.un = E.removeListener;
2342
2343     if (document && document.body) {
2344         E._load();
2345     } else {
2346         E.doAdd(window, "load", E._load);
2347     }
2348     E.doAdd(window, "unload", E._unload);
2349     E._tryPreloadAttach();
2350 })();
2351
2352 /*
2353  * Portions of this file are based on pieces of Yahoo User Interface Library
2354  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2355  * YUI licensed under the BSD License:
2356  * http://developer.yahoo.net/yui/license.txt
2357  * <script type="text/javascript">
2358  *
2359  */
2360
2361 (function() {
2362     
2363     Roo.lib.Ajax = {
2364         request : function(method, uri, cb, data, options) {
2365             if(options){
2366                 var hs = options.headers;
2367                 if(hs){
2368                     for(var h in hs){
2369                         if(hs.hasOwnProperty(h)){
2370                             this.initHeader(h, hs[h], false);
2371                         }
2372                     }
2373                 }
2374                 if(options.xmlData){
2375                     this.initHeader('Content-Type', 'text/xml', false);
2376                     method = 'POST';
2377                     data = options.xmlData;
2378                 }
2379             }
2380
2381             return this.asyncRequest(method, uri, cb, data);
2382         },
2383
2384         serializeForm : function(form) {
2385             if(typeof form == 'string') {
2386                 form = (document.getElementById(form) || document.forms[form]);
2387             }
2388
2389             var el, name, val, disabled, data = '', hasSubmit = false;
2390             for (var i = 0; i < form.elements.length; i++) {
2391                 el = form.elements[i];
2392                 disabled = form.elements[i].disabled;
2393                 name = form.elements[i].name;
2394                 val = form.elements[i].value;
2395
2396                 if (!disabled && name){
2397                     switch (el.type)
2398                             {
2399                         case 'select-one':
2400                         case 'select-multiple':
2401                             for (var j = 0; j < el.options.length; j++) {
2402                                 if (el.options[j].selected) {
2403                                     if (Roo.isIE) {
2404                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2405                                     }
2406                                     else {
2407                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2408                                     }
2409                                 }
2410                             }
2411                             break;
2412                         case 'radio':
2413                         case 'checkbox':
2414                             if (el.checked) {
2415                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2416                             }
2417                             break;
2418                         case 'file':
2419
2420                         case undefined:
2421
2422                         case 'reset':
2423
2424                         case 'button':
2425
2426                             break;
2427                         case 'submit':
2428                             if(hasSubmit == false) {
2429                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2430                                 hasSubmit = true;
2431                             }
2432                             break;
2433                         default:
2434                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2435                             break;
2436                     }
2437                 }
2438             }
2439             data = data.substr(0, data.length - 1);
2440             return data;
2441         },
2442
2443         headers:{},
2444
2445         hasHeaders:false,
2446
2447         useDefaultHeader:true,
2448
2449         defaultPostHeader:'application/x-www-form-urlencoded',
2450
2451         useDefaultXhrHeader:true,
2452
2453         defaultXhrHeader:'XMLHttpRequest',
2454
2455         hasDefaultHeaders:true,
2456
2457         defaultHeaders:{},
2458
2459         poll:{},
2460
2461         timeout:{},
2462
2463         pollInterval:50,
2464
2465         transactionId:0,
2466
2467         setProgId:function(id)
2468         {
2469             this.activeX.unshift(id);
2470         },
2471
2472         setDefaultPostHeader:function(b)
2473         {
2474             this.useDefaultHeader = b;
2475         },
2476
2477         setDefaultXhrHeader:function(b)
2478         {
2479             this.useDefaultXhrHeader = b;
2480         },
2481
2482         setPollingInterval:function(i)
2483         {
2484             if (typeof i == 'number' && isFinite(i)) {
2485                 this.pollInterval = i;
2486             }
2487         },
2488
2489         createXhrObject:function(transactionId)
2490         {
2491             var obj,http;
2492             try
2493             {
2494
2495                 http = new XMLHttpRequest();
2496
2497                 obj = { conn:http, tId:transactionId };
2498             }
2499             catch(e)
2500             {
2501                 for (var i = 0; i < this.activeX.length; ++i) {
2502                     try
2503                     {
2504
2505                         http = new ActiveXObject(this.activeX[i]);
2506
2507                         obj = { conn:http, tId:transactionId };
2508                         break;
2509                     }
2510                     catch(e) {
2511                     }
2512                 }
2513             }
2514             finally
2515             {
2516                 return obj;
2517             }
2518         },
2519
2520         getConnectionObject:function()
2521         {
2522             var o;
2523             var tId = this.transactionId;
2524
2525             try
2526             {
2527                 o = this.createXhrObject(tId);
2528                 if (o) {
2529                     this.transactionId++;
2530                 }
2531             }
2532             catch(e) {
2533             }
2534             finally
2535             {
2536                 return o;
2537             }
2538         },
2539
2540         asyncRequest:function(method, uri, callback, postData)
2541         {
2542             var o = this.getConnectionObject();
2543
2544             if (!o) {
2545                 return null;
2546             }
2547             else {
2548                 o.conn.open(method, uri, true);
2549
2550                 if (this.useDefaultXhrHeader) {
2551                     if (!this.defaultHeaders['X-Requested-With']) {
2552                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2553                     }
2554                 }
2555
2556                 if(postData && this.useDefaultHeader){
2557                     this.initHeader('Content-Type', this.defaultPostHeader);
2558                 }
2559
2560                  if (this.hasDefaultHeaders || this.hasHeaders) {
2561                     this.setHeader(o);
2562                 }
2563
2564                 this.handleReadyState(o, callback);
2565                 o.conn.send(postData || null);
2566
2567                 return o;
2568             }
2569         },
2570
2571         handleReadyState:function(o, callback)
2572         {
2573             var oConn = this;
2574
2575             if (callback && callback.timeout) {
2576                 this.timeout[o.tId] = window.setTimeout(function() {
2577                     oConn.abort(o, callback, true);
2578                 }, callback.timeout);
2579             }
2580
2581             this.poll[o.tId] = window.setInterval(
2582                     function() {
2583                         if (o.conn && o.conn.readyState == 4) {
2584                             window.clearInterval(oConn.poll[o.tId]);
2585                             delete oConn.poll[o.tId];
2586
2587                             if(callback && callback.timeout) {
2588                                 window.clearTimeout(oConn.timeout[o.tId]);
2589                                 delete oConn.timeout[o.tId];
2590                             }
2591
2592                             oConn.handleTransactionResponse(o, callback);
2593                         }
2594                     }
2595                     , this.pollInterval);
2596         },
2597
2598         handleTransactionResponse:function(o, callback, isAbort)
2599         {
2600
2601             if (!callback) {
2602                 this.releaseObject(o);
2603                 return;
2604             }
2605
2606             var httpStatus, responseObject;
2607
2608             try
2609             {
2610                 if (o.conn.status !== undefined && o.conn.status != 0) {
2611                     httpStatus = o.conn.status;
2612                 }
2613                 else {
2614                     httpStatus = 13030;
2615                 }
2616             }
2617             catch(e) {
2618
2619
2620                 httpStatus = 13030;
2621             }
2622
2623             if (httpStatus >= 200 && httpStatus < 300) {
2624                 responseObject = this.createResponseObject(o, callback.argument);
2625                 if (callback.success) {
2626                     if (!callback.scope) {
2627                         callback.success(responseObject);
2628                     }
2629                     else {
2630
2631
2632                         callback.success.apply(callback.scope, [responseObject]);
2633                     }
2634                 }
2635             }
2636             else {
2637                 switch (httpStatus) {
2638
2639                     case 12002:
2640                     case 12029:
2641                     case 12030:
2642                     case 12031:
2643                     case 12152:
2644                     case 13030:
2645                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2646                         if (callback.failure) {
2647                             if (!callback.scope) {
2648                                 callback.failure(responseObject);
2649                             }
2650                             else {
2651                                 callback.failure.apply(callback.scope, [responseObject]);
2652                             }
2653                         }
2654                         break;
2655                     default:
2656                         responseObject = this.createResponseObject(o, callback.argument);
2657                         if (callback.failure) {
2658                             if (!callback.scope) {
2659                                 callback.failure(responseObject);
2660                             }
2661                             else {
2662                                 callback.failure.apply(callback.scope, [responseObject]);
2663                             }
2664                         }
2665                 }
2666             }
2667
2668             this.releaseObject(o);
2669             responseObject = null;
2670         },
2671
2672         createResponseObject:function(o, callbackArg)
2673         {
2674             var obj = {};
2675             var headerObj = {};
2676
2677             try
2678             {
2679                 var headerStr = o.conn.getAllResponseHeaders();
2680                 var header = headerStr.split('\n');
2681                 for (var i = 0; i < header.length; i++) {
2682                     var delimitPos = header[i].indexOf(':');
2683                     if (delimitPos != -1) {
2684                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2685                     }
2686                 }
2687             }
2688             catch(e) {
2689             }
2690
2691             obj.tId = o.tId;
2692             obj.status = o.conn.status;
2693             obj.statusText = o.conn.statusText;
2694             obj.getResponseHeader = headerObj;
2695             obj.getAllResponseHeaders = headerStr;
2696             obj.responseText = o.conn.responseText;
2697             obj.responseXML = o.conn.responseXML;
2698
2699             if (typeof callbackArg !== undefined) {
2700                 obj.argument = callbackArg;
2701             }
2702
2703             return obj;
2704         },
2705
2706         createExceptionObject:function(tId, callbackArg, isAbort)
2707         {
2708             var COMM_CODE = 0;
2709             var COMM_ERROR = 'communication failure';
2710             var ABORT_CODE = -1;
2711             var ABORT_ERROR = 'transaction aborted';
2712
2713             var obj = {};
2714
2715             obj.tId = tId;
2716             if (isAbort) {
2717                 obj.status = ABORT_CODE;
2718                 obj.statusText = ABORT_ERROR;
2719             }
2720             else {
2721                 obj.status = COMM_CODE;
2722                 obj.statusText = COMM_ERROR;
2723             }
2724
2725             if (callbackArg) {
2726                 obj.argument = callbackArg;
2727             }
2728
2729             return obj;
2730         },
2731
2732         initHeader:function(label, value, isDefault)
2733         {
2734             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2735
2736             if (headerObj[label] === undefined) {
2737                 headerObj[label] = value;
2738             }
2739             else {
2740
2741
2742                 headerObj[label] = value + "," + headerObj[label];
2743             }
2744
2745             if (isDefault) {
2746                 this.hasDefaultHeaders = true;
2747             }
2748             else {
2749                 this.hasHeaders = true;
2750             }
2751         },
2752
2753
2754         setHeader:function(o)
2755         {
2756             if (this.hasDefaultHeaders) {
2757                 for (var prop in this.defaultHeaders) {
2758                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2759                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2760                     }
2761                 }
2762             }
2763
2764             if (this.hasHeaders) {
2765                 for (var prop in this.headers) {
2766                     if (this.headers.hasOwnProperty(prop)) {
2767                         o.conn.setRequestHeader(prop, this.headers[prop]);
2768                     }
2769                 }
2770                 this.headers = {};
2771                 this.hasHeaders = false;
2772             }
2773         },
2774
2775         resetDefaultHeaders:function() {
2776             delete this.defaultHeaders;
2777             this.defaultHeaders = {};
2778             this.hasDefaultHeaders = false;
2779         },
2780
2781         abort:function(o, callback, isTimeout)
2782         {
2783             if(this.isCallInProgress(o)) {
2784                 o.conn.abort();
2785                 window.clearInterval(this.poll[o.tId]);
2786                 delete this.poll[o.tId];
2787                 if (isTimeout) {
2788                     delete this.timeout[o.tId];
2789                 }
2790
2791                 this.handleTransactionResponse(o, callback, true);
2792
2793                 return true;
2794             }
2795             else {
2796                 return false;
2797             }
2798         },
2799
2800
2801         isCallInProgress:function(o)
2802         {
2803             if (o && o.conn) {
2804                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2805             }
2806             else {
2807
2808                 return false;
2809             }
2810         },
2811
2812
2813         releaseObject:function(o)
2814         {
2815
2816             o.conn = null;
2817
2818             o = null;
2819         },
2820
2821         activeX:[
2822         'MSXML2.XMLHTTP.3.0',
2823         'MSXML2.XMLHTTP',
2824         'Microsoft.XMLHTTP'
2825         ]
2826
2827
2828     };
2829 })();/*
2830  * Portions of this file are based on pieces of Yahoo User Interface Library
2831  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2832  * YUI licensed under the BSD License:
2833  * http://developer.yahoo.net/yui/license.txt
2834  * <script type="text/javascript">
2835  *
2836  */
2837
2838 Roo.lib.Region = function(t, r, b, l) {
2839     this.top = t;
2840     this[1] = t;
2841     this.right = r;
2842     this.bottom = b;
2843     this.left = l;
2844     this[0] = l;
2845 };
2846
2847
2848 Roo.lib.Region.prototype = {
2849     contains : function(region) {
2850         return ( region.left >= this.left &&
2851                  region.right <= this.right &&
2852                  region.top >= this.top &&
2853                  region.bottom <= this.bottom    );
2854
2855     },
2856
2857     getArea : function() {
2858         return ( (this.bottom - this.top) * (this.right - this.left) );
2859     },
2860
2861     intersect : function(region) {
2862         var t = Math.max(this.top, region.top);
2863         var r = Math.min(this.right, region.right);
2864         var b = Math.min(this.bottom, region.bottom);
2865         var l = Math.max(this.left, region.left);
2866
2867         if (b >= t && r >= l) {
2868             return new Roo.lib.Region(t, r, b, l);
2869         } else {
2870             return null;
2871         }
2872     },
2873     union : function(region) {
2874         var t = Math.min(this.top, region.top);
2875         var r = Math.max(this.right, region.right);
2876         var b = Math.max(this.bottom, region.bottom);
2877         var l = Math.min(this.left, region.left);
2878
2879         return new Roo.lib.Region(t, r, b, l);
2880     },
2881
2882     adjust : function(t, l, b, r) {
2883         this.top += t;
2884         this.left += l;
2885         this.right += r;
2886         this.bottom += b;
2887         return this;
2888     }
2889 };
2890
2891 Roo.lib.Region.getRegion = function(el) {
2892     var p = Roo.lib.Dom.getXY(el);
2893
2894     var t = p[1];
2895     var r = p[0] + el.offsetWidth;
2896     var b = p[1] + el.offsetHeight;
2897     var l = p[0];
2898
2899     return new Roo.lib.Region(t, r, b, l);
2900 };
2901 /*
2902  * Portions of this file are based on pieces of Yahoo User Interface Library
2903  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2904  * YUI licensed under the BSD License:
2905  * http://developer.yahoo.net/yui/license.txt
2906  * <script type="text/javascript">
2907  *
2908  */
2909 //@@dep Roo.lib.Region
2910
2911
2912 Roo.lib.Point = function(x, y) {
2913     if (x instanceof Array) {
2914         y = x[1];
2915         x = x[0];
2916     }
2917     this.x = this.right = this.left = this[0] = x;
2918     this.y = this.top = this.bottom = this[1] = y;
2919 };
2920
2921 Roo.lib.Point.prototype = new Roo.lib.Region();
2922 /*
2923  * Portions of this file are based on pieces of Yahoo User Interface Library
2924  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2925  * YUI licensed under the BSD License:
2926  * http://developer.yahoo.net/yui/license.txt
2927  * <script type="text/javascript">
2928  *
2929  */
2930  
2931 (function() {   
2932
2933     Roo.lib.Anim = {
2934         scroll : function(el, args, duration, easing, cb, scope) {
2935             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2936         },
2937
2938         motion : function(el, args, duration, easing, cb, scope) {
2939             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2940         },
2941
2942         color : function(el, args, duration, easing, cb, scope) {
2943             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2944         },
2945
2946         run : function(el, args, duration, easing, cb, scope, type) {
2947             type = type || Roo.lib.AnimBase;
2948             if (typeof easing == "string") {
2949                 easing = Roo.lib.Easing[easing];
2950             }
2951             var anim = new type(el, args, duration, easing);
2952             anim.animateX(function() {
2953                 Roo.callback(cb, scope);
2954             });
2955             return anim;
2956         }
2957     };
2958 })();/*
2959  * Portions of this file are based on pieces of Yahoo User Interface Library
2960  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2961  * YUI licensed under the BSD License:
2962  * http://developer.yahoo.net/yui/license.txt
2963  * <script type="text/javascript">
2964  *
2965  */
2966
2967 (function() {    
2968     var libFlyweight;
2969     
2970     function fly(el) {
2971         if (!libFlyweight) {
2972             libFlyweight = new Roo.Element.Flyweight();
2973         }
2974         libFlyweight.dom = el;
2975         return libFlyweight;
2976     }
2977
2978     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2979     
2980    
2981     
2982     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2983         if (el) {
2984             this.init(el, attributes, duration, method);
2985         }
2986     };
2987
2988     Roo.lib.AnimBase.fly = fly;
2989     
2990     
2991     
2992     Roo.lib.AnimBase.prototype = {
2993
2994         toString: function() {
2995             var el = this.getEl();
2996             var id = el.id || el.tagName;
2997             return ("Anim " + id);
2998         },
2999
3000         patterns: {
3001             noNegatives:        /width|height|opacity|padding/i,
3002             offsetAttribute:  /^((width|height)|(top|left))$/,
3003             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3004             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3005         },
3006
3007
3008         doMethod: function(attr, start, end) {
3009             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3010         },
3011
3012
3013         setAttribute: function(attr, val, unit) {
3014             if (this.patterns.noNegatives.test(attr)) {
3015                 val = (val > 0) ? val : 0;
3016             }
3017
3018             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3019         },
3020
3021
3022         getAttribute: function(attr) {
3023             var el = this.getEl();
3024             var val = fly(el).getStyle(attr);
3025
3026             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3027                 return parseFloat(val);
3028             }
3029
3030             var a = this.patterns.offsetAttribute.exec(attr) || [];
3031             var pos = !!( a[3] );
3032             var box = !!( a[2] );
3033
3034
3035             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3036                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3037             } else {
3038                 val = 0;
3039             }
3040
3041             return val;
3042         },
3043
3044
3045         getDefaultUnit: function(attr) {
3046             if (this.patterns.defaultUnit.test(attr)) {
3047                 return 'px';
3048             }
3049
3050             return '';
3051         },
3052
3053         animateX : function(callback, scope) {
3054             var f = function() {
3055                 this.onComplete.removeListener(f);
3056                 if (typeof callback == "function") {
3057                     callback.call(scope || this, this);
3058                 }
3059             };
3060             this.onComplete.addListener(f, this);
3061             this.animate();
3062         },
3063
3064
3065         setRuntimeAttribute: function(attr) {
3066             var start;
3067             var end;
3068             var attributes = this.attributes;
3069
3070             this.runtimeAttributes[attr] = {};
3071
3072             var isset = function(prop) {
3073                 return (typeof prop !== 'undefined');
3074             };
3075
3076             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3077                 return false;
3078             }
3079
3080             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3081
3082
3083             if (isset(attributes[attr]['to'])) {
3084                 end = attributes[attr]['to'];
3085             } else if (isset(attributes[attr]['by'])) {
3086                 if (start.constructor == Array) {
3087                     end = [];
3088                     for (var i = 0, len = start.length; i < len; ++i) {
3089                         end[i] = start[i] + attributes[attr]['by'][i];
3090                     }
3091                 } else {
3092                     end = start + attributes[attr]['by'];
3093                 }
3094             }
3095
3096             this.runtimeAttributes[attr].start = start;
3097             this.runtimeAttributes[attr].end = end;
3098
3099
3100             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3101         },
3102
3103
3104         init: function(el, attributes, duration, method) {
3105
3106             var isAnimated = false;
3107
3108
3109             var startTime = null;
3110
3111
3112             var actualFrames = 0;
3113
3114
3115             el = Roo.getDom(el);
3116
3117
3118             this.attributes = attributes || {};
3119
3120
3121             this.duration = duration || 1;
3122
3123
3124             this.method = method || Roo.lib.Easing.easeNone;
3125
3126
3127             this.useSeconds = true;
3128
3129
3130             this.currentFrame = 0;
3131
3132
3133             this.totalFrames = Roo.lib.AnimMgr.fps;
3134
3135
3136             this.getEl = function() {
3137                 return el;
3138             };
3139
3140
3141             this.isAnimated = function() {
3142                 return isAnimated;
3143             };
3144
3145
3146             this.getStartTime = function() {
3147                 return startTime;
3148             };
3149
3150             this.runtimeAttributes = {};
3151
3152
3153             this.animate = function() {
3154                 if (this.isAnimated()) {
3155                     return false;
3156                 }
3157
3158                 this.currentFrame = 0;
3159
3160                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3161
3162                 Roo.lib.AnimMgr.registerElement(this);
3163             };
3164
3165
3166             this.stop = function(finish) {
3167                 if (finish) {
3168                     this.currentFrame = this.totalFrames;
3169                     this._onTween.fire();
3170                 }
3171                 Roo.lib.AnimMgr.stop(this);
3172             };
3173
3174             var onStart = function() {
3175                 this.onStart.fire();
3176
3177                 this.runtimeAttributes = {};
3178                 for (var attr in this.attributes) {
3179                     this.setRuntimeAttribute(attr);
3180                 }
3181
3182                 isAnimated = true;
3183                 actualFrames = 0;
3184                 startTime = new Date();
3185             };
3186
3187
3188             var onTween = function() {
3189                 var data = {
3190                     duration: new Date() - this.getStartTime(),
3191                     currentFrame: this.currentFrame
3192                 };
3193
3194                 data.toString = function() {
3195                     return (
3196                             'duration: ' + data.duration +
3197                             ', currentFrame: ' + data.currentFrame
3198                             );
3199                 };
3200
3201                 this.onTween.fire(data);
3202
3203                 var runtimeAttributes = this.runtimeAttributes;
3204
3205                 for (var attr in runtimeAttributes) {
3206                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3207                 }
3208
3209                 actualFrames += 1;
3210             };
3211
3212             var onComplete = function() {
3213                 var actual_duration = (new Date() - startTime) / 1000 ;
3214
3215                 var data = {
3216                     duration: actual_duration,
3217                     frames: actualFrames,
3218                     fps: actualFrames / actual_duration
3219                 };
3220
3221                 data.toString = function() {
3222                     return (
3223                             'duration: ' + data.duration +
3224                             ', frames: ' + data.frames +
3225                             ', fps: ' + data.fps
3226                             );
3227                 };
3228
3229                 isAnimated = false;
3230                 actualFrames = 0;
3231                 this.onComplete.fire(data);
3232             };
3233
3234
3235             this._onStart = new Roo.util.Event(this);
3236             this.onStart = new Roo.util.Event(this);
3237             this.onTween = new Roo.util.Event(this);
3238             this._onTween = new Roo.util.Event(this);
3239             this.onComplete = new Roo.util.Event(this);
3240             this._onComplete = new Roo.util.Event(this);
3241             this._onStart.addListener(onStart);
3242             this._onTween.addListener(onTween);
3243             this._onComplete.addListener(onComplete);
3244         }
3245     };
3246 })();
3247 /*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 Roo.lib.AnimMgr = new function() {
3257
3258         var thread = null;
3259
3260
3261         var queue = [];
3262
3263
3264         var tweenCount = 0;
3265
3266
3267         this.fps = 1000;
3268
3269
3270         this.delay = 1;
3271
3272
3273         this.registerElement = function(tween) {
3274             queue[queue.length] = tween;
3275             tweenCount += 1;
3276             tween._onStart.fire();
3277             this.start();
3278         };
3279
3280
3281         this.unRegister = function(tween, index) {
3282             tween._onComplete.fire();
3283             index = index || getIndex(tween);
3284             if (index != -1) {
3285                 queue.splice(index, 1);
3286             }
3287
3288             tweenCount -= 1;
3289             if (tweenCount <= 0) {
3290                 this.stop();
3291             }
3292         };
3293
3294
3295         this.start = function() {
3296             if (thread === null) {
3297                 thread = setInterval(this.run, this.delay);
3298             }
3299         };
3300
3301
3302         this.stop = function(tween) {
3303             if (!tween) {
3304                 clearInterval(thread);
3305
3306                 for (var i = 0, len = queue.length; i < len; ++i) {
3307                     if (queue[0].isAnimated()) {
3308                         this.unRegister(queue[0], 0);
3309                     }
3310                 }
3311
3312                 queue = [];
3313                 thread = null;
3314                 tweenCount = 0;
3315             }
3316             else {
3317                 this.unRegister(tween);
3318             }
3319         };
3320
3321
3322         this.run = function() {
3323             for (var i = 0, len = queue.length; i < len; ++i) {
3324                 var tween = queue[i];
3325                 if (!tween || !tween.isAnimated()) {
3326                     continue;
3327                 }
3328
3329                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3330                 {
3331                     tween.currentFrame += 1;
3332
3333                     if (tween.useSeconds) {
3334                         correctFrame(tween);
3335                     }
3336                     tween._onTween.fire();
3337                 }
3338                 else {
3339                     Roo.lib.AnimMgr.stop(tween, i);
3340                 }
3341             }
3342         };
3343
3344         var getIndex = function(anim) {
3345             for (var i = 0, len = queue.length; i < len; ++i) {
3346                 if (queue[i] == anim) {
3347                     return i;
3348                 }
3349             }
3350             return -1;
3351         };
3352
3353
3354         var correctFrame = function(tween) {
3355             var frames = tween.totalFrames;
3356             var frame = tween.currentFrame;
3357             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3358             var elapsed = (new Date() - tween.getStartTime());
3359             var tweak = 0;
3360
3361             if (elapsed < tween.duration * 1000) {
3362                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3363             } else {
3364                 tweak = frames - (frame + 1);
3365             }
3366             if (tweak > 0 && isFinite(tweak)) {
3367                 if (tween.currentFrame + tweak >= frames) {
3368                     tweak = frames - (frame + 1);
3369                 }
3370
3371                 tween.currentFrame += tweak;
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 Roo.lib.Bezier = new function() {
3383
3384         this.getPosition = function(points, t) {
3385             var n = points.length;
3386             var tmp = [];
3387
3388             for (var i = 0; i < n; ++i) {
3389                 tmp[i] = [points[i][0], points[i][1]];
3390             }
3391
3392             for (var j = 1; j < n; ++j) {
3393                 for (i = 0; i < n - j; ++i) {
3394                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3395                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3396                 }
3397             }
3398
3399             return [ tmp[0][0], tmp[0][1] ];
3400
3401         };
3402     };/*
3403  * Portions of this file are based on pieces of Yahoo User Interface Library
3404  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3405  * YUI licensed under the BSD License:
3406  * http://developer.yahoo.net/yui/license.txt
3407  * <script type="text/javascript">
3408  *
3409  */
3410 (function() {
3411
3412     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3413         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3414     };
3415
3416     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3417
3418     var fly = Roo.lib.AnimBase.fly;
3419     var Y = Roo.lib;
3420     var superclass = Y.ColorAnim.superclass;
3421     var proto = Y.ColorAnim.prototype;
3422
3423     proto.toString = function() {
3424         var el = this.getEl();
3425         var id = el.id || el.tagName;
3426         return ("ColorAnim " + id);
3427     };
3428
3429     proto.patterns.color = /color$/i;
3430     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3431     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3432     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3433     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3434
3435
3436     proto.parseColor = function(s) {
3437         if (s.length == 3) {
3438             return s;
3439         }
3440
3441         var c = this.patterns.hex.exec(s);
3442         if (c && c.length == 4) {
3443             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3444         }
3445
3446         c = this.patterns.rgb.exec(s);
3447         if (c && c.length == 4) {
3448             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3449         }
3450
3451         c = this.patterns.hex3.exec(s);
3452         if (c && c.length == 4) {
3453             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3454         }
3455
3456         return null;
3457     };
3458     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3459     proto.getAttribute = function(attr) {
3460         var el = this.getEl();
3461         if (this.patterns.color.test(attr)) {
3462             var val = fly(el).getStyle(attr);
3463
3464             if (this.patterns.transparent.test(val)) {
3465                 var parent = el.parentNode;
3466                 val = fly(parent).getStyle(attr);
3467
3468                 while (parent && this.patterns.transparent.test(val)) {
3469                     parent = parent.parentNode;
3470                     val = fly(parent).getStyle(attr);
3471                     if (parent.tagName.toUpperCase() == 'HTML') {
3472                         val = '#fff';
3473                     }
3474                 }
3475             }
3476         } else {
3477             val = superclass.getAttribute.call(this, attr);
3478         }
3479
3480         return val;
3481     };
3482     proto.getAttribute = function(attr) {
3483         var el = this.getEl();
3484         if (this.patterns.color.test(attr)) {
3485             var val = fly(el).getStyle(attr);
3486
3487             if (this.patterns.transparent.test(val)) {
3488                 var parent = el.parentNode;
3489                 val = fly(parent).getStyle(attr);
3490
3491                 while (parent && this.patterns.transparent.test(val)) {
3492                     parent = parent.parentNode;
3493                     val = fly(parent).getStyle(attr);
3494                     if (parent.tagName.toUpperCase() == 'HTML') {
3495                         val = '#fff';
3496                     }
3497                 }
3498             }
3499         } else {
3500             val = superclass.getAttribute.call(this, attr);
3501         }
3502
3503         return val;
3504     };
3505
3506     proto.doMethod = function(attr, start, end) {
3507         var val;
3508
3509         if (this.patterns.color.test(attr)) {
3510             val = [];
3511             for (var i = 0, len = start.length; i < len; ++i) {
3512                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3513             }
3514
3515             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3516         }
3517         else {
3518             val = superclass.doMethod.call(this, attr, start, end);
3519         }
3520
3521         return val;
3522     };
3523
3524     proto.setRuntimeAttribute = function(attr) {
3525         superclass.setRuntimeAttribute.call(this, attr);
3526
3527         if (this.patterns.color.test(attr)) {
3528             var attributes = this.attributes;
3529             var start = this.parseColor(this.runtimeAttributes[attr].start);
3530             var end = this.parseColor(this.runtimeAttributes[attr].end);
3531
3532             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3533                 end = this.parseColor(attributes[attr].by);
3534
3535                 for (var i = 0, len = start.length; i < len; ++i) {
3536                     end[i] = start[i] + end[i];
3537                 }
3538             }
3539
3540             this.runtimeAttributes[attr].start = start;
3541             this.runtimeAttributes[attr].end = end;
3542         }
3543     };
3544 })();
3545
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554 Roo.lib.Easing = {
3555
3556
3557     easeNone: function (t, b, c, d) {
3558         return c * t / d + b;
3559     },
3560
3561
3562     easeIn: function (t, b, c, d) {
3563         return c * (t /= d) * t + b;
3564     },
3565
3566
3567     easeOut: function (t, b, c, d) {
3568         return -c * (t /= d) * (t - 2) + b;
3569     },
3570
3571
3572     easeBoth: function (t, b, c, d) {
3573         if ((t /= d / 2) < 1) {
3574             return c / 2 * t * t + b;
3575         }
3576
3577         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3578     },
3579
3580
3581     easeInStrong: function (t, b, c, d) {
3582         return c * (t /= d) * t * t * t + b;
3583     },
3584
3585
3586     easeOutStrong: function (t, b, c, d) {
3587         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3588     },
3589
3590
3591     easeBothStrong: function (t, b, c, d) {
3592         if ((t /= d / 2) < 1) {
3593             return c / 2 * t * t * t * t + b;
3594         }
3595
3596         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3597     },
3598
3599
3600
3601     elasticIn: function (t, b, c, d, a, p) {
3602         if (t == 0) {
3603             return b;
3604         }
3605         if ((t /= d) == 1) {
3606             return b + c;
3607         }
3608         if (!p) {
3609             p = d * .3;
3610         }
3611
3612         if (!a || a < Math.abs(c)) {
3613             a = c;
3614             var s = p / 4;
3615         }
3616         else {
3617             var s = p / (2 * Math.PI) * Math.asin(c / a);
3618         }
3619
3620         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3621     },
3622
3623
3624     elasticOut: function (t, b, c, d, a, p) {
3625         if (t == 0) {
3626             return b;
3627         }
3628         if ((t /= d) == 1) {
3629             return b + c;
3630         }
3631         if (!p) {
3632             p = d * .3;
3633         }
3634
3635         if (!a || a < Math.abs(c)) {
3636             a = c;
3637             var s = p / 4;
3638         }
3639         else {
3640             var s = p / (2 * Math.PI) * Math.asin(c / a);
3641         }
3642
3643         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3644     },
3645
3646
3647     elasticBoth: function (t, b, c, d, a, p) {
3648         if (t == 0) {
3649             return b;
3650         }
3651
3652         if ((t /= d / 2) == 2) {
3653             return b + c;
3654         }
3655
3656         if (!p) {
3657             p = d * (.3 * 1.5);
3658         }
3659
3660         if (!a || a < Math.abs(c)) {
3661             a = c;
3662             var s = p / 4;
3663         }
3664         else {
3665             var s = p / (2 * Math.PI) * Math.asin(c / a);
3666         }
3667
3668         if (t < 1) {
3669             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3670                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3671         }
3672         return a * Math.pow(2, -10 * (t -= 1)) *
3673                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3674     },
3675
3676
3677
3678     backIn: function (t, b, c, d, s) {
3679         if (typeof s == 'undefined') {
3680             s = 1.70158;
3681         }
3682         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3683     },
3684
3685
3686     backOut: function (t, b, c, d, s) {
3687         if (typeof s == 'undefined') {
3688             s = 1.70158;
3689         }
3690         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3691     },
3692
3693
3694     backBoth: function (t, b, c, d, s) {
3695         if (typeof s == 'undefined') {
3696             s = 1.70158;
3697         }
3698
3699         if ((t /= d / 2 ) < 1) {
3700             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3701         }
3702         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3703     },
3704
3705
3706     bounceIn: function (t, b, c, d) {
3707         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3708     },
3709
3710
3711     bounceOut: function (t, b, c, d) {
3712         if ((t /= d) < (1 / 2.75)) {
3713             return c * (7.5625 * t * t) + b;
3714         } else if (t < (2 / 2.75)) {
3715             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3716         } else if (t < (2.5 / 2.75)) {
3717             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3718         }
3719         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3720     },
3721
3722
3723     bounceBoth: function (t, b, c, d) {
3724         if (t < d / 2) {
3725             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3726         }
3727         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3728     }
3729 };/*
3730  * Portions of this file are based on pieces of Yahoo User Interface Library
3731  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3732  * YUI licensed under the BSD License:
3733  * http://developer.yahoo.net/yui/license.txt
3734  * <script type="text/javascript">
3735  *
3736  */
3737     (function() {
3738         Roo.lib.Motion = function(el, attributes, duration, method) {
3739             if (el) {
3740                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3741             }
3742         };
3743
3744         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3745
3746
3747         var Y = Roo.lib;
3748         var superclass = Y.Motion.superclass;
3749         var proto = Y.Motion.prototype;
3750
3751         proto.toString = function() {
3752             var el = this.getEl();
3753             var id = el.id || el.tagName;
3754             return ("Motion " + id);
3755         };
3756
3757         proto.patterns.points = /^points$/i;
3758
3759         proto.setAttribute = function(attr, val, unit) {
3760             if (this.patterns.points.test(attr)) {
3761                 unit = unit || 'px';
3762                 superclass.setAttribute.call(this, 'left', val[0], unit);
3763                 superclass.setAttribute.call(this, 'top', val[1], unit);
3764             } else {
3765                 superclass.setAttribute.call(this, attr, val, unit);
3766             }
3767         };
3768
3769         proto.getAttribute = function(attr) {
3770             if (this.patterns.points.test(attr)) {
3771                 var val = [
3772                         superclass.getAttribute.call(this, 'left'),
3773                         superclass.getAttribute.call(this, 'top')
3774                         ];
3775             } else {
3776                 val = superclass.getAttribute.call(this, attr);
3777             }
3778
3779             return val;
3780         };
3781
3782         proto.doMethod = function(attr, start, end) {
3783             var val = null;
3784
3785             if (this.patterns.points.test(attr)) {
3786                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3787                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3788             } else {
3789                 val = superclass.doMethod.call(this, attr, start, end);
3790             }
3791             return val;
3792         };
3793
3794         proto.setRuntimeAttribute = function(attr) {
3795             if (this.patterns.points.test(attr)) {
3796                 var el = this.getEl();
3797                 var attributes = this.attributes;
3798                 var start;
3799                 var control = attributes['points']['control'] || [];
3800                 var end;
3801                 var i, len;
3802
3803                 if (control.length > 0 && !(control[0] instanceof Array)) {
3804                     control = [control];
3805                 } else {
3806                     var tmp = [];
3807                     for (i = 0,len = control.length; i < len; ++i) {
3808                         tmp[i] = control[i];
3809                     }
3810                     control = tmp;
3811                 }
3812
3813                 Roo.fly(el).position();
3814
3815                 if (isset(attributes['points']['from'])) {
3816                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3817                 }
3818                 else {
3819                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3820                 }
3821
3822                 start = this.getAttribute('points');
3823
3824
3825                 if (isset(attributes['points']['to'])) {
3826                     end = translateValues.call(this, attributes['points']['to'], start);
3827
3828                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3829                     for (i = 0,len = control.length; i < len; ++i) {
3830                         control[i] = translateValues.call(this, control[i], start);
3831                     }
3832
3833
3834                 } else if (isset(attributes['points']['by'])) {
3835                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3836
3837                     for (i = 0,len = control.length; i < len; ++i) {
3838                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3839                     }
3840                 }
3841
3842                 this.runtimeAttributes[attr] = [start];
3843
3844                 if (control.length > 0) {
3845                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3846                 }
3847
3848                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3849             }
3850             else {
3851                 superclass.setRuntimeAttribute.call(this, attr);
3852             }
3853         };
3854
3855         var translateValues = function(val, start) {
3856             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3857             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3858
3859             return val;
3860         };
3861
3862         var isset = function(prop) {
3863             return (typeof prop !== 'undefined');
3864         };
3865     })();
3866 /*
3867  * Portions of this file are based on pieces of Yahoo User Interface Library
3868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3869  * YUI licensed under the BSD License:
3870  * http://developer.yahoo.net/yui/license.txt
3871  * <script type="text/javascript">
3872  *
3873  */
3874     (function() {
3875         Roo.lib.Scroll = function(el, attributes, duration, method) {
3876             if (el) {
3877                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3878             }
3879         };
3880
3881         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3882
3883
3884         var Y = Roo.lib;
3885         var superclass = Y.Scroll.superclass;
3886         var proto = Y.Scroll.prototype;
3887
3888         proto.toString = function() {
3889             var el = this.getEl();
3890             var id = el.id || el.tagName;
3891             return ("Scroll " + id);
3892         };
3893
3894         proto.doMethod = function(attr, start, end) {
3895             var val = null;
3896
3897             if (attr == 'scroll') {
3898                 val = [
3899                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3900                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3901                         ];
3902
3903             } else {
3904                 val = superclass.doMethod.call(this, attr, start, end);
3905             }
3906             return val;
3907         };
3908
3909         proto.getAttribute = function(attr) {
3910             var val = null;
3911             var el = this.getEl();
3912
3913             if (attr == 'scroll') {
3914                 val = [ el.scrollLeft, el.scrollTop ];
3915             } else {
3916                 val = superclass.getAttribute.call(this, attr);
3917             }
3918
3919             return val;
3920         };
3921
3922         proto.setAttribute = function(attr, val, unit) {
3923             var el = this.getEl();
3924
3925             if (attr == 'scroll') {
3926                 el.scrollLeft = val[0];
3927                 el.scrollTop = val[1];
3928             } else {
3929                 superclass.setAttribute.call(this, attr, val, unit);
3930             }
3931         };
3932     })();
3933 /*
3934  * Based on:
3935  * Ext JS Library 1.1.1
3936  * Copyright(c) 2006-2007, Ext JS, LLC.
3937  *
3938  * Originally Released Under LGPL - original licence link has changed is not relivant.
3939  *
3940  * Fork - LGPL
3941  * <script type="text/javascript">
3942  */
3943  
3944
3945 /**
3946  * @class Roo.DomHelper
3947  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3948  * 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>.
3949  * @singleton
3950  */
3951 Roo.DomHelper = function(){
3952     var tempTableEl = null;
3953     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3954     var tableRe = /^table|tbody|tr|td$/i;
3955     var xmlns = {};
3956     // build as innerHTML where available
3957     /** @ignore */
3958     var createHtml = function(o){
3959         if(typeof o == 'string'){
3960             return o;
3961         }
3962         var b = "";
3963         if(!o.tag){
3964             o.tag = "div";
3965         }
3966         b += "<" + o.tag;
3967         for(var attr in o){
3968             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3969             if(attr == "style"){
3970                 var s = o["style"];
3971                 if(typeof s == "function"){
3972                     s = s.call();
3973                 }
3974                 if(typeof s == "string"){
3975                     b += ' style="' + s + '"';
3976                 }else if(typeof s == "object"){
3977                     b += ' style="';
3978                     for(var key in s){
3979                         if(typeof s[key] != "function"){
3980                             b += key + ":" + s[key] + ";";
3981                         }
3982                     }
3983                     b += '"';
3984                 }
3985             }else{
3986                 if(attr == "cls"){
3987                     b += ' class="' + o["cls"] + '"';
3988                 }else if(attr == "htmlFor"){
3989                     b += ' for="' + o["htmlFor"] + '"';
3990                 }else{
3991                     b += " " + attr + '="' + o[attr] + '"';
3992                 }
3993             }
3994         }
3995         if(emptyTags.test(o.tag)){
3996             b += "/>";
3997         }else{
3998             b += ">";
3999             var cn = o.children || o.cn;
4000             if(cn){
4001                 //http://bugs.kde.org/show_bug.cgi?id=71506
4002                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4003                     for(var i = 0, len = cn.length; i < len; i++) {
4004                         b += createHtml(cn[i], b);
4005                     }
4006                 }else{
4007                     b += createHtml(cn, b);
4008                 }
4009             }
4010             if(o.html){
4011                 b += o.html;
4012             }
4013             b += "</" + o.tag + ">";
4014         }
4015         return b;
4016     };
4017
4018     // build as dom
4019     /** @ignore */
4020     var createDom = function(o, parentNode){
4021          
4022         // defininition craeted..
4023         var ns = false;
4024         if (o.ns && o.ns != 'html') {
4025                
4026             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4027                 xmlns[o.ns] = o.xmlns;
4028                 ns = o.xmlns;
4029             }
4030             if (typeof(xmlns[o.ns]) == 'undefined') {
4031                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4032             }
4033             ns = xmlns[o.ns];
4034         }
4035         
4036         
4037         if (typeof(o) == 'string') {
4038             return parentNode.appendChild(document.createTextNode(o));
4039         }
4040         o.tag = o.tag || div;
4041         if (o.ns && Roo.isIE) {
4042             ns = false;
4043             o.tag = o.ns + ':' + o.tag;
4044             
4045         }
4046         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4047         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4048         for(var attr in o){
4049             
4050             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4051                     attr == "style" || typeof o[attr] == "function") continue;
4052                     
4053             if(attr=="cls" && Roo.isIE){
4054                 el.className = o["cls"];
4055             }else{
4056                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4057                 else el[attr] = o[attr];
4058             }
4059         }
4060         Roo.DomHelper.applyStyles(el, o.style);
4061         var cn = o.children || o.cn;
4062         if(cn){
4063             //http://bugs.kde.org/show_bug.cgi?id=71506
4064              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4065                 for(var i = 0, len = cn.length; i < len; i++) {
4066                     createDom(cn[i], el);
4067                 }
4068             }else{
4069                 createDom(cn, el);
4070             }
4071         }
4072         if(o.html){
4073             el.innerHTML = o.html;
4074         }
4075         if(parentNode){
4076            parentNode.appendChild(el);
4077         }
4078         return el;
4079     };
4080
4081     var ieTable = function(depth, s, h, e){
4082         tempTableEl.innerHTML = [s, h, e].join('');
4083         var i = -1, el = tempTableEl;
4084         while(++i < depth){
4085             el = el.firstChild;
4086         }
4087         return el;
4088     };
4089
4090     // kill repeat to save bytes
4091     var ts = '<table>',
4092         te = '</table>',
4093         tbs = ts+'<tbody>',
4094         tbe = '</tbody>'+te,
4095         trs = tbs + '<tr>',
4096         tre = '</tr>'+tbe;
4097
4098     /**
4099      * @ignore
4100      * Nasty code for IE's broken table implementation
4101      */
4102     var insertIntoTable = function(tag, where, el, html){
4103         if(!tempTableEl){
4104             tempTableEl = document.createElement('div');
4105         }
4106         var node;
4107         var before = null;
4108         if(tag == 'td'){
4109             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4110                 return;
4111             }
4112             if(where == 'beforebegin'){
4113                 before = el;
4114                 el = el.parentNode;
4115             } else{
4116                 before = el.nextSibling;
4117                 el = el.parentNode;
4118             }
4119             node = ieTable(4, trs, html, tre);
4120         }
4121         else if(tag == 'tr'){
4122             if(where == 'beforebegin'){
4123                 before = el;
4124                 el = el.parentNode;
4125                 node = ieTable(3, tbs, html, tbe);
4126             } else if(where == 'afterend'){
4127                 before = el.nextSibling;
4128                 el = el.parentNode;
4129                 node = ieTable(3, tbs, html, tbe);
4130             } else{ // INTO a TR
4131                 if(where == 'afterbegin'){
4132                     before = el.firstChild;
4133                 }
4134                 node = ieTable(4, trs, html, tre);
4135             }
4136         } else if(tag == 'tbody'){
4137             if(where == 'beforebegin'){
4138                 before = el;
4139                 el = el.parentNode;
4140                 node = ieTable(2, ts, html, te);
4141             } else if(where == 'afterend'){
4142                 before = el.nextSibling;
4143                 el = el.parentNode;
4144                 node = ieTable(2, ts, html, te);
4145             } else{
4146                 if(where == 'afterbegin'){
4147                     before = el.firstChild;
4148                 }
4149                 node = ieTable(3, tbs, html, tbe);
4150             }
4151         } else{ // TABLE
4152             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4153                 return;
4154             }
4155             if(where == 'afterbegin'){
4156                 before = el.firstChild;
4157             }
4158             node = ieTable(2, ts, html, te);
4159         }
4160         el.insertBefore(node, before);
4161         return node;
4162     };
4163
4164     return {
4165     /** True to force the use of DOM instead of html fragments @type Boolean */
4166     useDom : false,
4167
4168     /**
4169      * Returns the markup for the passed Element(s) config
4170      * @param {Object} o The Dom object spec (and children)
4171      * @return {String}
4172      */
4173     markup : function(o){
4174         return createHtml(o);
4175     },
4176
4177     /**
4178      * Applies a style specification to an element
4179      * @param {String/HTMLElement} el The element to apply styles to
4180      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4181      * a function which returns such a specification.
4182      */
4183     applyStyles : function(el, styles){
4184         if(styles){
4185            el = Roo.fly(el);
4186            if(typeof styles == "string"){
4187                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4188                var matches;
4189                while ((matches = re.exec(styles)) != null){
4190                    el.setStyle(matches[1], matches[2]);
4191                }
4192            }else if (typeof styles == "object"){
4193                for (var style in styles){
4194                   el.setStyle(style, styles[style]);
4195                }
4196            }else if (typeof styles == "function"){
4197                 Roo.DomHelper.applyStyles(el, styles.call());
4198            }
4199         }
4200     },
4201
4202     /**
4203      * Inserts an HTML fragment into the Dom
4204      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4205      * @param {HTMLElement} el The context element
4206      * @param {String} html The HTML fragmenet
4207      * @return {HTMLElement} The new node
4208      */
4209     insertHtml : function(where, el, html){
4210         where = where.toLowerCase();
4211         if(el.insertAdjacentHTML){
4212             if(tableRe.test(el.tagName)){
4213                 var rs;
4214                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4215                     return rs;
4216                 }
4217             }
4218             switch(where){
4219                 case "beforebegin":
4220                     el.insertAdjacentHTML('BeforeBegin', html);
4221                     return el.previousSibling;
4222                 case "afterbegin":
4223                     el.insertAdjacentHTML('AfterBegin', html);
4224                     return el.firstChild;
4225                 case "beforeend":
4226                     el.insertAdjacentHTML('BeforeEnd', html);
4227                     return el.lastChild;
4228                 case "afterend":
4229                     el.insertAdjacentHTML('AfterEnd', html);
4230                     return el.nextSibling;
4231             }
4232             throw 'Illegal insertion point -> "' + where + '"';
4233         }
4234         var range = el.ownerDocument.createRange();
4235         var frag;
4236         switch(where){
4237              case "beforebegin":
4238                 range.setStartBefore(el);
4239                 frag = range.createContextualFragment(html);
4240                 el.parentNode.insertBefore(frag, el);
4241                 return el.previousSibling;
4242              case "afterbegin":
4243                 if(el.firstChild){
4244                     range.setStartBefore(el.firstChild);
4245                     frag = range.createContextualFragment(html);
4246                     el.insertBefore(frag, el.firstChild);
4247                     return el.firstChild;
4248                 }else{
4249                     el.innerHTML = html;
4250                     return el.firstChild;
4251                 }
4252             case "beforeend":
4253                 if(el.lastChild){
4254                     range.setStartAfter(el.lastChild);
4255                     frag = range.createContextualFragment(html);
4256                     el.appendChild(frag);
4257                     return el.lastChild;
4258                 }else{
4259                     el.innerHTML = html;
4260                     return el.lastChild;
4261                 }
4262             case "afterend":
4263                 range.setStartAfter(el);
4264                 frag = range.createContextualFragment(html);
4265                 el.parentNode.insertBefore(frag, el.nextSibling);
4266                 return el.nextSibling;
4267             }
4268             throw 'Illegal insertion point -> "' + where + '"';
4269     },
4270
4271     /**
4272      * Creates new Dom element(s) and inserts them before el
4273      * @param {String/HTMLElement/Element} el The context element
4274      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4275      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4276      * @return {HTMLElement/Roo.Element} The new node
4277      */
4278     insertBefore : function(el, o, returnElement){
4279         return this.doInsert(el, o, returnElement, "beforeBegin");
4280     },
4281
4282     /**
4283      * Creates new Dom element(s) and inserts them after el
4284      * @param {String/HTMLElement/Element} el The context element
4285      * @param {Object} o The Dom object spec (and children)
4286      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4287      * @return {HTMLElement/Roo.Element} The new node
4288      */
4289     insertAfter : function(el, o, returnElement){
4290         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4291     },
4292
4293     /**
4294      * Creates new Dom element(s) and inserts them as the first child of el
4295      * @param {String/HTMLElement/Element} el The context element
4296      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4297      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4298      * @return {HTMLElement/Roo.Element} The new node
4299      */
4300     insertFirst : function(el, o, returnElement){
4301         return this.doInsert(el, o, returnElement, "afterBegin");
4302     },
4303
4304     // private
4305     doInsert : function(el, o, returnElement, pos, sibling){
4306         el = Roo.getDom(el);
4307         var newNode;
4308         if(this.useDom || o.ns){
4309             newNode = createDom(o, null);
4310             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4311         }else{
4312             var html = createHtml(o);
4313             newNode = this.insertHtml(pos, el, html);
4314         }
4315         return returnElement ? Roo.get(newNode, true) : newNode;
4316     },
4317
4318     /**
4319      * Creates new Dom element(s) and appends them to el
4320      * @param {String/HTMLElement/Element} el The context element
4321      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4322      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4323      * @return {HTMLElement/Roo.Element} The new node
4324      */
4325     append : function(el, o, returnElement){
4326         el = Roo.getDom(el);
4327         var newNode;
4328         if(this.useDom || o.ns){
4329             newNode = createDom(o, null);
4330             el.appendChild(newNode);
4331         }else{
4332             var html = createHtml(o);
4333             newNode = this.insertHtml("beforeEnd", el, html);
4334         }
4335         return returnElement ? Roo.get(newNode, true) : newNode;
4336     },
4337
4338     /**
4339      * Creates new Dom element(s) and overwrites the contents of el with them
4340      * @param {String/HTMLElement/Element} el The context element
4341      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4342      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4343      * @return {HTMLElement/Roo.Element} The new node
4344      */
4345     overwrite : function(el, o, returnElement){
4346         el = Roo.getDom(el);
4347         if (o.ns) {
4348           
4349             while (el.childNodes.length) {
4350                 el.removeChild(el.firstChild);
4351             }
4352             createDom(o, el);
4353         } else {
4354             el.innerHTML = createHtml(o);   
4355         }
4356         
4357         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4358     },
4359
4360     /**
4361      * Creates a new Roo.DomHelper.Template from the Dom object spec
4362      * @param {Object} o The Dom object spec (and children)
4363      * @return {Roo.DomHelper.Template} The new template
4364      */
4365     createTemplate : function(o){
4366         var html = createHtml(o);
4367         return new Roo.Template(html);
4368     }
4369     };
4370 }();
4371 /*
4372  * Based on:
4373  * Ext JS Library 1.1.1
4374  * Copyright(c) 2006-2007, Ext JS, LLC.
4375  *
4376  * Originally Released Under LGPL - original licence link has changed is not relivant.
4377  *
4378  * Fork - LGPL
4379  * <script type="text/javascript">
4380  */
4381  
4382 /**
4383 * @class Roo.Template
4384 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4385 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4386 * Usage:
4387 <pre><code>
4388 var t = new Roo.Template(
4389     '&lt;div name="{id}"&gt;',
4390         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4391     '&lt;/div&gt;'
4392 );
4393 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4394 </code></pre>
4395 * 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>. 
4396 * @constructor
4397 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4398 */
4399 Roo.Template = function(html){
4400     if(html instanceof Array){
4401         html = html.join("");
4402     }else if(arguments.length > 1){
4403         html = Array.prototype.join.call(arguments, "");
4404     }
4405     /**@private*/
4406     this.html = html;
4407     
4408 };
4409 Roo.Template.prototype = {
4410     /**
4411      * Returns an HTML fragment of this template with the specified values applied.
4412      * @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'})
4413      * @return {String} The HTML fragment
4414      */
4415     applyTemplate : function(values){
4416         if(this.compiled){
4417             return this.compiled(values);
4418         }
4419         var useF = this.disableFormats !== true;
4420         var fm = Roo.util.Format, tpl = this;
4421         var fn = function(m, name, format, args){
4422             if(format && useF){
4423                 if(format.substr(0, 5) == "this."){
4424                     return tpl.call(format.substr(5), values[name], values);
4425                 }else{
4426                     if(args){
4427                         // quoted values are required for strings in compiled templates, 
4428                         // but for non compiled we need to strip them
4429                         // quoted reversed for jsmin
4430                         var re = /^\s*['"](.*)["']\s*$/;
4431                         args = args.split(',');
4432                         for(var i = 0, len = args.length; i < len; i++){
4433                             args[i] = args[i].replace(re, "$1");
4434                         }
4435                         args = [values[name]].concat(args);
4436                     }else{
4437                         args = [values[name]];
4438                     }
4439                     return fm[format].apply(fm, args);
4440                 }
4441             }else{
4442                 return values[name] !== undefined ? values[name] : "";
4443             }
4444         };
4445         return this.html.replace(this.re, fn);
4446     },
4447     
4448     /**
4449      * Sets the HTML used as the template and optionally compiles it.
4450      * @param {String} html
4451      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4452      * @return {Roo.Template} this
4453      */
4454     set : function(html, compile){
4455         this.html = html;
4456         this.compiled = null;
4457         if(compile){
4458             this.compile();
4459         }
4460         return this;
4461     },
4462     
4463     /**
4464      * True to disable format functions (defaults to false)
4465      * @type Boolean
4466      */
4467     disableFormats : false,
4468     
4469     /**
4470     * The regular expression used to match template variables 
4471     * @type RegExp
4472     * @property 
4473     */
4474     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4475     
4476     /**
4477      * Compiles the template into an internal function, eliminating the RegEx overhead.
4478      * @return {Roo.Template} this
4479      */
4480     compile : function(){
4481         var fm = Roo.util.Format;
4482         var useF = this.disableFormats !== true;
4483         var sep = Roo.isGecko ? "+" : ",";
4484         var fn = function(m, name, format, args){
4485             if(format && useF){
4486                 args = args ? ',' + args : "";
4487                 if(format.substr(0, 5) != "this."){
4488                     format = "fm." + format + '(';
4489                 }else{
4490                     format = 'this.call("'+ format.substr(5) + '", ';
4491                     args = ", values";
4492                 }
4493             }else{
4494                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4495             }
4496             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4497         };
4498         var body;
4499         // branched to use + in gecko and [].join() in others
4500         if(Roo.isGecko){
4501             body = "this.compiled = function(values){ return '" +
4502                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4503                     "';};";
4504         }else{
4505             body = ["this.compiled = function(values){ return ['"];
4506             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4507             body.push("'].join('');};");
4508             body = body.join('');
4509         }
4510         /**
4511          * eval:var:values
4512          * eval:var:fm
4513          */
4514         eval(body);
4515         return this;
4516     },
4517     
4518     // private function used to call members
4519     call : function(fnName, value, allValues){
4520         return this[fnName](value, allValues);
4521     },
4522     
4523     /**
4524      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4525      * @param {String/HTMLElement/Roo.Element} el The context element
4526      * @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'})
4527      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4528      * @return {HTMLElement/Roo.Element} The new node or Element
4529      */
4530     insertFirst: function(el, values, returnElement){
4531         return this.doInsert('afterBegin', el, values, returnElement);
4532     },
4533
4534     /**
4535      * Applies the supplied values to the template and inserts the new node(s) before el.
4536      * @param {String/HTMLElement/Roo.Element} el The context element
4537      * @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'})
4538      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4539      * @return {HTMLElement/Roo.Element} The new node or Element
4540      */
4541     insertBefore: function(el, values, returnElement){
4542         return this.doInsert('beforeBegin', el, values, returnElement);
4543     },
4544
4545     /**
4546      * Applies the supplied values to the template and inserts the new node(s) after el.
4547      * @param {String/HTMLElement/Roo.Element} el The context element
4548      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4549      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4550      * @return {HTMLElement/Roo.Element} The new node or Element
4551      */
4552     insertAfter : function(el, values, returnElement){
4553         return this.doInsert('afterEnd', el, values, returnElement);
4554     },
4555     
4556     /**
4557      * Applies the supplied values to the template and appends the new node(s) to el.
4558      * @param {String/HTMLElement/Roo.Element} el The context element
4559      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4560      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4561      * @return {HTMLElement/Roo.Element} The new node or Element
4562      */
4563     append : function(el, values, returnElement){
4564         return this.doInsert('beforeEnd', el, values, returnElement);
4565     },
4566
4567     doInsert : function(where, el, values, returnEl){
4568         el = Roo.getDom(el);
4569         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4570         return returnEl ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4575      * @param {String/HTMLElement/Roo.Element} el The context element
4576      * @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'})
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4578      * @return {HTMLElement/Roo.Element} The new node or Element
4579      */
4580     overwrite : function(el, values, returnElement){
4581         el = Roo.getDom(el);
4582         el.innerHTML = this.applyTemplate(values);
4583         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4584     }
4585 };
4586 /**
4587  * Alias for {@link #applyTemplate}
4588  * @method
4589  */
4590 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4591
4592 // backwards compat
4593 Roo.DomHelper.Template = Roo.Template;
4594
4595 /**
4596  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4597  * @param {String/HTMLElement} el A DOM element or its id
4598  * @returns {Roo.Template} The created template
4599  * @static
4600  */
4601 Roo.Template.from = function(el){
4602     el = Roo.getDom(el);
4603     return new Roo.Template(el.value || el.innerHTML);
4604 };/*
4605  * Based on:
4606  * Ext JS Library 1.1.1
4607  * Copyright(c) 2006-2007, Ext JS, LLC.
4608  *
4609  * Originally Released Under LGPL - original licence link has changed is not relivant.
4610  *
4611  * Fork - LGPL
4612  * <script type="text/javascript">
4613  */
4614  
4615
4616 /*
4617  * This is code is also distributed under MIT license for use
4618  * with jQuery and prototype JavaScript libraries.
4619  */
4620 /**
4621  * @class Roo.DomQuery
4622 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).
4623 <p>
4624 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>
4625
4626 <p>
4627 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.
4628 </p>
4629 <h4>Element Selectors:</h4>
4630 <ul class="list">
4631     <li> <b>*</b> any element</li>
4632     <li> <b>E</b> an element with the tag E</li>
4633     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4634     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4635     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4636     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4637 </ul>
4638 <h4>Attribute Selectors:</h4>
4639 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4640 <ul class="list">
4641     <li> <b>E[foo]</b> has an attribute "foo"</li>
4642     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4643     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4644     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4645     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4646     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4647     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4648 </ul>
4649 <h4>Pseudo Classes:</h4>
4650 <ul class="list">
4651     <li> <b>E:first-child</b> E is the first child of its parent</li>
4652     <li> <b>E:last-child</b> E is the last child of its parent</li>
4653     <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>
4654     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4655     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4656     <li> <b>E:only-child</b> E is the only child of its parent</li>
4657     <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>
4658     <li> <b>E:first</b> the first E in the resultset</li>
4659     <li> <b>E:last</b> the last E in the resultset</li>
4660     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4661     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4662     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4663     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4664     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4665     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4666     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4667     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4668     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4669 </ul>
4670 <h4>CSS Value Selectors:</h4>
4671 <ul class="list">
4672     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4673     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4674     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4675     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4676     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4677     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4678 </ul>
4679  * @singleton
4680  */
4681 Roo.DomQuery = function(){
4682     var cache = {}, simpleCache = {}, valueCache = {};
4683     var nonSpace = /\S/;
4684     var trimRe = /^\s+|\s+$/g;
4685     var tplRe = /\{(\d+)\}/g;
4686     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4687     var tagTokenRe = /^(#)?([\w-\*]+)/;
4688     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4689
4690     function child(p, index){
4691         var i = 0;
4692         var n = p.firstChild;
4693         while(n){
4694             if(n.nodeType == 1){
4695                if(++i == index){
4696                    return n;
4697                }
4698             }
4699             n = n.nextSibling;
4700         }
4701         return null;
4702     };
4703
4704     function next(n){
4705         while((n = n.nextSibling) && n.nodeType != 1);
4706         return n;
4707     };
4708
4709     function prev(n){
4710         while((n = n.previousSibling) && n.nodeType != 1);
4711         return n;
4712     };
4713
4714     function children(d){
4715         var n = d.firstChild, ni = -1;
4716             while(n){
4717                 var nx = n.nextSibling;
4718                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4719                     d.removeChild(n);
4720                 }else{
4721                     n.nodeIndex = ++ni;
4722                 }
4723                 n = nx;
4724             }
4725             return this;
4726         };
4727
4728     function byClassName(c, a, v){
4729         if(!v){
4730             return c;
4731         }
4732         var r = [], ri = -1, cn;
4733         for(var i = 0, ci; ci = c[i]; i++){
4734             if((' '+ci.className+' ').indexOf(v) != -1){
4735                 r[++ri] = ci;
4736             }
4737         }
4738         return r;
4739     };
4740
4741     function attrValue(n, attr){
4742         if(!n.tagName && typeof n.length != "undefined"){
4743             n = n[0];
4744         }
4745         if(!n){
4746             return null;
4747         }
4748         if(attr == "for"){
4749             return n.htmlFor;
4750         }
4751         if(attr == "class" || attr == "className"){
4752             return n.className;
4753         }
4754         return n.getAttribute(attr) || n[attr];
4755
4756     };
4757
4758     function getNodes(ns, mode, tagName){
4759         var result = [], ri = -1, cs;
4760         if(!ns){
4761             return result;
4762         }
4763         tagName = tagName || "*";
4764         if(typeof ns.getElementsByTagName != "undefined"){
4765             ns = [ns];
4766         }
4767         if(!mode){
4768             for(var i = 0, ni; ni = ns[i]; i++){
4769                 cs = ni.getElementsByTagName(tagName);
4770                 for(var j = 0, ci; ci = cs[j]; j++){
4771                     result[++ri] = ci;
4772                 }
4773             }
4774         }else if(mode == "/" || mode == ">"){
4775             var utag = tagName.toUpperCase();
4776             for(var i = 0, ni, cn; ni = ns[i]; i++){
4777                 cn = ni.children || ni.childNodes;
4778                 for(var j = 0, cj; cj = cn[j]; j++){
4779                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4780                         result[++ri] = cj;
4781                     }
4782                 }
4783             }
4784         }else if(mode == "+"){
4785             var utag = tagName.toUpperCase();
4786             for(var i = 0, n; n = ns[i]; i++){
4787                 while((n = n.nextSibling) && n.nodeType != 1);
4788                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4789                     result[++ri] = n;
4790                 }
4791             }
4792         }else if(mode == "~"){
4793             for(var i = 0, n; n = ns[i]; i++){
4794                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4795                 if(n){
4796                     result[++ri] = n;
4797                 }
4798             }
4799         }
4800         return result;
4801     };
4802
4803     function concat(a, b){
4804         if(b.slice){
4805             return a.concat(b);
4806         }
4807         for(var i = 0, l = b.length; i < l; i++){
4808             a[a.length] = b[i];
4809         }
4810         return a;
4811     }
4812
4813     function byTag(cs, tagName){
4814         if(cs.tagName || cs == document){
4815             cs = [cs];
4816         }
4817         if(!tagName){
4818             return cs;
4819         }
4820         var r = [], ri = -1;
4821         tagName = tagName.toLowerCase();
4822         for(var i = 0, ci; ci = cs[i]; i++){
4823             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4824                 r[++ri] = ci;
4825             }
4826         }
4827         return r;
4828     };
4829
4830     function byId(cs, attr, id){
4831         if(cs.tagName || cs == document){
4832             cs = [cs];
4833         }
4834         if(!id){
4835             return cs;
4836         }
4837         var r = [], ri = -1;
4838         for(var i = 0,ci; ci = cs[i]; i++){
4839             if(ci && ci.id == id){
4840                 r[++ri] = ci;
4841                 return r;
4842             }
4843         }
4844         return r;
4845     };
4846
4847     function byAttribute(cs, attr, value, op, custom){
4848         var r = [], ri = -1, st = custom=="{";
4849         var f = Roo.DomQuery.operators[op];
4850         for(var i = 0, ci; ci = cs[i]; i++){
4851             var a;
4852             if(st){
4853                 a = Roo.DomQuery.getStyle(ci, attr);
4854             }
4855             else if(attr == "class" || attr == "className"){
4856                 a = ci.className;
4857             }else if(attr == "for"){
4858                 a = ci.htmlFor;
4859             }else if(attr == "href"){
4860                 a = ci.getAttribute("href", 2);
4861             }else{
4862                 a = ci.getAttribute(attr);
4863             }
4864             if((f && f(a, value)) || (!f && a)){
4865                 r[++ri] = ci;
4866             }
4867         }
4868         return r;
4869     };
4870
4871     function byPseudo(cs, name, value){
4872         return Roo.DomQuery.pseudos[name](cs, value);
4873     };
4874
4875     // This is for IE MSXML which does not support expandos.
4876     // IE runs the same speed using setAttribute, however FF slows way down
4877     // and Safari completely fails so they need to continue to use expandos.
4878     var isIE = window.ActiveXObject ? true : false;
4879
4880     // this eval is stop the compressor from
4881     // renaming the variable to something shorter
4882     
4883     /** eval:var:batch */
4884     var batch = 30803; 
4885
4886     var key = 30803;
4887
4888     function nodupIEXml(cs){
4889         var d = ++key;
4890         cs[0].setAttribute("_nodup", d);
4891         var r = [cs[0]];
4892         for(var i = 1, len = cs.length; i < len; i++){
4893             var c = cs[i];
4894             if(!c.getAttribute("_nodup") != d){
4895                 c.setAttribute("_nodup", d);
4896                 r[r.length] = c;
4897             }
4898         }
4899         for(var i = 0, len = cs.length; i < len; i++){
4900             cs[i].removeAttribute("_nodup");
4901         }
4902         return r;
4903     }
4904
4905     function nodup(cs){
4906         if(!cs){
4907             return [];
4908         }
4909         var len = cs.length, c, i, r = cs, cj, ri = -1;
4910         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4911             return cs;
4912         }
4913         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4914             return nodupIEXml(cs);
4915         }
4916         var d = ++key;
4917         cs[0]._nodup = d;
4918         for(i = 1; c = cs[i]; i++){
4919             if(c._nodup != d){
4920                 c._nodup = d;
4921             }else{
4922                 r = [];
4923                 for(var j = 0; j < i; j++){
4924                     r[++ri] = cs[j];
4925                 }
4926                 for(j = i+1; cj = cs[j]; j++){
4927                     if(cj._nodup != d){
4928                         cj._nodup = d;
4929                         r[++ri] = cj;
4930                     }
4931                 }
4932                 return r;
4933             }
4934         }
4935         return r;
4936     }
4937
4938     function quickDiffIEXml(c1, c2){
4939         var d = ++key;
4940         for(var i = 0, len = c1.length; i < len; i++){
4941             c1[i].setAttribute("_qdiff", d);
4942         }
4943         var r = [];
4944         for(var i = 0, len = c2.length; i < len; i++){
4945             if(c2[i].getAttribute("_qdiff") != d){
4946                 r[r.length] = c2[i];
4947             }
4948         }
4949         for(var i = 0, len = c1.length; i < len; i++){
4950            c1[i].removeAttribute("_qdiff");
4951         }
4952         return r;
4953     }
4954
4955     function quickDiff(c1, c2){
4956         var len1 = c1.length;
4957         if(!len1){
4958             return c2;
4959         }
4960         if(isIE && c1[0].selectSingleNode){
4961             return quickDiffIEXml(c1, c2);
4962         }
4963         var d = ++key;
4964         for(var i = 0; i < len1; i++){
4965             c1[i]._qdiff = d;
4966         }
4967         var r = [];
4968         for(var i = 0, len = c2.length; i < len; i++){
4969             if(c2[i]._qdiff != d){
4970                 r[r.length] = c2[i];
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickId(ns, mode, root, id){
4977         if(ns == root){
4978            var d = root.ownerDocument || root;
4979            return d.getElementById(id);
4980         }
4981         ns = getNodes(ns, mode, "*");
4982         return byId(ns, null, id);
4983     }
4984
4985     return {
4986         getStyle : function(el, name){
4987             return Roo.fly(el).getStyle(name);
4988         },
4989         /**
4990          * Compiles a selector/xpath query into a reusable function. The returned function
4991          * takes one parameter "root" (optional), which is the context node from where the query should start.
4992          * @param {String} selector The selector/xpath query
4993          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4994          * @return {Function}
4995          */
4996         compile : function(path, type){
4997             type = type || "select";
4998             
4999             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5000             var q = path, mode, lq;
5001             var tk = Roo.DomQuery.matchers;
5002             var tklen = tk.length;
5003             var mm;
5004
5005             // accept leading mode switch
5006             var lmode = q.match(modeRe);
5007             if(lmode && lmode[1]){
5008                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5009                 q = q.replace(lmode[1], "");
5010             }
5011             // strip leading slashes
5012             while(path.substr(0, 1)=="/"){
5013                 path = path.substr(1);
5014             }
5015
5016             while(q && lq != q){
5017                 lq = q;
5018                 var tm = q.match(tagTokenRe);
5019                 if(type == "select"){
5020                     if(tm){
5021                         if(tm[1] == "#"){
5022                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5023                         }else{
5024                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5025                         }
5026                         q = q.replace(tm[0], "");
5027                     }else if(q.substr(0, 1) != '@'){
5028                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5029                     }
5030                 }else{
5031                     if(tm){
5032                         if(tm[1] == "#"){
5033                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5034                         }else{
5035                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5036                         }
5037                         q = q.replace(tm[0], "");
5038                     }
5039                 }
5040                 while(!(mm = q.match(modeRe))){
5041                     var matched = false;
5042                     for(var j = 0; j < tklen; j++){
5043                         var t = tk[j];
5044                         var m = q.match(t.re);
5045                         if(m){
5046                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5047                                                     return m[i];
5048                                                 });
5049                             q = q.replace(m[0], "");
5050                             matched = true;
5051                             break;
5052                         }
5053                     }
5054                     // prevent infinite loop on bad selector
5055                     if(!matched){
5056                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5057                     }
5058                 }
5059                 if(mm[1]){
5060                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5061                     q = q.replace(mm[1], "");
5062                 }
5063             }
5064             fn[fn.length] = "return nodup(n);\n}";
5065             
5066              /** 
5067               * list of variables that need from compression as they are used by eval.
5068              *  eval:var:batch 
5069              *  eval:var:nodup
5070              *  eval:var:byTag
5071              *  eval:var:ById
5072              *  eval:var:getNodes
5073              *  eval:var:quickId
5074              *  eval:var:mode
5075              *  eval:var:root
5076              *  eval:var:n
5077              *  eval:var:byClassName
5078              *  eval:var:byPseudo
5079              *  eval:var:byAttribute
5080              *  eval:var:attrValue
5081              * 
5082              **/ 
5083             eval(fn.join(""));
5084             return f;
5085         },
5086
5087         /**
5088          * Selects a group of elements.
5089          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5090          * @param {Node} root (optional) The start of the query (defaults to document).
5091          * @return {Array}
5092          */
5093         select : function(path, root, type){
5094             if(!root || root == document){
5095                 root = document;
5096             }
5097             if(typeof root == "string"){
5098                 root = document.getElementById(root);
5099             }
5100             var paths = path.split(",");
5101             var results = [];
5102             for(var i = 0, len = paths.length; i < len; i++){
5103                 var p = paths[i].replace(trimRe, "");
5104                 if(!cache[p]){
5105                     cache[p] = Roo.DomQuery.compile(p);
5106                     if(!cache[p]){
5107                         throw p + " is not a valid selector";
5108                     }
5109                 }
5110                 var result = cache[p](root);
5111                 if(result && result != document){
5112                     results = results.concat(result);
5113                 }
5114             }
5115             if(paths.length > 1){
5116                 return nodup(results);
5117             }
5118             return results;
5119         },
5120
5121         /**
5122          * Selects a single element.
5123          * @param {String} selector The selector/xpath query
5124          * @param {Node} root (optional) The start of the query (defaults to document).
5125          * @return {Element}
5126          */
5127         selectNode : function(path, root){
5128             return Roo.DomQuery.select(path, root)[0];
5129         },
5130
5131         /**
5132          * Selects the value of a node, optionally replacing null with the defaultValue.
5133          * @param {String} selector The selector/xpath query
5134          * @param {Node} root (optional) The start of the query (defaults to document).
5135          * @param {String} defaultValue
5136          */
5137         selectValue : function(path, root, defaultValue){
5138             path = path.replace(trimRe, "");
5139             if(!valueCache[path]){
5140                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5141             }
5142             var n = valueCache[path](root);
5143             n = n[0] ? n[0] : n;
5144             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5145             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5146         },
5147
5148         /**
5149          * Selects the value of a node, parsing integers and floats.
5150          * @param {String} selector The selector/xpath query
5151          * @param {Node} root (optional) The start of the query (defaults to document).
5152          * @param {Number} defaultValue
5153          * @return {Number}
5154          */
5155         selectNumber : function(path, root, defaultValue){
5156             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5157             return parseFloat(v);
5158         },
5159
5160         /**
5161          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5162          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5163          * @param {String} selector The simple selector to test
5164          * @return {Boolean}
5165          */
5166         is : function(el, ss){
5167             if(typeof el == "string"){
5168                 el = document.getElementById(el);
5169             }
5170             var isArray = (el instanceof Array);
5171             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5172             return isArray ? (result.length == el.length) : (result.length > 0);
5173         },
5174
5175         /**
5176          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5177          * @param {Array} el An array of elements to filter
5178          * @param {String} selector The simple selector to test
5179          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5180          * the selector instead of the ones that match
5181          * @return {Array}
5182          */
5183         filter : function(els, ss, nonMatches){
5184             ss = ss.replace(trimRe, "");
5185             if(!simpleCache[ss]){
5186                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5187             }
5188             var result = simpleCache[ss](els);
5189             return nonMatches ? quickDiff(result, els) : result;
5190         },
5191
5192         /**
5193          * Collection of matching regular expressions and code snippets.
5194          */
5195         matchers : [{
5196                 re: /^\.([\w-]+)/,
5197                 select: 'n = byClassName(n, null, " {1} ");'
5198             }, {
5199                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5200                 select: 'n = byPseudo(n, "{1}", "{2}");'
5201             },{
5202                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5203                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5204             }, {
5205                 re: /^#([\w-]+)/,
5206                 select: 'n = byId(n, null, "{1}");'
5207             },{
5208                 re: /^@([\w-]+)/,
5209                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5210             }
5211         ],
5212
5213         /**
5214          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5215          * 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;.
5216          */
5217         operators : {
5218             "=" : function(a, v){
5219                 return a == v;
5220             },
5221             "!=" : function(a, v){
5222                 return a != v;
5223             },
5224             "^=" : function(a, v){
5225                 return a && a.substr(0, v.length) == v;
5226             },
5227             "$=" : function(a, v){
5228                 return a && a.substr(a.length-v.length) == v;
5229             },
5230             "*=" : function(a, v){
5231                 return a && a.indexOf(v) !== -1;
5232             },
5233             "%=" : function(a, v){
5234                 return (a % v) == 0;
5235             },
5236             "|=" : function(a, v){
5237                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5238             },
5239             "~=" : function(a, v){
5240                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5241             }
5242         },
5243
5244         /**
5245          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5246          * and the argument (if any) supplied in the selector.
5247          */
5248         pseudos : {
5249             "first-child" : function(c){
5250                 var r = [], ri = -1, n;
5251                 for(var i = 0, ci; ci = n = c[i]; i++){
5252                     while((n = n.previousSibling) && n.nodeType != 1);
5253                     if(!n){
5254                         r[++ri] = ci;
5255                     }
5256                 }
5257                 return r;
5258             },
5259
5260             "last-child" : function(c){
5261                 var r = [], ri = -1, n;
5262                 for(var i = 0, ci; ci = n = c[i]; i++){
5263                     while((n = n.nextSibling) && n.nodeType != 1);
5264                     if(!n){
5265                         r[++ri] = ci;
5266                     }
5267                 }
5268                 return r;
5269             },
5270
5271             "nth-child" : function(c, a) {
5272                 var r = [], ri = -1;
5273                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5274                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5275                 for(var i = 0, n; n = c[i]; i++){
5276                     var pn = n.parentNode;
5277                     if (batch != pn._batch) {
5278                         var j = 0;
5279                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5280                             if(cn.nodeType == 1){
5281                                cn.nodeIndex = ++j;
5282                             }
5283                         }
5284                         pn._batch = batch;
5285                     }
5286                     if (f == 1) {
5287                         if (l == 0 || n.nodeIndex == l){
5288                             r[++ri] = n;
5289                         }
5290                     } else if ((n.nodeIndex + l) % f == 0){
5291                         r[++ri] = n;
5292                     }
5293                 }
5294
5295                 return r;
5296             },
5297
5298             "only-child" : function(c){
5299                 var r = [], ri = -1;;
5300                 for(var i = 0, ci; ci = c[i]; i++){
5301                     if(!prev(ci) && !next(ci)){
5302                         r[++ri] = ci;
5303                     }
5304                 }
5305                 return r;
5306             },
5307
5308             "empty" : function(c){
5309                 var r = [], ri = -1;
5310                 for(var i = 0, ci; ci = c[i]; i++){
5311                     var cns = ci.childNodes, j = 0, cn, empty = true;
5312                     while(cn = cns[j]){
5313                         ++j;
5314                         if(cn.nodeType == 1 || cn.nodeType == 3){
5315                             empty = false;
5316                             break;
5317                         }
5318                     }
5319                     if(empty){
5320                         r[++ri] = ci;
5321                     }
5322                 }
5323                 return r;
5324             },
5325
5326             "contains" : function(c, v){
5327                 var r = [], ri = -1;
5328                 for(var i = 0, ci; ci = c[i]; i++){
5329                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5330                         r[++ri] = ci;
5331                     }
5332                 }
5333                 return r;
5334             },
5335
5336             "nodeValue" : function(c, v){
5337                 var r = [], ri = -1;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "checked" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     if(ci.checked == true){
5350                         r[++ri] = ci;
5351                     }
5352                 }
5353                 return r;
5354             },
5355
5356             "not" : function(c, ss){
5357                 return Roo.DomQuery.filter(c, ss, true);
5358             },
5359
5360             "odd" : function(c){
5361                 return this["nth-child"](c, "odd");
5362             },
5363
5364             "even" : function(c){
5365                 return this["nth-child"](c, "even");
5366             },
5367
5368             "nth" : function(c, a){
5369                 return c[a-1] || [];
5370             },
5371
5372             "first" : function(c){
5373                 return c[0] || [];
5374             },
5375
5376             "last" : function(c){
5377                 return c[c.length-1] || [];
5378             },
5379
5380             "has" : function(c, ss){
5381                 var s = Roo.DomQuery.select;
5382                 var r = [], ri = -1;
5383                 for(var i = 0, ci; ci = c[i]; i++){
5384                     if(s(ss, ci).length > 0){
5385                         r[++ri] = ci;
5386                     }
5387                 }
5388                 return r;
5389             },
5390
5391             "next" : function(c, ss){
5392                 var is = Roo.DomQuery.is;
5393                 var r = [], ri = -1;
5394                 for(var i = 0, ci; ci = c[i]; i++){
5395                     var n = next(ci);
5396                     if(n && is(n, ss)){
5397                         r[++ri] = ci;
5398                     }
5399                 }
5400                 return r;
5401             },
5402
5403             "prev" : function(c, ss){
5404                 var is = Roo.DomQuery.is;
5405                 var r = [], ri = -1;
5406                 for(var i = 0, ci; ci = c[i]; i++){
5407                     var n = prev(ci);
5408                     if(n && is(n, ss)){
5409                         r[++ri] = ci;
5410                     }
5411                 }
5412                 return r;
5413             }
5414         }
5415     };
5416 }();
5417
5418 /**
5419  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5420  * @param {String} path The selector/xpath query
5421  * @param {Node} root (optional) The start of the query (defaults to document).
5422  * @return {Array}
5423  * @member Roo
5424  * @method query
5425  */
5426 Roo.query = Roo.DomQuery.select;
5427 /*
5428  * Based on:
5429  * Ext JS Library 1.1.1
5430  * Copyright(c) 2006-2007, Ext JS, LLC.
5431  *
5432  * Originally Released Under LGPL - original licence link has changed is not relivant.
5433  *
5434  * Fork - LGPL
5435  * <script type="text/javascript">
5436  */
5437
5438 /**
5439  * @class Roo.util.Observable
5440  * Base class that provides a common interface for publishing events. Subclasses are expected to
5441  * to have a property "events" with all the events defined.<br>
5442  * For example:
5443  * <pre><code>
5444  Employee = function(name){
5445     this.name = name;
5446     this.addEvents({
5447         "fired" : true,
5448         "quit" : true
5449     });
5450  }
5451  Roo.extend(Employee, Roo.util.Observable);
5452 </code></pre>
5453  * @param {Object} config properties to use (incuding events / listeners)
5454  */
5455
5456 Roo.util.Observable = function(cfg){
5457     
5458     cfg = cfg|| {};
5459     this.addEvents(cfg.events || {});
5460     if (cfg.events) {
5461         delete cfg.events; // make sure
5462     }
5463      
5464     Roo.apply(this, cfg);
5465     
5466     if(this.listeners){
5467         this.on(this.listeners);
5468         delete this.listeners;
5469     }
5470 };
5471 Roo.util.Observable.prototype = {
5472     /** 
5473  * @cfg {Object} listeners  list of events and functions to call for this object, 
5474  * For example :
5475  * <pre><code>
5476     listeners :  { 
5477        'click' : function(e) {
5478            ..... 
5479         } ,
5480         .... 
5481     } 
5482   </code></pre>
5483  */
5484     
5485     
5486     /**
5487      * Fires the specified event with the passed parameters (minus the event name).
5488      * @param {String} eventName
5489      * @param {Object...} args Variable number of parameters are passed to handlers
5490      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5491      */
5492     fireEvent : function(){
5493         var ce = this.events[arguments[0].toLowerCase()];
5494         if(typeof ce == "object"){
5495             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5496         }else{
5497             return true;
5498         }
5499     },
5500
5501     // private
5502     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5503
5504     /**
5505      * Appends an event handler to this component
5506      * @param {String}   eventName The type of event to listen for
5507      * @param {Function} handler The method the event invokes
5508      * @param {Object}   scope (optional) The scope in which to execute the handler
5509      * function. The handler function's "this" context.
5510      * @param {Object}   options (optional) An object containing handler configuration
5511      * properties. This may contain any of the following properties:<ul>
5512      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5513      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5514      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5515      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5516      * by the specified number of milliseconds. If the event fires again within that time, the original
5517      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5518      * </ul><br>
5519      * <p>
5520      * <b>Combining Options</b><br>
5521      * Using the options argument, it is possible to combine different types of listeners:<br>
5522      * <br>
5523      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5524                 <pre><code>
5525                 el.on('click', this.onClick, this, {
5526                         single: true,
5527                 delay: 100,
5528                 forumId: 4
5529                 });
5530                 </code></pre>
5531      * <p>
5532      * <b>Attaching multiple handlers in 1 call</b><br>
5533      * The method also allows for a single argument to be passed which is a config object containing properties
5534      * which specify multiple handlers.
5535      * <pre><code>
5536                 el.on({
5537                         'click': {
5538                         fn: this.onClick,
5539                         scope: this,
5540                         delay: 100
5541                 }, 
5542                 'mouseover': {
5543                         fn: this.onMouseOver,
5544                         scope: this
5545                 },
5546                 'mouseout': {
5547                         fn: this.onMouseOut,
5548                         scope: this
5549                 }
5550                 });
5551                 </code></pre>
5552      * <p>
5553      * Or a shorthand syntax which passes the same scope object to all handlers:
5554         <pre><code>
5555                 el.on({
5556                         'click': this.onClick,
5557                 'mouseover': this.onMouseOver,
5558                 'mouseout': this.onMouseOut,
5559                 scope: this
5560                 });
5561                 </code></pre>
5562      */
5563     addListener : function(eventName, fn, scope, o){
5564         if(typeof eventName == "object"){
5565             o = eventName;
5566             for(var e in o){
5567                 if(this.filterOptRe.test(e)){
5568                     continue;
5569                 }
5570                 if(typeof o[e] == "function"){
5571                     // shared options
5572                     this.addListener(e, o[e], o.scope,  o);
5573                 }else{
5574                     // individual options
5575                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5576                 }
5577             }
5578             return;
5579         }
5580         o = (!o || typeof o == "boolean") ? {} : o;
5581         eventName = eventName.toLowerCase();
5582         var ce = this.events[eventName] || true;
5583         if(typeof ce == "boolean"){
5584             ce = new Roo.util.Event(this, eventName);
5585             this.events[eventName] = ce;
5586         }
5587         ce.addListener(fn, scope, o);
5588     },
5589
5590     /**
5591      * Removes a listener
5592      * @param {String}   eventName     The type of event to listen for
5593      * @param {Function} handler        The handler to remove
5594      * @param {Object}   scope  (optional) The scope (this object) for the handler
5595      */
5596     removeListener : function(eventName, fn, scope){
5597         var ce = this.events[eventName.toLowerCase()];
5598         if(typeof ce == "object"){
5599             ce.removeListener(fn, scope);
5600         }
5601     },
5602
5603     /**
5604      * Removes all listeners for this object
5605      */
5606     purgeListeners : function(){
5607         for(var evt in this.events){
5608             if(typeof this.events[evt] == "object"){
5609                  this.events[evt].clearListeners();
5610             }
5611         }
5612     },
5613
5614     relayEvents : function(o, events){
5615         var createHandler = function(ename){
5616             return function(){
5617                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5618             };
5619         };
5620         for(var i = 0, len = events.length; i < len; i++){
5621             var ename = events[i];
5622             if(!this.events[ename]){ this.events[ename] = true; };
5623             o.on(ename, createHandler(ename), this);
5624         }
5625     },
5626
5627     /**
5628      * Used to define events on this Observable
5629      * @param {Object} object The object with the events defined
5630      */
5631     addEvents : function(o){
5632         if(!this.events){
5633             this.events = {};
5634         }
5635         Roo.applyIf(this.events, o);
5636     },
5637
5638     /**
5639      * Checks to see if this object has any listeners for a specified event
5640      * @param {String} eventName The name of the event to check for
5641      * @return {Boolean} True if the event is being listened for, else false
5642      */
5643     hasListener : function(eventName){
5644         var e = this.events[eventName];
5645         return typeof e == "object" && e.listeners.length > 0;
5646     }
5647 };
5648 /**
5649  * Appends an event handler to this element (shorthand for addListener)
5650  * @param {String}   eventName     The type of event to listen for
5651  * @param {Function} handler        The method the event invokes
5652  * @param {Object}   scope (optional) The scope in which to execute the handler
5653  * function. The handler function's "this" context.
5654  * @param {Object}   options  (optional)
5655  * @method
5656  */
5657 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5658 /**
5659  * Removes a listener (shorthand for removeListener)
5660  * @param {String}   eventName     The type of event to listen for
5661  * @param {Function} handler        The handler to remove
5662  * @param {Object}   scope  (optional) The scope (this object) for the handler
5663  * @method
5664  */
5665 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5666
5667 /**
5668  * Starts capture on the specified Observable. All events will be passed
5669  * to the supplied function with the event name + standard signature of the event
5670  * <b>before</b> the event is fired. If the supplied function returns false,
5671  * the event will not fire.
5672  * @param {Observable} o The Observable to capture
5673  * @param {Function} fn The function to call
5674  * @param {Object} scope (optional) The scope (this object) for the fn
5675  * @static
5676  */
5677 Roo.util.Observable.capture = function(o, fn, scope){
5678     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5679 };
5680
5681 /**
5682  * Removes <b>all</b> added captures from the Observable.
5683  * @param {Observable} o The Observable to release
5684  * @static
5685  */
5686 Roo.util.Observable.releaseCapture = function(o){
5687     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5688 };
5689
5690 (function(){
5691
5692     var createBuffered = function(h, o, scope){
5693         var task = new Roo.util.DelayedTask();
5694         return function(){
5695             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5696         };
5697     };
5698
5699     var createSingle = function(h, e, fn, scope){
5700         return function(){
5701             e.removeListener(fn, scope);
5702             return h.apply(scope, arguments);
5703         };
5704     };
5705
5706     var createDelayed = function(h, o, scope){
5707         return function(){
5708             var args = Array.prototype.slice.call(arguments, 0);
5709             setTimeout(function(){
5710                 h.apply(scope, args);
5711             }, o.delay || 10);
5712         };
5713     };
5714
5715     Roo.util.Event = function(obj, name){
5716         this.name = name;
5717         this.obj = obj;
5718         this.listeners = [];
5719     };
5720
5721     Roo.util.Event.prototype = {
5722         addListener : function(fn, scope, options){
5723             var o = options || {};
5724             scope = scope || this.obj;
5725             if(!this.isListening(fn, scope)){
5726                 var l = {fn: fn, scope: scope, options: o};
5727                 var h = fn;
5728                 if(o.delay){
5729                     h = createDelayed(h, o, scope);
5730                 }
5731                 if(o.single){
5732                     h = createSingle(h, this, fn, scope);
5733                 }
5734                 if(o.buffer){
5735                     h = createBuffered(h, o, scope);
5736                 }
5737                 l.fireFn = h;
5738                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5739                     this.listeners.push(l);
5740                 }else{
5741                     this.listeners = this.listeners.slice(0);
5742                     this.listeners.push(l);
5743                 }
5744             }
5745         },
5746
5747         findListener : function(fn, scope){
5748             scope = scope || this.obj;
5749             var ls = this.listeners;
5750             for(var i = 0, len = ls.length; i < len; i++){
5751                 var l = ls[i];
5752                 if(l.fn == fn && l.scope == scope){
5753                     return i;
5754                 }
5755             }
5756             return -1;
5757         },
5758
5759         isListening : function(fn, scope){
5760             return this.findListener(fn, scope) != -1;
5761         },
5762
5763         removeListener : function(fn, scope){
5764             var index;
5765             if((index = this.findListener(fn, scope)) != -1){
5766                 if(!this.firing){
5767                     this.listeners.splice(index, 1);
5768                 }else{
5769                     this.listeners = this.listeners.slice(0);
5770                     this.listeners.splice(index, 1);
5771                 }
5772                 return true;
5773             }
5774             return false;
5775         },
5776
5777         clearListeners : function(){
5778             this.listeners = [];
5779         },
5780
5781         fire : function(){
5782             var ls = this.listeners, scope, len = ls.length;
5783             if(len > 0){
5784                 this.firing = true;
5785                 var args = Array.prototype.slice.call(arguments, 0);
5786                 for(var i = 0; i < len; i++){
5787                     var l = ls[i];
5788                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5789                         this.firing = false;
5790                         return false;
5791                     }
5792                 }
5793                 this.firing = false;
5794             }
5795             return true;
5796         }
5797     };
5798 })();/*
5799  * Based on:
5800  * Ext JS Library 1.1.1
5801  * Copyright(c) 2006-2007, Ext JS, LLC.
5802  *
5803  * Originally Released Under LGPL - original licence link has changed is not relivant.
5804  *
5805  * Fork - LGPL
5806  * <script type="text/javascript">
5807  */
5808
5809 /**
5810  * @class Roo.EventManager
5811  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5812  * several useful events directly.
5813  * See {@link Roo.EventObject} for more details on normalized event objects.
5814  * @singleton
5815  */
5816 Roo.EventManager = function(){
5817     var docReadyEvent, docReadyProcId, docReadyState = false;
5818     var resizeEvent, resizeTask, textEvent, textSize;
5819     var E = Roo.lib.Event;
5820     var D = Roo.lib.Dom;
5821
5822
5823     var fireDocReady = function(){
5824         if(!docReadyState){
5825             docReadyState = true;
5826             Roo.isReady = true;
5827             if(docReadyProcId){
5828                 clearInterval(docReadyProcId);
5829             }
5830             if(Roo.isGecko || Roo.isOpera) {
5831                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5832             }
5833             if(Roo.isIE){
5834                 var defer = document.getElementById("ie-deferred-loader");
5835                 if(defer){
5836                     defer.onreadystatechange = null;
5837                     defer.parentNode.removeChild(defer);
5838                 }
5839             }
5840             if(docReadyEvent){
5841                 docReadyEvent.fire();
5842                 docReadyEvent.clearListeners();
5843             }
5844         }
5845     };
5846     
5847     var initDocReady = function(){
5848         docReadyEvent = new Roo.util.Event();
5849         if(Roo.isGecko || Roo.isOpera) {
5850             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5851         }else if(Roo.isIE){
5852             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5853             var defer = document.getElementById("ie-deferred-loader");
5854             defer.onreadystatechange = function(){
5855                 if(this.readyState == "complete"){
5856                     fireDocReady();
5857                 }
5858             };
5859         }else if(Roo.isSafari){ 
5860             docReadyProcId = setInterval(function(){
5861                 var rs = document.readyState;
5862                 if(rs == "complete") {
5863                     fireDocReady();     
5864                  }
5865             }, 10);
5866         }
5867         // no matter what, make sure it fires on load
5868         E.on(window, "load", fireDocReady);
5869     };
5870
5871     var createBuffered = function(h, o){
5872         var task = new Roo.util.DelayedTask(h);
5873         return function(e){
5874             // create new event object impl so new events don't wipe out properties
5875             e = new Roo.EventObjectImpl(e);
5876             task.delay(o.buffer, h, null, [e]);
5877         };
5878     };
5879
5880     var createSingle = function(h, el, ename, fn){
5881         return function(e){
5882             Roo.EventManager.removeListener(el, ename, fn);
5883             h(e);
5884         };
5885     };
5886
5887     var createDelayed = function(h, o){
5888         return function(e){
5889             // create new event object impl so new events don't wipe out properties
5890             e = new Roo.EventObjectImpl(e);
5891             setTimeout(function(){
5892                 h(e);
5893             }, o.delay || 10);
5894         };
5895     };
5896
5897     var listen = function(element, ename, opt, fn, scope){
5898         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5899         fn = fn || o.fn; scope = scope || o.scope;
5900         var el = Roo.getDom(element);
5901         if(!el){
5902             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5903         }
5904         var h = function(e){
5905             e = Roo.EventObject.setEvent(e);
5906             var t;
5907             if(o.delegate){
5908                 t = e.getTarget(o.delegate, el);
5909                 if(!t){
5910                     return;
5911                 }
5912             }else{
5913                 t = e.target;
5914             }
5915             if(o.stopEvent === true){
5916                 e.stopEvent();
5917             }
5918             if(o.preventDefault === true){
5919                e.preventDefault();
5920             }
5921             if(o.stopPropagation === true){
5922                 e.stopPropagation();
5923             }
5924
5925             if(o.normalized === false){
5926                 e = e.browserEvent;
5927             }
5928
5929             fn.call(scope || el, e, t, o);
5930         };
5931         if(o.delay){
5932             h = createDelayed(h, o);
5933         }
5934         if(o.single){
5935             h = createSingle(h, el, ename, fn);
5936         }
5937         if(o.buffer){
5938             h = createBuffered(h, o);
5939         }
5940         fn._handlers = fn._handlers || [];
5941         fn._handlers.push([Roo.id(el), ename, h]);
5942
5943         E.on(el, ename, h);
5944         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5945             el.addEventListener("DOMMouseScroll", h, false);
5946             E.on(window, 'unload', function(){
5947                 el.removeEventListener("DOMMouseScroll", h, false);
5948             });
5949         }
5950         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5951             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5952         }
5953         return h;
5954     };
5955
5956     var stopListening = function(el, ename, fn){
5957         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5958         if(hds){
5959             for(var i = 0, len = hds.length; i < len; i++){
5960                 var h = hds[i];
5961                 if(h[0] == id && h[1] == ename){
5962                     hd = h[2];
5963                     hds.splice(i, 1);
5964                     break;
5965                 }
5966             }
5967         }
5968         E.un(el, ename, hd);
5969         el = Roo.getDom(el);
5970         if(ename == "mousewheel" && el.addEventListener){
5971             el.removeEventListener("DOMMouseScroll", hd, false);
5972         }
5973         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5974             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5975         }
5976     };
5977
5978     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5979     
5980     var pub = {
5981         
5982         
5983         /** 
5984          * Fix for doc tools
5985          * @scope Roo.EventManager
5986          */
5987         
5988         
5989         /** 
5990          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5991          * object with a Roo.EventObject
5992          * @param {Function} fn        The method the event invokes
5993          * @param {Object}   scope    An object that becomes the scope of the handler
5994          * @param {boolean}  override If true, the obj passed in becomes
5995          *                             the execution scope of the listener
5996          * @return {Function} The wrapped function
5997          * @deprecated
5998          */
5999         wrap : function(fn, scope, override){
6000             return function(e){
6001                 Roo.EventObject.setEvent(e);
6002                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6003             };
6004         },
6005         
6006         /**
6007      * Appends an event handler to an element (shorthand for addListener)
6008      * @param {String/HTMLElement}   element        The html element or id to assign the
6009      * @param {String}   eventName The type of event to listen for
6010      * @param {Function} handler The method the event invokes
6011      * @param {Object}   scope (optional) The scope in which to execute the handler
6012      * function. The handler function's "this" context.
6013      * @param {Object}   options (optional) An object containing handler configuration
6014      * properties. This may contain any of the following properties:<ul>
6015      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6016      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6017      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6018      * <li>preventDefault {Boolean} True to prevent the default action</li>
6019      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6020      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6021      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6022      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6023      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6024      * by the specified number of milliseconds. If the event fires again within that time, the original
6025      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6026      * </ul><br>
6027      * <p>
6028      * <b>Combining Options</b><br>
6029      * Using the options argument, it is possible to combine different types of listeners:<br>
6030      * <br>
6031      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6032      * Code:<pre><code>
6033 el.on('click', this.onClick, this, {
6034     single: true,
6035     delay: 100,
6036     stopEvent : true,
6037     forumId: 4
6038 });</code></pre>
6039      * <p>
6040      * <b>Attaching multiple handlers in 1 call</b><br>
6041       * The method also allows for a single argument to be passed which is a config object containing properties
6042      * which specify multiple handlers.
6043      * <p>
6044      * Code:<pre><code>
6045 el.on({
6046     'click' : {
6047         fn: this.onClick
6048         scope: this,
6049         delay: 100
6050     },
6051     'mouseover' : {
6052         fn: this.onMouseOver
6053         scope: this
6054     },
6055     'mouseout' : {
6056         fn: this.onMouseOut
6057         scope: this
6058     }
6059 });</code></pre>
6060      * <p>
6061      * Or a shorthand syntax:<br>
6062      * Code:<pre><code>
6063 el.on({
6064     'click' : this.onClick,
6065     'mouseover' : this.onMouseOver,
6066     'mouseout' : this.onMouseOut
6067     scope: this
6068 });</code></pre>
6069      */
6070         addListener : function(element, eventName, fn, scope, options){
6071             if(typeof eventName == "object"){
6072                 var o = eventName;
6073                 for(var e in o){
6074                     if(propRe.test(e)){
6075                         continue;
6076                     }
6077                     if(typeof o[e] == "function"){
6078                         // shared options
6079                         listen(element, e, o, o[e], o.scope);
6080                     }else{
6081                         // individual options
6082                         listen(element, e, o[e]);
6083                     }
6084                 }
6085                 return;
6086             }
6087             return listen(element, eventName, options, fn, scope);
6088         },
6089         
6090         /**
6091          * Removes an event handler
6092          *
6093          * @param {String/HTMLElement}   element        The id or html element to remove the 
6094          *                             event from
6095          * @param {String}   eventName     The type of event
6096          * @param {Function} fn
6097          * @return {Boolean} True if a listener was actually removed
6098          */
6099         removeListener : function(element, eventName, fn){
6100             return stopListening(element, eventName, fn);
6101         },
6102         
6103         /**
6104          * Fires when the document is ready (before onload and before images are loaded). Can be 
6105          * accessed shorthanded Roo.onReady().
6106          * @param {Function} fn        The method the event invokes
6107          * @param {Object}   scope    An  object that becomes the scope of the handler
6108          * @param {boolean}  options
6109          */
6110         onDocumentReady : function(fn, scope, options){
6111             if(docReadyState){ // if it already fired
6112                 docReadyEvent.addListener(fn, scope, options);
6113                 docReadyEvent.fire();
6114                 docReadyEvent.clearListeners();
6115                 return;
6116             }
6117             if(!docReadyEvent){
6118                 initDocReady();
6119             }
6120             docReadyEvent.addListener(fn, scope, options);
6121         },
6122         
6123         /**
6124          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6125          * @param {Function} fn        The method the event invokes
6126          * @param {Object}   scope    An object that becomes the scope of the handler
6127          * @param {boolean}  options
6128          */
6129         onWindowResize : function(fn, scope, options){
6130             if(!resizeEvent){
6131                 resizeEvent = new Roo.util.Event();
6132                 resizeTask = new Roo.util.DelayedTask(function(){
6133                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6134                 });
6135                 E.on(window, "resize", function(){
6136                     if(Roo.isIE){
6137                         resizeTask.delay(50);
6138                     }else{
6139                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6140                     }
6141                 });
6142             }
6143             resizeEvent.addListener(fn, scope, options);
6144         },
6145
6146         /**
6147          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  options
6151          */
6152         onTextResize : function(fn, scope, options){
6153             if(!textEvent){
6154                 textEvent = new Roo.util.Event();
6155                 var textEl = new Roo.Element(document.createElement('div'));
6156                 textEl.dom.className = 'x-text-resize';
6157                 textEl.dom.innerHTML = 'X';
6158                 textEl.appendTo(document.body);
6159                 textSize = textEl.dom.offsetHeight;
6160                 setInterval(function(){
6161                     if(textEl.dom.offsetHeight != textSize){
6162                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6163                     }
6164                 }, this.textResizeInterval);
6165             }
6166             textEvent.addListener(fn, scope, options);
6167         },
6168
6169         /**
6170          * Removes the passed window resize listener.
6171          * @param {Function} fn        The method the event invokes
6172          * @param {Object}   scope    The scope of handler
6173          */
6174         removeResizeListener : function(fn, scope){
6175             if(resizeEvent){
6176                 resizeEvent.removeListener(fn, scope);
6177             }
6178         },
6179
6180         // private
6181         fireResize : function(){
6182             if(resizeEvent){
6183                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6184             }   
6185         },
6186         /**
6187          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6188          */
6189         ieDeferSrc : false,
6190         /**
6191          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6192          */
6193         textResizeInterval : 50
6194     };
6195     
6196     /**
6197      * Fix for doc tools
6198      * @scopeAlias pub=Roo.EventManager
6199      */
6200     
6201      /**
6202      * Appends an event handler to an element (shorthand for addListener)
6203      * @param {String/HTMLElement}   element        The html element or id to assign the
6204      * @param {String}   eventName The type of event to listen for
6205      * @param {Function} handler The method the event invokes
6206      * @param {Object}   scope (optional) The scope in which to execute the handler
6207      * function. The handler function's "this" context.
6208      * @param {Object}   options (optional) An object containing handler configuration
6209      * properties. This may contain any of the following properties:<ul>
6210      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6211      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6212      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6213      * <li>preventDefault {Boolean} True to prevent the default action</li>
6214      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6215      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6216      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6217      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6218      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6219      * by the specified number of milliseconds. If the event fires again within that time, the original
6220      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6221      * </ul><br>
6222      * <p>
6223      * <b>Combining Options</b><br>
6224      * Using the options argument, it is possible to combine different types of listeners:<br>
6225      * <br>
6226      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6227      * Code:<pre><code>
6228 el.on('click', this.onClick, this, {
6229     single: true,
6230     delay: 100,
6231     stopEvent : true,
6232     forumId: 4
6233 });</code></pre>
6234      * <p>
6235      * <b>Attaching multiple handlers in 1 call</b><br>
6236       * The method also allows for a single argument to be passed which is a config object containing properties
6237      * which specify multiple handlers.
6238      * <p>
6239      * Code:<pre><code>
6240 el.on({
6241     'click' : {
6242         fn: this.onClick
6243         scope: this,
6244         delay: 100
6245     },
6246     'mouseover' : {
6247         fn: this.onMouseOver
6248         scope: this
6249     },
6250     'mouseout' : {
6251         fn: this.onMouseOut
6252         scope: this
6253     }
6254 });</code></pre>
6255      * <p>
6256      * Or a shorthand syntax:<br>
6257      * Code:<pre><code>
6258 el.on({
6259     'click' : this.onClick,
6260     'mouseover' : this.onMouseOver,
6261     'mouseout' : this.onMouseOut
6262     scope: this
6263 });</code></pre>
6264      */
6265     pub.on = pub.addListener;
6266     pub.un = pub.removeListener;
6267
6268     pub.stoppedMouseDownEvent = new Roo.util.Event();
6269     return pub;
6270 }();
6271 /**
6272   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6273   * @param {Function} fn        The method the event invokes
6274   * @param {Object}   scope    An  object that becomes the scope of the handler
6275   * @param {boolean}  override If true, the obj passed in becomes
6276   *                             the execution scope of the listener
6277   * @member Roo
6278   * @method onReady
6279  */
6280 Roo.onReady = Roo.EventManager.onDocumentReady;
6281
6282 Roo.onReady(function(){
6283     var bd = Roo.get(document.body);
6284     if(!bd){ return; }
6285
6286     var cls = [
6287             Roo.isIE ? "roo-ie"
6288             : Roo.isGecko ? "roo-gecko"
6289             : Roo.isOpera ? "roo-opera"
6290             : Roo.isSafari ? "roo-safari" : ""];
6291
6292     if(Roo.isMac){
6293         cls.push("roo-mac");
6294     }
6295     if(Roo.isLinux){
6296         cls.push("roo-linux");
6297     }
6298     if(Roo.isBorderBox){
6299         cls.push('roo-border-box');
6300     }
6301     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6302         var p = bd.dom.parentNode;
6303         if(p){
6304             p.className += ' roo-strict';
6305         }
6306     }
6307     bd.addClass(cls.join(' '));
6308 });
6309
6310 /**
6311  * @class Roo.EventObject
6312  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6313  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6314  * Example:
6315  * <pre><code>
6316  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6317     e.preventDefault();
6318     var target = e.getTarget();
6319     ...
6320  }
6321  var myDiv = Roo.get("myDiv");
6322  myDiv.on("click", handleClick);
6323  //or
6324  Roo.EventManager.on("myDiv", 'click', handleClick);
6325  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6326  </code></pre>
6327  * @singleton
6328  */
6329 Roo.EventObject = function(){
6330     
6331     var E = Roo.lib.Event;
6332     
6333     // safari keypress events for special keys return bad keycodes
6334     var safariKeys = {
6335         63234 : 37, // left
6336         63235 : 39, // right
6337         63232 : 38, // up
6338         63233 : 40, // down
6339         63276 : 33, // page up
6340         63277 : 34, // page down
6341         63272 : 46, // delete
6342         63273 : 36, // home
6343         63275 : 35  // end
6344     };
6345
6346     // normalize button clicks
6347     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6348                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6349
6350     Roo.EventObjectImpl = function(e){
6351         if(e){
6352             this.setEvent(e.browserEvent || e);
6353         }
6354     };
6355     Roo.EventObjectImpl.prototype = {
6356         /**
6357          * Used to fix doc tools.
6358          * @scope Roo.EventObject.prototype
6359          */
6360             
6361
6362         
6363         
6364         /** The normal browser event */
6365         browserEvent : null,
6366         /** The button pressed in a mouse event */
6367         button : -1,
6368         /** True if the shift key was down during the event */
6369         shiftKey : false,
6370         /** True if the control key was down during the event */
6371         ctrlKey : false,
6372         /** True if the alt key was down during the event */
6373         altKey : false,
6374
6375         /** Key constant 
6376         * @type Number */
6377         BACKSPACE : 8,
6378         /** Key constant 
6379         * @type Number */
6380         TAB : 9,
6381         /** Key constant 
6382         * @type Number */
6383         RETURN : 13,
6384         /** Key constant 
6385         * @type Number */
6386         ENTER : 13,
6387         /** Key constant 
6388         * @type Number */
6389         SHIFT : 16,
6390         /** Key constant 
6391         * @type Number */
6392         CONTROL : 17,
6393         /** Key constant 
6394         * @type Number */
6395         ESC : 27,
6396         /** Key constant 
6397         * @type Number */
6398         SPACE : 32,
6399         /** Key constant 
6400         * @type Number */
6401         PAGEUP : 33,
6402         /** Key constant 
6403         * @type Number */
6404         PAGEDOWN : 34,
6405         /** Key constant 
6406         * @type Number */
6407         END : 35,
6408         /** Key constant 
6409         * @type Number */
6410         HOME : 36,
6411         /** Key constant 
6412         * @type Number */
6413         LEFT : 37,
6414         /** Key constant 
6415         * @type Number */
6416         UP : 38,
6417         /** Key constant 
6418         * @type Number */
6419         RIGHT : 39,
6420         /** Key constant 
6421         * @type Number */
6422         DOWN : 40,
6423         /** Key constant 
6424         * @type Number */
6425         DELETE : 46,
6426         /** Key constant 
6427         * @type Number */
6428         F5 : 116,
6429
6430            /** @private */
6431         setEvent : function(e){
6432             if(e == this || (e && e.browserEvent)){ // already wrapped
6433                 return e;
6434             }
6435             this.browserEvent = e;
6436             if(e){
6437                 // normalize buttons
6438                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6439                 if(e.type == 'click' && this.button == -1){
6440                     this.button = 0;
6441                 }
6442                 this.type = e.type;
6443                 this.shiftKey = e.shiftKey;
6444                 // mac metaKey behaves like ctrlKey
6445                 this.ctrlKey = e.ctrlKey || e.metaKey;
6446                 this.altKey = e.altKey;
6447                 // in getKey these will be normalized for the mac
6448                 this.keyCode = e.keyCode;
6449                 // keyup warnings on firefox.
6450                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6451                 // cache the target for the delayed and or buffered events
6452                 this.target = E.getTarget(e);
6453                 // same for XY
6454                 this.xy = E.getXY(e);
6455             }else{
6456                 this.button = -1;
6457                 this.shiftKey = false;
6458                 this.ctrlKey = false;
6459                 this.altKey = false;
6460                 this.keyCode = 0;
6461                 this.charCode =0;
6462                 this.target = null;
6463                 this.xy = [0, 0];
6464             }
6465             return this;
6466         },
6467
6468         /**
6469          * Stop the event (preventDefault and stopPropagation)
6470          */
6471         stopEvent : function(){
6472             if(this.browserEvent){
6473                 if(this.browserEvent.type == 'mousedown'){
6474                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6475                 }
6476                 E.stopEvent(this.browserEvent);
6477             }
6478         },
6479
6480         /**
6481          * Prevents the browsers default handling of the event.
6482          */
6483         preventDefault : function(){
6484             if(this.browserEvent){
6485                 E.preventDefault(this.browserEvent);
6486             }
6487         },
6488
6489         /** @private */
6490         isNavKeyPress : function(){
6491             var k = this.keyCode;
6492             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6493             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6494         },
6495
6496         isSpecialKey : function(){
6497             var k = this.keyCode;
6498             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6499             (k == 16) || (k == 17) ||
6500             (k >= 18 && k <= 20) ||
6501             (k >= 33 && k <= 35) ||
6502             (k >= 36 && k <= 39) ||
6503             (k >= 44 && k <= 45);
6504         },
6505         /**
6506          * Cancels bubbling of the event.
6507          */
6508         stopPropagation : function(){
6509             if(this.browserEvent){
6510                 if(this.type == 'mousedown'){
6511                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6512                 }
6513                 E.stopPropagation(this.browserEvent);
6514             }
6515         },
6516
6517         /**
6518          * Gets the key code for the event.
6519          * @return {Number}
6520          */
6521         getCharCode : function(){
6522             return this.charCode || this.keyCode;
6523         },
6524
6525         /**
6526          * Returns a normalized keyCode for the event.
6527          * @return {Number} The key code
6528          */
6529         getKey : function(){
6530             var k = this.keyCode || this.charCode;
6531             return Roo.isSafari ? (safariKeys[k] || k) : k;
6532         },
6533
6534         /**
6535          * Gets the x coordinate of the event.
6536          * @return {Number}
6537          */
6538         getPageX : function(){
6539             return this.xy[0];
6540         },
6541
6542         /**
6543          * Gets the y coordinate of the event.
6544          * @return {Number}
6545          */
6546         getPageY : function(){
6547             return this.xy[1];
6548         },
6549
6550         /**
6551          * Gets the time of the event.
6552          * @return {Number}
6553          */
6554         getTime : function(){
6555             if(this.browserEvent){
6556                 return E.getTime(this.browserEvent);
6557             }
6558             return null;
6559         },
6560
6561         /**
6562          * Gets the page coordinates of the event.
6563          * @return {Array} The xy values like [x, y]
6564          */
6565         getXY : function(){
6566             return this.xy;
6567         },
6568
6569         /**
6570          * Gets the target for the event.
6571          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6572          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6573                 search as a number or element (defaults to 10 || document.body)
6574          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6575          * @return {HTMLelement}
6576          */
6577         getTarget : function(selector, maxDepth, returnEl){
6578             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6579         },
6580         /**
6581          * Gets the related target.
6582          * @return {HTMLElement}
6583          */
6584         getRelatedTarget : function(){
6585             if(this.browserEvent){
6586                 return E.getRelatedTarget(this.browserEvent);
6587             }
6588             return null;
6589         },
6590
6591         /**
6592          * Normalizes mouse wheel delta across browsers
6593          * @return {Number} The delta
6594          */
6595         getWheelDelta : function(){
6596             var e = this.browserEvent;
6597             var delta = 0;
6598             if(e.wheelDelta){ /* IE/Opera. */
6599                 delta = e.wheelDelta/120;
6600             }else if(e.detail){ /* Mozilla case. */
6601                 delta = -e.detail/3;
6602             }
6603             return delta;
6604         },
6605
6606         /**
6607          * Returns true if the control, meta, shift or alt key was pressed during this event.
6608          * @return {Boolean}
6609          */
6610         hasModifier : function(){
6611             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6612         },
6613
6614         /**
6615          * Returns true if the target of this event equals el or is a child of el
6616          * @param {String/HTMLElement/Element} el
6617          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6618          * @return {Boolean}
6619          */
6620         within : function(el, related){
6621             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6622             return t && Roo.fly(el).contains(t);
6623         },
6624
6625         getPoint : function(){
6626             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6627         }
6628     };
6629
6630     return new Roo.EventObjectImpl();
6631 }();
6632             
6633     /*
6634  * Based on:
6635  * Ext JS Library 1.1.1
6636  * Copyright(c) 2006-2007, Ext JS, LLC.
6637  *
6638  * Originally Released Under LGPL - original licence link has changed is not relivant.
6639  *
6640  * Fork - LGPL
6641  * <script type="text/javascript">
6642  */
6643
6644  
6645 // was in Composite Element!??!?!
6646  
6647 (function(){
6648     var D = Roo.lib.Dom;
6649     var E = Roo.lib.Event;
6650     var A = Roo.lib.Anim;
6651
6652     // local style camelizing for speed
6653     var propCache = {};
6654     var camelRe = /(-[a-z])/gi;
6655     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6656     var view = document.defaultView;
6657
6658 /**
6659  * @class Roo.Element
6660  * Represents an Element in the DOM.<br><br>
6661  * Usage:<br>
6662 <pre><code>
6663 var el = Roo.get("my-div");
6664
6665 // or with getEl
6666 var el = getEl("my-div");
6667
6668 // or with a DOM element
6669 var el = Roo.get(myDivElement);
6670 </code></pre>
6671  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6672  * each call instead of constructing a new one.<br><br>
6673  * <b>Animations</b><br />
6674  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6675  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6676 <pre>
6677 Option    Default   Description
6678 --------- --------  ---------------------------------------------
6679 duration  .35       The duration of the animation in seconds
6680 easing    easeOut   The YUI easing method
6681 callback  none      A function to execute when the anim completes
6682 scope     this      The scope (this) of the callback function
6683 </pre>
6684 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6685 * manipulate the animation. Here's an example:
6686 <pre><code>
6687 var el = Roo.get("my-div");
6688
6689 // no animation
6690 el.setWidth(100);
6691
6692 // default animation
6693 el.setWidth(100, true);
6694
6695 // animation with some options set
6696 el.setWidth(100, {
6697     duration: 1,
6698     callback: this.foo,
6699     scope: this
6700 });
6701
6702 // using the "anim" property to get the Anim object
6703 var opt = {
6704     duration: 1,
6705     callback: this.foo,
6706     scope: this
6707 };
6708 el.setWidth(100, opt);
6709 ...
6710 if(opt.anim.isAnimated()){
6711     opt.anim.stop();
6712 }
6713 </code></pre>
6714 * <b> Composite (Collections of) Elements</b><br />
6715  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6716  * @constructor Create a new Element directly.
6717  * @param {String/HTMLElement} element
6718  * @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).
6719  */
6720     Roo.Element = function(element, forceNew){
6721         var dom = typeof element == "string" ?
6722                 document.getElementById(element) : element;
6723         if(!dom){ // invalid id/element
6724             return null;
6725         }
6726         var id = dom.id;
6727         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6728             return Roo.Element.cache[id];
6729         }
6730
6731         /**
6732          * The DOM element
6733          * @type HTMLElement
6734          */
6735         this.dom = dom;
6736
6737         /**
6738          * The DOM element ID
6739          * @type String
6740          */
6741         this.id = id || Roo.id(dom);
6742     };
6743
6744     var El = Roo.Element;
6745
6746     El.prototype = {
6747         /**
6748          * The element's default display mode  (defaults to "")
6749          * @type String
6750          */
6751         originalDisplay : "",
6752
6753         visibilityMode : 1,
6754         /**
6755          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6756          * @type String
6757          */
6758         defaultUnit : "px",
6759         /**
6760          * Sets the element's visibility mode. When setVisible() is called it
6761          * will use this to determine whether to set the visibility or the display property.
6762          * @param visMode Element.VISIBILITY or Element.DISPLAY
6763          * @return {Roo.Element} this
6764          */
6765         setVisibilityMode : function(visMode){
6766             this.visibilityMode = visMode;
6767             return this;
6768         },
6769         /**
6770          * Convenience method for setVisibilityMode(Element.DISPLAY)
6771          * @param {String} display (optional) What to set display to when visible
6772          * @return {Roo.Element} this
6773          */
6774         enableDisplayMode : function(display){
6775             this.setVisibilityMode(El.DISPLAY);
6776             if(typeof display != "undefined") this.originalDisplay = display;
6777             return this;
6778         },
6779
6780         /**
6781          * 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)
6782          * @param {String} selector The simple selector to test
6783          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6784                 search as a number or element (defaults to 10 || document.body)
6785          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6786          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6787          */
6788         findParent : function(simpleSelector, maxDepth, returnEl){
6789             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6790             maxDepth = maxDepth || 50;
6791             if(typeof maxDepth != "number"){
6792                 stopEl = Roo.getDom(maxDepth);
6793                 maxDepth = 10;
6794             }
6795             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6796                 if(dq.is(p, simpleSelector)){
6797                     return returnEl ? Roo.get(p) : p;
6798                 }
6799                 depth++;
6800                 p = p.parentNode;
6801             }
6802             return null;
6803         },
6804
6805
6806         /**
6807          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6808          * @param {String} selector The simple selector to test
6809          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6810                 search as a number or element (defaults to 10 || document.body)
6811          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6812          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6813          */
6814         findParentNode : function(simpleSelector, maxDepth, returnEl){
6815             var p = Roo.fly(this.dom.parentNode, '_internal');
6816             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6817         },
6818
6819         /**
6820          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6821          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6822          * @param {String} selector The simple selector to test
6823          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6824                 search as a number or element (defaults to 10 || document.body)
6825          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6826          */
6827         up : function(simpleSelector, maxDepth){
6828             return this.findParentNode(simpleSelector, maxDepth, true);
6829         },
6830
6831
6832
6833         /**
6834          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6835          * @param {String} selector The simple selector to test
6836          * @return {Boolean} True if this element matches the selector, else false
6837          */
6838         is : function(simpleSelector){
6839             return Roo.DomQuery.is(this.dom, simpleSelector);
6840         },
6841
6842         /**
6843          * Perform animation on this element.
6844          * @param {Object} args The YUI animation control args
6845          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6846          * @param {Function} onComplete (optional) Function to call when animation completes
6847          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6848          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6849          * @return {Roo.Element} this
6850          */
6851         animate : function(args, duration, onComplete, easing, animType){
6852             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6853             return this;
6854         },
6855
6856         /*
6857          * @private Internal animation call
6858          */
6859         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6860             animType = animType || 'run';
6861             opt = opt || {};
6862             var anim = Roo.lib.Anim[animType](
6863                 this.dom, args,
6864                 (opt.duration || defaultDur) || .35,
6865                 (opt.easing || defaultEase) || 'easeOut',
6866                 function(){
6867                     Roo.callback(cb, this);
6868                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6869                 },
6870                 this
6871             );
6872             opt.anim = anim;
6873             return anim;
6874         },
6875
6876         // private legacy anim prep
6877         preanim : function(a, i){
6878             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6879         },
6880
6881         /**
6882          * Removes worthless text nodes
6883          * @param {Boolean} forceReclean (optional) By default the element
6884          * keeps track if it has been cleaned already so
6885          * you can call this over and over. However, if you update the element and
6886          * need to force a reclean, you can pass true.
6887          */
6888         clean : function(forceReclean){
6889             if(this.isCleaned && forceReclean !== true){
6890                 return this;
6891             }
6892             var ns = /\S/;
6893             var d = this.dom, n = d.firstChild, ni = -1;
6894             while(n){
6895                 var nx = n.nextSibling;
6896                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6897                     d.removeChild(n);
6898                 }else{
6899                     n.nodeIndex = ++ni;
6900                 }
6901                 n = nx;
6902             }
6903             this.isCleaned = true;
6904             return this;
6905         },
6906
6907         // private
6908         calcOffsetsTo : function(el){
6909             el = Roo.get(el);
6910             var d = el.dom;
6911             var restorePos = false;
6912             if(el.getStyle('position') == 'static'){
6913                 el.position('relative');
6914                 restorePos = true;
6915             }
6916             var x = 0, y =0;
6917             var op = this.dom;
6918             while(op && op != d && op.tagName != 'HTML'){
6919                 x+= op.offsetLeft;
6920                 y+= op.offsetTop;
6921                 op = op.offsetParent;
6922             }
6923             if(restorePos){
6924                 el.position('static');
6925             }
6926             return [x, y];
6927         },
6928
6929         /**
6930          * Scrolls this element into view within the passed container.
6931          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6932          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6933          * @return {Roo.Element} this
6934          */
6935         scrollIntoView : function(container, hscroll){
6936             var c = Roo.getDom(container) || document.body;
6937             var el = this.dom;
6938
6939             var o = this.calcOffsetsTo(c),
6940                 l = o[0],
6941                 t = o[1],
6942                 b = t+el.offsetHeight,
6943                 r = l+el.offsetWidth;
6944
6945             var ch = c.clientHeight;
6946             var ct = parseInt(c.scrollTop, 10);
6947             var cl = parseInt(c.scrollLeft, 10);
6948             var cb = ct + ch;
6949             var cr = cl + c.clientWidth;
6950
6951             if(t < ct){
6952                 c.scrollTop = t;
6953             }else if(b > cb){
6954                 c.scrollTop = b-ch;
6955             }
6956
6957             if(hscroll !== false){
6958                 if(l < cl){
6959                     c.scrollLeft = l;
6960                 }else if(r > cr){
6961                     c.scrollLeft = r-c.clientWidth;
6962                 }
6963             }
6964             return this;
6965         },
6966
6967         // private
6968         scrollChildIntoView : function(child, hscroll){
6969             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6970         },
6971
6972         /**
6973          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6974          * the new height may not be available immediately.
6975          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6976          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6977          * @param {Function} onComplete (optional) Function to call when animation completes
6978          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6979          * @return {Roo.Element} this
6980          */
6981         autoHeight : function(animate, duration, onComplete, easing){
6982             var oldHeight = this.getHeight();
6983             this.clip();
6984             this.setHeight(1); // force clipping
6985             setTimeout(function(){
6986                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6987                 if(!animate){
6988                     this.setHeight(height);
6989                     this.unclip();
6990                     if(typeof onComplete == "function"){
6991                         onComplete();
6992                     }
6993                 }else{
6994                     this.setHeight(oldHeight); // restore original height
6995                     this.setHeight(height, animate, duration, function(){
6996                         this.unclip();
6997                         if(typeof onComplete == "function") onComplete();
6998                     }.createDelegate(this), easing);
6999                 }
7000             }.createDelegate(this), 0);
7001             return this;
7002         },
7003
7004         /**
7005          * Returns true if this element is an ancestor of the passed element
7006          * @param {HTMLElement/String} el The element to check
7007          * @return {Boolean} True if this element is an ancestor of el, else false
7008          */
7009         contains : function(el){
7010             if(!el){return false;}
7011             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7012         },
7013
7014         /**
7015          * Checks whether the element is currently visible using both visibility and display properties.
7016          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7017          * @return {Boolean} True if the element is currently visible, else false
7018          */
7019         isVisible : function(deep) {
7020             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7021             if(deep !== true || !vis){
7022                 return vis;
7023             }
7024             var p = this.dom.parentNode;
7025             while(p && p.tagName.toLowerCase() != "body"){
7026                 if(!Roo.fly(p, '_isVisible').isVisible()){
7027                     return false;
7028                 }
7029                 p = p.parentNode;
7030             }
7031             return true;
7032         },
7033
7034         /**
7035          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7036          * @param {String} selector The CSS selector
7037          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7038          * @return {CompositeElement/CompositeElementLite} The composite element
7039          */
7040         select : function(selector, unique){
7041             return El.select(selector, unique, this.dom);
7042         },
7043
7044         /**
7045          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7046          * @param {String} selector The CSS selector
7047          * @return {Array} An array of the matched nodes
7048          */
7049         query : function(selector, unique){
7050             return Roo.DomQuery.select(selector, this.dom);
7051         },
7052
7053         /**
7054          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7055          * @param {String} selector The CSS selector
7056          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7057          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7058          */
7059         child : function(selector, returnDom){
7060             var n = Roo.DomQuery.selectNode(selector, this.dom);
7061             return returnDom ? n : Roo.get(n);
7062         },
7063
7064         /**
7065          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7066          * @param {String} selector The CSS selector
7067          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7068          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7069          */
7070         down : function(selector, returnDom){
7071             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7072             return returnDom ? n : Roo.get(n);
7073         },
7074
7075         /**
7076          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7077          * @param {String} group The group the DD object is member of
7078          * @param {Object} config The DD config object
7079          * @param {Object} overrides An object containing methods to override/implement on the DD object
7080          * @return {Roo.dd.DD} The DD object
7081          */
7082         initDD : function(group, config, overrides){
7083             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7084             return Roo.apply(dd, overrides);
7085         },
7086
7087         /**
7088          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7089          * @param {String} group The group the DDProxy object is member of
7090          * @param {Object} config The DDProxy config object
7091          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7092          * @return {Roo.dd.DDProxy} The DDProxy object
7093          */
7094         initDDProxy : function(group, config, overrides){
7095             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7096             return Roo.apply(dd, overrides);
7097         },
7098
7099         /**
7100          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7101          * @param {String} group The group the DDTarget object is member of
7102          * @param {Object} config The DDTarget config object
7103          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7104          * @return {Roo.dd.DDTarget} The DDTarget object
7105          */
7106         initDDTarget : function(group, config, overrides){
7107             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7108             return Roo.apply(dd, overrides);
7109         },
7110
7111         /**
7112          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7113          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7114          * @param {Boolean} visible Whether the element is visible
7115          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7116          * @return {Roo.Element} this
7117          */
7118          setVisible : function(visible, animate){
7119             if(!animate || !A){
7120                 if(this.visibilityMode == El.DISPLAY){
7121                     this.setDisplayed(visible);
7122                 }else{
7123                     this.fixDisplay();
7124                     this.dom.style.visibility = visible ? "visible" : "hidden";
7125                 }
7126             }else{
7127                 // closure for composites
7128                 var dom = this.dom;
7129                 var visMode = this.visibilityMode;
7130                 if(visible){
7131                     this.setOpacity(.01);
7132                     this.setVisible(true);
7133                 }
7134                 this.anim({opacity: { to: (visible?1:0) }},
7135                       this.preanim(arguments, 1),
7136                       null, .35, 'easeIn', function(){
7137                          if(!visible){
7138                              if(visMode == El.DISPLAY){
7139                                  dom.style.display = "none";
7140                              }else{
7141                                  dom.style.visibility = "hidden";
7142                              }
7143                              Roo.get(dom).setOpacity(1);
7144                          }
7145                      });
7146             }
7147             return this;
7148         },
7149
7150         /**
7151          * Returns true if display is not "none"
7152          * @return {Boolean}
7153          */
7154         isDisplayed : function() {
7155             return this.getStyle("display") != "none";
7156         },
7157
7158         /**
7159          * Toggles the element's visibility or display, depending on visibility mode.
7160          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7161          * @return {Roo.Element} this
7162          */
7163         toggle : function(animate){
7164             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7165             return this;
7166         },
7167
7168         /**
7169          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7170          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7171          * @return {Roo.Element} this
7172          */
7173         setDisplayed : function(value) {
7174             if(typeof value == "boolean"){
7175                value = value ? this.originalDisplay : "none";
7176             }
7177             this.setStyle("display", value);
7178             return this;
7179         },
7180
7181         /**
7182          * Tries to focus the element. Any exceptions are caught and ignored.
7183          * @return {Roo.Element} this
7184          */
7185         focus : function() {
7186             try{
7187                 this.dom.focus();
7188             }catch(e){}
7189             return this;
7190         },
7191
7192         /**
7193          * Tries to blur the element. Any exceptions are caught and ignored.
7194          * @return {Roo.Element} this
7195          */
7196         blur : function() {
7197             try{
7198                 this.dom.blur();
7199             }catch(e){}
7200             return this;
7201         },
7202
7203         /**
7204          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7205          * @param {String/Array} className The CSS class to add, or an array of classes
7206          * @return {Roo.Element} this
7207          */
7208         addClass : function(className){
7209             if(className instanceof Array){
7210                 for(var i = 0, len = className.length; i < len; i++) {
7211                     this.addClass(className[i]);
7212                 }
7213             }else{
7214                 if(className && !this.hasClass(className)){
7215                     this.dom.className = this.dom.className + " " + className;
7216                 }
7217             }
7218             return this;
7219         },
7220
7221         /**
7222          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7223          * @param {String/Array} className The CSS class to add, or an array of classes
7224          * @return {Roo.Element} this
7225          */
7226         radioClass : function(className){
7227             var siblings = this.dom.parentNode.childNodes;
7228             for(var i = 0; i < siblings.length; i++) {
7229                 var s = siblings[i];
7230                 if(s.nodeType == 1){
7231                     Roo.get(s).removeClass(className);
7232                 }
7233             }
7234             this.addClass(className);
7235             return this;
7236         },
7237
7238         /**
7239          * Removes one or more CSS classes from the element.
7240          * @param {String/Array} className The CSS class to remove, or an array of classes
7241          * @return {Roo.Element} this
7242          */
7243         removeClass : function(className){
7244             if(!className || !this.dom.className){
7245                 return this;
7246             }
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.removeClass(className[i]);
7250                 }
7251             }else{
7252                 if(this.hasClass(className)){
7253                     var re = this.classReCache[className];
7254                     if (!re) {
7255                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7256                        this.classReCache[className] = re;
7257                     }
7258                     this.dom.className =
7259                         this.dom.className.replace(re, " ");
7260                 }
7261             }
7262             return this;
7263         },
7264
7265         // private
7266         classReCache: {},
7267
7268         /**
7269          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7270          * @param {String} className The CSS class to toggle
7271          * @return {Roo.Element} this
7272          */
7273         toggleClass : function(className){
7274             if(this.hasClass(className)){
7275                 this.removeClass(className);
7276             }else{
7277                 this.addClass(className);
7278             }
7279             return this;
7280         },
7281
7282         /**
7283          * Checks if the specified CSS class exists on this element's DOM node.
7284          * @param {String} className The CSS class to check for
7285          * @return {Boolean} True if the class exists, else false
7286          */
7287         hasClass : function(className){
7288             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7289         },
7290
7291         /**
7292          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7293          * @param {String} oldClassName The CSS class to replace
7294          * @param {String} newClassName The replacement CSS class
7295          * @return {Roo.Element} this
7296          */
7297         replaceClass : function(oldClassName, newClassName){
7298             this.removeClass(oldClassName);
7299             this.addClass(newClassName);
7300             return this;
7301         },
7302
7303         /**
7304          * Returns an object with properties matching the styles requested.
7305          * For example, el.getStyles('color', 'font-size', 'width') might return
7306          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7307          * @param {String} style1 A style name
7308          * @param {String} style2 A style name
7309          * @param {String} etc.
7310          * @return {Object} The style object
7311          */
7312         getStyles : function(){
7313             var a = arguments, len = a.length, r = {};
7314             for(var i = 0; i < len; i++){
7315                 r[a[i]] = this.getStyle(a[i]);
7316             }
7317             return r;
7318         },
7319
7320         /**
7321          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7322          * @param {String} property The style property whose value is returned.
7323          * @return {String} The current value of the style property for this element.
7324          */
7325         getStyle : function(){
7326             return view && view.getComputedStyle ?
7327                 function(prop){
7328                     var el = this.dom, v, cs, camel;
7329                     if(prop == 'float'){
7330                         prop = "cssFloat";
7331                     }
7332                     if(el.style && (v = el.style[prop])){
7333                         return v;
7334                     }
7335                     if(cs = view.getComputedStyle(el, "")){
7336                         if(!(camel = propCache[prop])){
7337                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7338                         }
7339                         return cs[camel];
7340                     }
7341                     return null;
7342                 } :
7343                 function(prop){
7344                     var el = this.dom, v, cs, camel;
7345                     if(prop == 'opacity'){
7346                         if(typeof el.style.filter == 'string'){
7347                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7348                             if(m){
7349                                 var fv = parseFloat(m[1]);
7350                                 if(!isNaN(fv)){
7351                                     return fv ? fv / 100 : 0;
7352                                 }
7353                             }
7354                         }
7355                         return 1;
7356                     }else if(prop == 'float'){
7357                         prop = "styleFloat";
7358                     }
7359                     if(!(camel = propCache[prop])){
7360                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7361                     }
7362                     if(v = el.style[camel]){
7363                         return v;
7364                     }
7365                     if(cs = el.currentStyle){
7366                         return cs[camel];
7367                     }
7368                     return null;
7369                 };
7370         }(),
7371
7372         /**
7373          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7374          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7375          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7376          * @return {Roo.Element} this
7377          */
7378         setStyle : function(prop, value){
7379             if(typeof prop == "string"){
7380                 var camel;
7381                 if(!(camel = propCache[prop])){
7382                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7383                 }
7384                 if(camel == 'opacity') {
7385                     this.setOpacity(value);
7386                 }else{
7387                     this.dom.style[camel] = value;
7388                 }
7389             }else{
7390                 for(var style in prop){
7391                     if(typeof prop[style] != "function"){
7392                        this.setStyle(style, prop[style]);
7393                     }
7394                 }
7395             }
7396             return this;
7397         },
7398
7399         /**
7400          * More flexible version of {@link #setStyle} for setting style properties.
7401          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7402          * a function which returns such a specification.
7403          * @return {Roo.Element} this
7404          */
7405         applyStyles : function(style){
7406             Roo.DomHelper.applyStyles(this.dom, style);
7407             return this;
7408         },
7409
7410         /**
7411           * 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).
7412           * @return {Number} The X position of the element
7413           */
7414         getX : function(){
7415             return D.getX(this.dom);
7416         },
7417
7418         /**
7419           * 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).
7420           * @return {Number} The Y position of the element
7421           */
7422         getY : function(){
7423             return D.getY(this.dom);
7424         },
7425
7426         /**
7427           * 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).
7428           * @return {Array} The XY position of the element
7429           */
7430         getXY : function(){
7431             return D.getXY(this.dom);
7432         },
7433
7434         /**
7435          * 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).
7436          * @param {Number} The X position of the element
7437          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7438          * @return {Roo.Element} this
7439          */
7440         setX : function(x, animate){
7441             if(!animate || !A){
7442                 D.setX(this.dom, x);
7443             }else{
7444                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7445             }
7446             return this;
7447         },
7448
7449         /**
7450          * 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).
7451          * @param {Number} The Y position of the element
7452          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7453          * @return {Roo.Element} this
7454          */
7455         setY : function(y, animate){
7456             if(!animate || !A){
7457                 D.setY(this.dom, y);
7458             }else{
7459                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7460             }
7461             return this;
7462         },
7463
7464         /**
7465          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7466          * @param {String} left The left CSS property value
7467          * @return {Roo.Element} this
7468          */
7469         setLeft : function(left){
7470             this.setStyle("left", this.addUnits(left));
7471             return this;
7472         },
7473
7474         /**
7475          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7476          * @param {String} top The top CSS property value
7477          * @return {Roo.Element} this
7478          */
7479         setTop : function(top){
7480             this.setStyle("top", this.addUnits(top));
7481             return this;
7482         },
7483
7484         /**
7485          * Sets the element's CSS right style.
7486          * @param {String} right The right CSS property value
7487          * @return {Roo.Element} this
7488          */
7489         setRight : function(right){
7490             this.setStyle("right", this.addUnits(right));
7491             return this;
7492         },
7493
7494         /**
7495          * Sets the element's CSS bottom style.
7496          * @param {String} bottom The bottom CSS property value
7497          * @return {Roo.Element} this
7498          */
7499         setBottom : function(bottom){
7500             this.setStyle("bottom", this.addUnits(bottom));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7508          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7509          * @return {Roo.Element} this
7510          */
7511         setXY : function(pos, animate){
7512             if(!animate || !A){
7513                 D.setXY(this.dom, pos);
7514             }else{
7515                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7516             }
7517             return this;
7518         },
7519
7520         /**
7521          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7522          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7523          * @param {Number} x X value for new position (coordinates are page-based)
7524          * @param {Number} y Y value for new position (coordinates are page-based)
7525          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7526          * @return {Roo.Element} this
7527          */
7528         setLocation : function(x, y, animate){
7529             this.setXY([x, y], this.preanim(arguments, 2));
7530             return this;
7531         },
7532
7533         /**
7534          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7535          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7536          * @param {Number} x X value for new position (coordinates are page-based)
7537          * @param {Number} y Y value for new position (coordinates are page-based)
7538          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7539          * @return {Roo.Element} this
7540          */
7541         moveTo : function(x, y, animate){
7542             this.setXY([x, y], this.preanim(arguments, 2));
7543             return this;
7544         },
7545
7546         /**
7547          * Returns the region of the given element.
7548          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7549          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7550          */
7551         getRegion : function(){
7552             return D.getRegion(this.dom);
7553         },
7554
7555         /**
7556          * Returns the offset height of the element
7557          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7558          * @return {Number} The element's height
7559          */
7560         getHeight : function(contentHeight){
7561             var h = this.dom.offsetHeight || 0;
7562             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7563         },
7564
7565         /**
7566          * Returns the offset width of the element
7567          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7568          * @return {Number} The element's width
7569          */
7570         getWidth : function(contentWidth){
7571             var w = this.dom.offsetWidth || 0;
7572             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7573         },
7574
7575         /**
7576          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7577          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7578          * if a height has not been set using CSS.
7579          * @return {Number}
7580          */
7581         getComputedHeight : function(){
7582             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7583             if(!h){
7584                 h = parseInt(this.getStyle('height'), 10) || 0;
7585                 if(!this.isBorderBox()){
7586                     h += this.getFrameWidth('tb');
7587                 }
7588             }
7589             return h;
7590         },
7591
7592         /**
7593          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7594          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7595          * if a width has not been set using CSS.
7596          * @return {Number}
7597          */
7598         getComputedWidth : function(){
7599             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7600             if(!w){
7601                 w = parseInt(this.getStyle('width'), 10) || 0;
7602                 if(!this.isBorderBox()){
7603                     w += this.getFrameWidth('lr');
7604                 }
7605             }
7606             return w;
7607         },
7608
7609         /**
7610          * Returns the size of the element.
7611          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7612          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7613          */
7614         getSize : function(contentSize){
7615             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7616         },
7617
7618         /**
7619          * Returns the width and height of the viewport.
7620          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7621          */
7622         getViewSize : function(){
7623             var d = this.dom, doc = document, aw = 0, ah = 0;
7624             if(d == doc || d == doc.body){
7625                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7626             }else{
7627                 return {
7628                     width : d.clientWidth,
7629                     height: d.clientHeight
7630                 };
7631             }
7632         },
7633
7634         /**
7635          * Returns the value of the "value" attribute
7636          * @param {Boolean} asNumber true to parse the value as a number
7637          * @return {String/Number}
7638          */
7639         getValue : function(asNumber){
7640             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7641         },
7642
7643         // private
7644         adjustWidth : function(width){
7645             if(typeof width == "number"){
7646                 if(this.autoBoxAdjust && !this.isBorderBox()){
7647                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7648                 }
7649                 if(width < 0){
7650                     width = 0;
7651                 }
7652             }
7653             return width;
7654         },
7655
7656         // private
7657         adjustHeight : function(height){
7658             if(typeof height == "number"){
7659                if(this.autoBoxAdjust && !this.isBorderBox()){
7660                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7661                }
7662                if(height < 0){
7663                    height = 0;
7664                }
7665             }
7666             return height;
7667         },
7668
7669         /**
7670          * Set the width of the element
7671          * @param {Number} width The new width
7672          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7673          * @return {Roo.Element} this
7674          */
7675         setWidth : function(width, animate){
7676             width = this.adjustWidth(width);
7677             if(!animate || !A){
7678                 this.dom.style.width = this.addUnits(width);
7679             }else{
7680                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * Set the height of the element
7687          * @param {Number} height The new height
7688          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691          setHeight : function(height, animate){
7692             height = this.adjustHeight(height);
7693             if(!animate || !A){
7694                 this.dom.style.height = this.addUnits(height);
7695             }else{
7696                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7697             }
7698             return this;
7699         },
7700
7701         /**
7702          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7703          * @param {Number} width The new width
7704          * @param {Number} height The new height
7705          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7706          * @return {Roo.Element} this
7707          */
7708          setSize : function(width, height, animate){
7709             if(typeof width == "object"){ // in case of object from getSize()
7710                 height = width.height; width = width.width;
7711             }
7712             width = this.adjustWidth(width); height = this.adjustHeight(height);
7713             if(!animate || !A){
7714                 this.dom.style.width = this.addUnits(width);
7715                 this.dom.style.height = this.addUnits(height);
7716             }else{
7717                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7718             }
7719             return this;
7720         },
7721
7722         /**
7723          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7724          * @param {Number} x X value for new position (coordinates are page-based)
7725          * @param {Number} y Y value for new position (coordinates are page-based)
7726          * @param {Number} width The new width
7727          * @param {Number} height The new height
7728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7729          * @return {Roo.Element} this
7730          */
7731         setBounds : function(x, y, width, height, animate){
7732             if(!animate || !A){
7733                 this.setSize(width, height);
7734                 this.setLocation(x, y);
7735             }else{
7736                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7737                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7738                               this.preanim(arguments, 4), 'motion');
7739             }
7740             return this;
7741         },
7742
7743         /**
7744          * 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.
7745          * @param {Roo.lib.Region} region The region to fill
7746          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setRegion : function(region, animate){
7750             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7751             return this;
7752         },
7753
7754         /**
7755          * Appends an event handler
7756          *
7757          * @param {String}   eventName     The type of event to append
7758          * @param {Function} fn        The method the event invokes
7759          * @param {Object} scope       (optional) The scope (this object) of the fn
7760          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7761          */
7762         addListener : function(eventName, fn, scope, options){
7763             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7764         },
7765
7766         /**
7767          * Removes an event handler from this element
7768          * @param {String} eventName the type of event to remove
7769          * @param {Function} fn the method the event invokes
7770          * @return {Roo.Element} this
7771          */
7772         removeListener : function(eventName, fn){
7773             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7774             return this;
7775         },
7776
7777         /**
7778          * Removes all previous added listeners from this element
7779          * @return {Roo.Element} this
7780          */
7781         removeAllListeners : function(){
7782             E.purgeElement(this.dom);
7783             return this;
7784         },
7785
7786         relayEvent : function(eventName, observable){
7787             this.on(eventName, function(e){
7788                 observable.fireEvent(eventName, e);
7789             });
7790         },
7791
7792         /**
7793          * Set the opacity of the element
7794          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7796          * @return {Roo.Element} this
7797          */
7798          setOpacity : function(opacity, animate){
7799             if(!animate || !A){
7800                 var s = this.dom.style;
7801                 if(Roo.isIE){
7802                     s.zoom = 1;
7803                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7804                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7805                 }else{
7806                     s.opacity = opacity;
7807                 }
7808             }else{
7809                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7810             }
7811             return this;
7812         },
7813
7814         /**
7815          * Gets the left X coordinate
7816          * @param {Boolean} local True to get the local css position instead of page coordinate
7817          * @return {Number}
7818          */
7819         getLeft : function(local){
7820             if(!local){
7821                 return this.getX();
7822             }else{
7823                 return parseInt(this.getStyle("left"), 10) || 0;
7824             }
7825         },
7826
7827         /**
7828          * Gets the right X coordinate of the element (element X position + element width)
7829          * @param {Boolean} local True to get the local css position instead of page coordinate
7830          * @return {Number}
7831          */
7832         getRight : function(local){
7833             if(!local){
7834                 return this.getX() + this.getWidth();
7835             }else{
7836                 return (this.getLeft(true) + this.getWidth()) || 0;
7837             }
7838         },
7839
7840         /**
7841          * Gets the top Y coordinate
7842          * @param {Boolean} local True to get the local css position instead of page coordinate
7843          * @return {Number}
7844          */
7845         getTop : function(local) {
7846             if(!local){
7847                 return this.getY();
7848             }else{
7849                 return parseInt(this.getStyle("top"), 10) || 0;
7850             }
7851         },
7852
7853         /**
7854          * Gets the bottom Y coordinate of the element (element Y position + element height)
7855          * @param {Boolean} local True to get the local css position instead of page coordinate
7856          * @return {Number}
7857          */
7858         getBottom : function(local){
7859             if(!local){
7860                 return this.getY() + this.getHeight();
7861             }else{
7862                 return (this.getTop(true) + this.getHeight()) || 0;
7863             }
7864         },
7865
7866         /**
7867         * Initializes positioning on this element. If a desired position is not passed, it will make the
7868         * the element positioned relative IF it is not already positioned.
7869         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7870         * @param {Number} zIndex (optional) The zIndex to apply
7871         * @param {Number} x (optional) Set the page X position
7872         * @param {Number} y (optional) Set the page Y position
7873         */
7874         position : function(pos, zIndex, x, y){
7875             if(!pos){
7876                if(this.getStyle('position') == 'static'){
7877                    this.setStyle('position', 'relative');
7878                }
7879             }else{
7880                 this.setStyle("position", pos);
7881             }
7882             if(zIndex){
7883                 this.setStyle("z-index", zIndex);
7884             }
7885             if(x !== undefined && y !== undefined){
7886                 this.setXY([x, y]);
7887             }else if(x !== undefined){
7888                 this.setX(x);
7889             }else if(y !== undefined){
7890                 this.setY(y);
7891             }
7892         },
7893
7894         /**
7895         * Clear positioning back to the default when the document was loaded
7896         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7897         * @return {Roo.Element} this
7898          */
7899         clearPositioning : function(value){
7900             value = value ||'';
7901             this.setStyle({
7902                 "left": value,
7903                 "right": value,
7904                 "top": value,
7905                 "bottom": value,
7906                 "z-index": "",
7907                 "position" : "static"
7908             });
7909             return this;
7910         },
7911
7912         /**
7913         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7914         * snapshot before performing an update and then restoring the element.
7915         * @return {Object}
7916         */
7917         getPositioning : function(){
7918             var l = this.getStyle("left");
7919             var t = this.getStyle("top");
7920             return {
7921                 "position" : this.getStyle("position"),
7922                 "left" : l,
7923                 "right" : l ? "" : this.getStyle("right"),
7924                 "top" : t,
7925                 "bottom" : t ? "" : this.getStyle("bottom"),
7926                 "z-index" : this.getStyle("z-index")
7927             };
7928         },
7929
7930         /**
7931          * Gets the width of the border(s) for the specified side(s)
7932          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7933          * passing lr would get the border (l)eft width + the border (r)ight width.
7934          * @return {Number} The width of the sides passed added together
7935          */
7936         getBorderWidth : function(side){
7937             return this.addStyles(side, El.borders);
7938         },
7939
7940         /**
7941          * Gets the width of the padding(s) for the specified side(s)
7942          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7943          * passing lr would get the padding (l)eft + the padding (r)ight.
7944          * @return {Number} The padding of the sides passed added together
7945          */
7946         getPadding : function(side){
7947             return this.addStyles(side, El.paddings);
7948         },
7949
7950         /**
7951         * Set positioning with an object returned by getPositioning().
7952         * @param {Object} posCfg
7953         * @return {Roo.Element} this
7954          */
7955         setPositioning : function(pc){
7956             this.applyStyles(pc);
7957             if(pc.right == "auto"){
7958                 this.dom.style.right = "";
7959             }
7960             if(pc.bottom == "auto"){
7961                 this.dom.style.bottom = "";
7962             }
7963             return this;
7964         },
7965
7966         // private
7967         fixDisplay : function(){
7968             if(this.getStyle("display") == "none"){
7969                 this.setStyle("visibility", "hidden");
7970                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7971                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7972                     this.setStyle("display", "block");
7973                 }
7974             }
7975         },
7976
7977         /**
7978          * Quick set left and top adding default units
7979          * @param {String} left The left CSS property value
7980          * @param {String} top The top CSS property value
7981          * @return {Roo.Element} this
7982          */
7983          setLeftTop : function(left, top){
7984             this.dom.style.left = this.addUnits(left);
7985             this.dom.style.top = this.addUnits(top);
7986             return this;
7987         },
7988
7989         /**
7990          * Move this element relative to its current position.
7991          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7992          * @param {Number} distance How far to move the element in pixels
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          move : function(direction, distance, animate){
7997             var xy = this.getXY();
7998             direction = direction.toLowerCase();
7999             switch(direction){
8000                 case "l":
8001                 case "left":
8002                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8003                     break;
8004                case "r":
8005                case "right":
8006                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8007                     break;
8008                case "t":
8009                case "top":
8010                case "up":
8011                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8012                     break;
8013                case "b":
8014                case "bottom":
8015                case "down":
8016                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8017                     break;
8018             }
8019             return this;
8020         },
8021
8022         /**
8023          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8024          * @return {Roo.Element} this
8025          */
8026         clip : function(){
8027             if(!this.isClipped){
8028                this.isClipped = true;
8029                this.originalClip = {
8030                    "o": this.getStyle("overflow"),
8031                    "x": this.getStyle("overflow-x"),
8032                    "y": this.getStyle("overflow-y")
8033                };
8034                this.setStyle("overflow", "hidden");
8035                this.setStyle("overflow-x", "hidden");
8036                this.setStyle("overflow-y", "hidden");
8037             }
8038             return this;
8039         },
8040
8041         /**
8042          *  Return clipping (overflow) to original clipping before clip() was called
8043          * @return {Roo.Element} this
8044          */
8045         unclip : function(){
8046             if(this.isClipped){
8047                 this.isClipped = false;
8048                 var o = this.originalClip;
8049                 if(o.o){this.setStyle("overflow", o.o);}
8050                 if(o.x){this.setStyle("overflow-x", o.x);}
8051                 if(o.y){this.setStyle("overflow-y", o.y);}
8052             }
8053             return this;
8054         },
8055
8056
8057         /**
8058          * Gets the x,y coordinates specified by the anchor position on the element.
8059          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8060          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8061          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8062          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8063          * @return {Array} [x, y] An array containing the element's x and y coordinates
8064          */
8065         getAnchorXY : function(anchor, local, s){
8066             //Passing a different size is useful for pre-calculating anchors,
8067             //especially for anchored animations that change the el size.
8068
8069             var w, h, vp = false;
8070             if(!s){
8071                 var d = this.dom;
8072                 if(d == document.body || d == document){
8073                     vp = true;
8074                     w = D.getViewWidth(); h = D.getViewHeight();
8075                 }else{
8076                     w = this.getWidth(); h = this.getHeight();
8077                 }
8078             }else{
8079                 w = s.width;  h = s.height;
8080             }
8081             var x = 0, y = 0, r = Math.round;
8082             switch((anchor || "tl").toLowerCase()){
8083                 case "c":
8084                     x = r(w*.5);
8085                     y = r(h*.5);
8086                 break;
8087                 case "t":
8088                     x = r(w*.5);
8089                     y = 0;
8090                 break;
8091                 case "l":
8092                     x = 0;
8093                     y = r(h*.5);
8094                 break;
8095                 case "r":
8096                     x = w;
8097                     y = r(h*.5);
8098                 break;
8099                 case "b":
8100                     x = r(w*.5);
8101                     y = h;
8102                 break;
8103                 case "tl":
8104                     x = 0;
8105                     y = 0;
8106                 break;
8107                 case "bl":
8108                     x = 0;
8109                     y = h;
8110                 break;
8111                 case "br":
8112                     x = w;
8113                     y = h;
8114                 break;
8115                 case "tr":
8116                     x = w;
8117                     y = 0;
8118                 break;
8119             }
8120             if(local === true){
8121                 return [x, y];
8122             }
8123             if(vp){
8124                 var sc = this.getScroll();
8125                 return [x + sc.left, y + sc.top];
8126             }
8127             //Add the element's offset xy
8128             var o = this.getXY();
8129             return [x+o[0], y+o[1]];
8130         },
8131
8132         /**
8133          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8134          * supported position values.
8135          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8136          * @param {String} position The position to align to.
8137          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8138          * @return {Array} [x, y]
8139          */
8140         getAlignToXY : function(el, p, o){
8141             el = Roo.get(el);
8142             var d = this.dom;
8143             if(!el.dom){
8144                 throw "Element.alignTo with an element that doesn't exist";
8145             }
8146             var c = false; //constrain to viewport
8147             var p1 = "", p2 = "";
8148             o = o || [0,0];
8149
8150             if(!p){
8151                 p = "tl-bl";
8152             }else if(p == "?"){
8153                 p = "tl-bl?";
8154             }else if(p.indexOf("-") == -1){
8155                 p = "tl-" + p;
8156             }
8157             p = p.toLowerCase();
8158             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8159             if(!m){
8160                throw "Element.alignTo with an invalid alignment " + p;
8161             }
8162             p1 = m[1]; p2 = m[2]; c = !!m[3];
8163
8164             //Subtract the aligned el's internal xy from the target's offset xy
8165             //plus custom offset to get the aligned el's new offset xy
8166             var a1 = this.getAnchorXY(p1, true);
8167             var a2 = el.getAnchorXY(p2, false);
8168             var x = a2[0] - a1[0] + o[0];
8169             var y = a2[1] - a1[1] + o[1];
8170             if(c){
8171                 //constrain the aligned el to viewport if necessary
8172                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8173                 // 5px of margin for ie
8174                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8175
8176                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8177                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8178                 //otherwise swap the aligned el to the opposite border of the target.
8179                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8180                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8181                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8182                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8183
8184                var doc = document;
8185                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8186                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8187
8188                if((x+w) > dw + scrollX){
8189                     x = swapX ? r.left-w : dw+scrollX-w;
8190                 }
8191                if(x < scrollX){
8192                    x = swapX ? r.right : scrollX;
8193                }
8194                if((y+h) > dh + scrollY){
8195                     y = swapY ? r.top-h : dh+scrollY-h;
8196                 }
8197                if (y < scrollY){
8198                    y = swapY ? r.bottom : scrollY;
8199                }
8200             }
8201             return [x,y];
8202         },
8203
8204         // private
8205         getConstrainToXY : function(){
8206             var os = {top:0, left:0, bottom:0, right: 0};
8207
8208             return function(el, local, offsets, proposedXY){
8209                 el = Roo.get(el);
8210                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8211
8212                 var vw, vh, vx = 0, vy = 0;
8213                 if(el.dom == document.body || el.dom == document){
8214                     vw = Roo.lib.Dom.getViewWidth();
8215                     vh = Roo.lib.Dom.getViewHeight();
8216                 }else{
8217                     vw = el.dom.clientWidth;
8218                     vh = el.dom.clientHeight;
8219                     if(!local){
8220                         var vxy = el.getXY();
8221                         vx = vxy[0];
8222                         vy = vxy[1];
8223                     }
8224                 }
8225
8226                 var s = el.getScroll();
8227
8228                 vx += offsets.left + s.left;
8229                 vy += offsets.top + s.top;
8230
8231                 vw -= offsets.right;
8232                 vh -= offsets.bottom;
8233
8234                 var vr = vx+vw;
8235                 var vb = vy+vh;
8236
8237                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8238                 var x = xy[0], y = xy[1];
8239                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8240
8241                 // only move it if it needs it
8242                 var moved = false;
8243
8244                 // first validate right/bottom
8245                 if((x + w) > vr){
8246                     x = vr - w;
8247                     moved = true;
8248                 }
8249                 if((y + h) > vb){
8250                     y = vb - h;
8251                     moved = true;
8252                 }
8253                 // then make sure top/left isn't negative
8254                 if(x < vx){
8255                     x = vx;
8256                     moved = true;
8257                 }
8258                 if(y < vy){
8259                     y = vy;
8260                     moved = true;
8261                 }
8262                 return moved ? [x, y] : false;
8263             };
8264         }(),
8265
8266         // private
8267         adjustForConstraints : function(xy, parent, offsets){
8268             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8269         },
8270
8271         /**
8272          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8273          * document it aligns it to the viewport.
8274          * The position parameter is optional, and can be specified in any one of the following formats:
8275          * <ul>
8276          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8277          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8278          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8279          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8280          *   <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
8281          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8282          * </ul>
8283          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8284          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8285          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8286          * that specified in order to enforce the viewport constraints.
8287          * Following are all of the supported anchor positions:
8288     <pre>
8289     Value  Description
8290     -----  -----------------------------
8291     tl     The top left corner (default)
8292     t      The center of the top edge
8293     tr     The top right corner
8294     l      The center of the left edge
8295     c      In the center of the element
8296     r      The center of the right edge
8297     bl     The bottom left corner
8298     b      The center of the bottom edge
8299     br     The bottom right corner
8300     </pre>
8301     Example Usage:
8302     <pre><code>
8303     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8304     el.alignTo("other-el");
8305
8306     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8307     el.alignTo("other-el", "tr?");
8308
8309     // align the bottom right corner of el with the center left edge of other-el
8310     el.alignTo("other-el", "br-l?");
8311
8312     // align the center of el with the bottom left corner of other-el and
8313     // adjust the x position by -6 pixels (and the y position by 0)
8314     el.alignTo("other-el", "c-bl", [-6, 0]);
8315     </code></pre>
8316          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8317          * @param {String} position The position to align to.
8318          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8319          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8320          * @return {Roo.Element} this
8321          */
8322         alignTo : function(element, position, offsets, animate){
8323             var xy = this.getAlignToXY(element, position, offsets);
8324             this.setXY(xy, this.preanim(arguments, 3));
8325             return this;
8326         },
8327
8328         /**
8329          * Anchors an element to another element and realigns it when the window is resized.
8330          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8331          * @param {String} position The position to align to.
8332          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8333          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8334          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8335          * is a number, it is used as the buffer delay (defaults to 50ms).
8336          * @param {Function} callback The function to call after the animation finishes
8337          * @return {Roo.Element} this
8338          */
8339         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8340             var action = function(){
8341                 this.alignTo(el, alignment, offsets, animate);
8342                 Roo.callback(callback, this);
8343             };
8344             Roo.EventManager.onWindowResize(action, this);
8345             var tm = typeof monitorScroll;
8346             if(tm != 'undefined'){
8347                 Roo.EventManager.on(window, 'scroll', action, this,
8348                     {buffer: tm == 'number' ? monitorScroll : 50});
8349             }
8350             action.call(this); // align immediately
8351             return this;
8352         },
8353         /**
8354          * Clears any opacity settings from this element. Required in some cases for IE.
8355          * @return {Roo.Element} this
8356          */
8357         clearOpacity : function(){
8358             if (window.ActiveXObject) {
8359                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8360                     this.dom.style.filter = "";
8361                 }
8362             } else {
8363                 this.dom.style.opacity = "";
8364                 this.dom.style["-moz-opacity"] = "";
8365                 this.dom.style["-khtml-opacity"] = "";
8366             }
8367             return this;
8368         },
8369
8370         /**
8371          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8372          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8373          * @return {Roo.Element} this
8374          */
8375         hide : function(animate){
8376             this.setVisible(false, this.preanim(arguments, 0));
8377             return this;
8378         },
8379
8380         /**
8381         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8382         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8383          * @return {Roo.Element} this
8384          */
8385         show : function(animate){
8386             this.setVisible(true, this.preanim(arguments, 0));
8387             return this;
8388         },
8389
8390         /**
8391          * @private Test if size has a unit, otherwise appends the default
8392          */
8393         addUnits : function(size){
8394             return Roo.Element.addUnits(size, this.defaultUnit);
8395         },
8396
8397         /**
8398          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8399          * @return {Roo.Element} this
8400          */
8401         beginMeasure : function(){
8402             var el = this.dom;
8403             if(el.offsetWidth || el.offsetHeight){
8404                 return this; // offsets work already
8405             }
8406             var changed = [];
8407             var p = this.dom, b = document.body; // start with this element
8408             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8409                 var pe = Roo.get(p);
8410                 if(pe.getStyle('display') == 'none'){
8411                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8412                     p.style.visibility = "hidden";
8413                     p.style.display = "block";
8414                 }
8415                 p = p.parentNode;
8416             }
8417             this._measureChanged = changed;
8418             return this;
8419
8420         },
8421
8422         /**
8423          * Restores displays to before beginMeasure was called
8424          * @return {Roo.Element} this
8425          */
8426         endMeasure : function(){
8427             var changed = this._measureChanged;
8428             if(changed){
8429                 for(var i = 0, len = changed.length; i < len; i++) {
8430                     var r = changed[i];
8431                     r.el.style.visibility = r.visibility;
8432                     r.el.style.display = "none";
8433                 }
8434                 this._measureChanged = null;
8435             }
8436             return this;
8437         },
8438
8439         /**
8440         * Update the innerHTML of this element, optionally searching for and processing scripts
8441         * @param {String} html The new HTML
8442         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8443         * @param {Function} callback For async script loading you can be noticed when the update completes
8444         * @return {Roo.Element} this
8445          */
8446         update : function(html, loadScripts, callback){
8447             if(typeof html == "undefined"){
8448                 html = "";
8449             }
8450             if(loadScripts !== true){
8451                 this.dom.innerHTML = html;
8452                 if(typeof callback == "function"){
8453                     callback();
8454                 }
8455                 return this;
8456             }
8457             var id = Roo.id();
8458             var dom = this.dom;
8459
8460             html += '<span id="' + id + '"></span>';
8461
8462             E.onAvailable(id, function(){
8463                 var hd = document.getElementsByTagName("head")[0];
8464                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8465                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8466                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8467
8468                 var match;
8469                 while(match = re.exec(html)){
8470                     var attrs = match[1];
8471                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8472                     if(srcMatch && srcMatch[2]){
8473                        var s = document.createElement("script");
8474                        s.src = srcMatch[2];
8475                        var typeMatch = attrs.match(typeRe);
8476                        if(typeMatch && typeMatch[2]){
8477                            s.type = typeMatch[2];
8478                        }
8479                        hd.appendChild(s);
8480                     }else if(match[2] && match[2].length > 0){
8481                         if(window.execScript) {
8482                            window.execScript(match[2]);
8483                         } else {
8484                             /**
8485                              * eval:var:id
8486                              * eval:var:dom
8487                              * eval:var:html
8488                              * 
8489                              */
8490                            window.eval(match[2]);
8491                         }
8492                     }
8493                 }
8494                 var el = document.getElementById(id);
8495                 if(el){el.parentNode.removeChild(el);}
8496                 if(typeof callback == "function"){
8497                     callback();
8498                 }
8499             });
8500             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8501             return this;
8502         },
8503
8504         /**
8505          * Direct access to the UpdateManager update() method (takes the same parameters).
8506          * @param {String/Function} url The url for this request or a function to call to get the url
8507          * @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}
8508          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8509          * @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.
8510          * @return {Roo.Element} this
8511          */
8512         load : function(){
8513             var um = this.getUpdateManager();
8514             um.update.apply(um, arguments);
8515             return this;
8516         },
8517
8518         /**
8519         * Gets this element's UpdateManager
8520         * @return {Roo.UpdateManager} The UpdateManager
8521         */
8522         getUpdateManager : function(){
8523             if(!this.updateManager){
8524                 this.updateManager = new Roo.UpdateManager(this);
8525             }
8526             return this.updateManager;
8527         },
8528
8529         /**
8530          * Disables text selection for this element (normalized across browsers)
8531          * @return {Roo.Element} this
8532          */
8533         unselectable : function(){
8534             this.dom.unselectable = "on";
8535             this.swallowEvent("selectstart", true);
8536             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8537             this.addClass("x-unselectable");
8538             return this;
8539         },
8540
8541         /**
8542         * Calculates the x, y to center this element on the screen
8543         * @return {Array} The x, y values [x, y]
8544         */
8545         getCenterXY : function(){
8546             return this.getAlignToXY(document, 'c-c');
8547         },
8548
8549         /**
8550         * Centers the Element in either the viewport, or another Element.
8551         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8552         */
8553         center : function(centerIn){
8554             this.alignTo(centerIn || document, 'c-c');
8555             return this;
8556         },
8557
8558         /**
8559          * Tests various css rules/browsers to determine if this element uses a border box
8560          * @return {Boolean}
8561          */
8562         isBorderBox : function(){
8563             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8564         },
8565
8566         /**
8567          * Return a box {x, y, width, height} that can be used to set another elements
8568          * size/location to match this element.
8569          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8570          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8571          * @return {Object} box An object in the format {x, y, width, height}
8572          */
8573         getBox : function(contentBox, local){
8574             var xy;
8575             if(!local){
8576                 xy = this.getXY();
8577             }else{
8578                 var left = parseInt(this.getStyle("left"), 10) || 0;
8579                 var top = parseInt(this.getStyle("top"), 10) || 0;
8580                 xy = [left, top];
8581             }
8582             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8583             if(!contentBox){
8584                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8585             }else{
8586                 var l = this.getBorderWidth("l")+this.getPadding("l");
8587                 var r = this.getBorderWidth("r")+this.getPadding("r");
8588                 var t = this.getBorderWidth("t")+this.getPadding("t");
8589                 var b = this.getBorderWidth("b")+this.getPadding("b");
8590                 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)};
8591             }
8592             bx.right = bx.x + bx.width;
8593             bx.bottom = bx.y + bx.height;
8594             return bx;
8595         },
8596
8597         /**
8598          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8599          for more information about the sides.
8600          * @param {String} sides
8601          * @return {Number}
8602          */
8603         getFrameWidth : function(sides, onlyContentBox){
8604             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8605         },
8606
8607         /**
8608          * 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.
8609          * @param {Object} box The box to fill {x, y, width, height}
8610          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8612          * @return {Roo.Element} this
8613          */
8614         setBox : function(box, adjust, animate){
8615             var w = box.width, h = box.height;
8616             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8617                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8618                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8619             }
8620             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8621             return this;
8622         },
8623
8624         /**
8625          * Forces the browser to repaint this element
8626          * @return {Roo.Element} this
8627          */
8628          repaint : function(){
8629             var dom = this.dom;
8630             this.addClass("x-repaint");
8631             setTimeout(function(){
8632                 Roo.get(dom).removeClass("x-repaint");
8633             }, 1);
8634             return this;
8635         },
8636
8637         /**
8638          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8639          * then it returns the calculated width of the sides (see getPadding)
8640          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8641          * @return {Object/Number}
8642          */
8643         getMargins : function(side){
8644             if(!side){
8645                 return {
8646                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8647                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8648                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8649                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8650                 };
8651             }else{
8652                 return this.addStyles(side, El.margins);
8653              }
8654         },
8655
8656         // private
8657         addStyles : function(sides, styles){
8658             var val = 0, v, w;
8659             for(var i = 0, len = sides.length; i < len; i++){
8660                 v = this.getStyle(styles[sides.charAt(i)]);
8661                 if(v){
8662                      w = parseInt(v, 10);
8663                      if(w){ val += w; }
8664                 }
8665             }
8666             return val;
8667         },
8668
8669         /**
8670          * Creates a proxy element of this element
8671          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8672          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8673          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8674          * @return {Roo.Element} The new proxy element
8675          */
8676         createProxy : function(config, renderTo, matchBox){
8677             if(renderTo){
8678                 renderTo = Roo.getDom(renderTo);
8679             }else{
8680                 renderTo = document.body;
8681             }
8682             config = typeof config == "object" ?
8683                 config : {tag : "div", cls: config};
8684             var proxy = Roo.DomHelper.append(renderTo, config, true);
8685             if(matchBox){
8686                proxy.setBox(this.getBox());
8687             }
8688             return proxy;
8689         },
8690
8691         /**
8692          * Puts a mask over this element to disable user interaction. Requires core.css.
8693          * This method can only be applied to elements which accept child nodes.
8694          * @param {String} msg (optional) A message to display in the mask
8695          * @param {String} msgCls (optional) A css class to apply to the msg element
8696          * @return {Element} The mask  element
8697          */
8698         mask : function(msg, msgCls){
8699             if(this.getStyle("position") == "static"){
8700                 this.setStyle("position", "relative");
8701             }
8702             if(!this._mask){
8703                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8704             }
8705             this.addClass("x-masked");
8706             this._mask.setDisplayed(true);
8707             if(typeof msg == 'string'){
8708                 if(!this._maskMsg){
8709                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8710                 }
8711                 var mm = this._maskMsg;
8712                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8713                 mm.dom.firstChild.innerHTML = msg;
8714                 mm.setDisplayed(true);
8715                 mm.center(this);
8716             }
8717             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8718                 this._mask.setHeight(this.getHeight());
8719             }
8720             return this._mask;
8721         },
8722
8723         /**
8724          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8725          * it is cached for reuse.
8726          */
8727         unmask : function(removeEl){
8728             if(this._mask){
8729                 if(removeEl === true){
8730                     this._mask.remove();
8731                     delete this._mask;
8732                     if(this._maskMsg){
8733                         this._maskMsg.remove();
8734                         delete this._maskMsg;
8735                     }
8736                 }else{
8737                     this._mask.setDisplayed(false);
8738                     if(this._maskMsg){
8739                         this._maskMsg.setDisplayed(false);
8740                     }
8741                 }
8742             }
8743             this.removeClass("x-masked");
8744         },
8745
8746         /**
8747          * Returns true if this element is masked
8748          * @return {Boolean}
8749          */
8750         isMasked : function(){
8751             return this._mask && this._mask.isVisible();
8752         },
8753
8754         /**
8755          * Creates an iframe shim for this element to keep selects and other windowed objects from
8756          * showing through.
8757          * @return {Roo.Element} The new shim element
8758          */
8759         createShim : function(){
8760             var el = document.createElement('iframe');
8761             el.frameBorder = 'no';
8762             el.className = 'roo-shim';
8763             if(Roo.isIE && Roo.isSecure){
8764                 el.src = Roo.SSL_SECURE_URL;
8765             }
8766             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8767             shim.autoBoxAdjust = false;
8768             return shim;
8769         },
8770
8771         /**
8772          * Removes this element from the DOM and deletes it from the cache
8773          */
8774         remove : function(){
8775             if(this.dom.parentNode){
8776                 this.dom.parentNode.removeChild(this.dom);
8777             }
8778             delete El.cache[this.dom.id];
8779         },
8780
8781         /**
8782          * Sets up event handlers to add and remove a css class when the mouse is over this element
8783          * @param {String} className
8784          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8785          * mouseout events for children elements
8786          * @return {Roo.Element} this
8787          */
8788         addClassOnOver : function(className, preventFlicker){
8789             this.on("mouseover", function(){
8790                 Roo.fly(this, '_internal').addClass(className);
8791             }, this.dom);
8792             var removeFn = function(e){
8793                 if(preventFlicker !== true || !e.within(this, true)){
8794                     Roo.fly(this, '_internal').removeClass(className);
8795                 }
8796             };
8797             this.on("mouseout", removeFn, this.dom);
8798             return this;
8799         },
8800
8801         /**
8802          * Sets up event handlers to add and remove a css class when this element has the focus
8803          * @param {String} className
8804          * @return {Roo.Element} this
8805          */
8806         addClassOnFocus : function(className){
8807             this.on("focus", function(){
8808                 Roo.fly(this, '_internal').addClass(className);
8809             }, this.dom);
8810             this.on("blur", function(){
8811                 Roo.fly(this, '_internal').removeClass(className);
8812             }, this.dom);
8813             return this;
8814         },
8815         /**
8816          * 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)
8817          * @param {String} className
8818          * @return {Roo.Element} this
8819          */
8820         addClassOnClick : function(className){
8821             var dom = this.dom;
8822             this.on("mousedown", function(){
8823                 Roo.fly(dom, '_internal').addClass(className);
8824                 var d = Roo.get(document);
8825                 var fn = function(){
8826                     Roo.fly(dom, '_internal').removeClass(className);
8827                     d.removeListener("mouseup", fn);
8828                 };
8829                 d.on("mouseup", fn);
8830             });
8831             return this;
8832         },
8833
8834         /**
8835          * Stops the specified event from bubbling and optionally prevents the default action
8836          * @param {String} eventName
8837          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8838          * @return {Roo.Element} this
8839          */
8840         swallowEvent : function(eventName, preventDefault){
8841             var fn = function(e){
8842                 e.stopPropagation();
8843                 if(preventDefault){
8844                     e.preventDefault();
8845                 }
8846             };
8847             if(eventName instanceof Array){
8848                 for(var i = 0, len = eventName.length; i < len; i++){
8849                      this.on(eventName[i], fn);
8850                 }
8851                 return this;
8852             }
8853             this.on(eventName, fn);
8854             return this;
8855         },
8856
8857         /**
8858          * @private
8859          */
8860       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8861
8862         /**
8863          * Sizes this element to its parent element's dimensions performing
8864          * neccessary box adjustments.
8865          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8866          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8867          * @return {Roo.Element} this
8868          */
8869         fitToParent : function(monitorResize, targetParent) {
8870           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8871           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8872           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8873             return;
8874           }
8875           var p = Roo.get(targetParent || this.dom.parentNode);
8876           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8877           if (monitorResize === true) {
8878             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8879             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8880           }
8881           return this;
8882         },
8883
8884         /**
8885          * Gets the next sibling, skipping text nodes
8886          * @return {HTMLElement} The next sibling or null
8887          */
8888         getNextSibling : function(){
8889             var n = this.dom.nextSibling;
8890             while(n && n.nodeType != 1){
8891                 n = n.nextSibling;
8892             }
8893             return n;
8894         },
8895
8896         /**
8897          * Gets the previous sibling, skipping text nodes
8898          * @return {HTMLElement} The previous sibling or null
8899          */
8900         getPrevSibling : function(){
8901             var n = this.dom.previousSibling;
8902             while(n && n.nodeType != 1){
8903                 n = n.previousSibling;
8904             }
8905             return n;
8906         },
8907
8908
8909         /**
8910          * Appends the passed element(s) to this element
8911          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8912          * @return {Roo.Element} this
8913          */
8914         appendChild: function(el){
8915             el = Roo.get(el);
8916             el.appendTo(this);
8917             return this;
8918         },
8919
8920         /**
8921          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8922          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8923          * automatically generated with the specified attributes.
8924          * @param {HTMLElement} insertBefore (optional) a child element of this element
8925          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8926          * @return {Roo.Element} The new child element
8927          */
8928         createChild: function(config, insertBefore, returnDom){
8929             config = config || {tag:'div'};
8930             if(insertBefore){
8931                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8932             }
8933             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8934         },
8935
8936         /**
8937          * Appends this element to the passed element
8938          * @param {String/HTMLElement/Element} el The new parent element
8939          * @return {Roo.Element} this
8940          */
8941         appendTo: function(el){
8942             el = Roo.getDom(el);
8943             el.appendChild(this.dom);
8944             return this;
8945         },
8946
8947         /**
8948          * Inserts this element before the passed element in the DOM
8949          * @param {String/HTMLElement/Element} el The element to insert before
8950          * @return {Roo.Element} this
8951          */
8952         insertBefore: function(el){
8953             el = Roo.getDom(el);
8954             el.parentNode.insertBefore(this.dom, el);
8955             return this;
8956         },
8957
8958         /**
8959          * Inserts this element after the passed element in the DOM
8960          * @param {String/HTMLElement/Element} el The element to insert after
8961          * @return {Roo.Element} this
8962          */
8963         insertAfter: function(el){
8964             el = Roo.getDom(el);
8965             el.parentNode.insertBefore(this.dom, el.nextSibling);
8966             return this;
8967         },
8968
8969         /**
8970          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8971          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8972          * @return {Roo.Element} The new child
8973          */
8974         insertFirst: function(el, returnDom){
8975             el = el || {};
8976             if(typeof el == 'object' && !el.nodeType){ // dh config
8977                 return this.createChild(el, this.dom.firstChild, returnDom);
8978             }else{
8979                 el = Roo.getDom(el);
8980                 this.dom.insertBefore(el, this.dom.firstChild);
8981                 return !returnDom ? Roo.get(el) : el;
8982             }
8983         },
8984
8985         /**
8986          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8987          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8988          * @param {String} where (optional) 'before' or 'after' defaults to before
8989          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8990          * @return {Roo.Element} the inserted Element
8991          */
8992         insertSibling: function(el, where, returnDom){
8993             where = where ? where.toLowerCase() : 'before';
8994             el = el || {};
8995             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8996
8997             if(typeof el == 'object' && !el.nodeType){ // dh config
8998                 if(where == 'after' && !this.dom.nextSibling){
8999                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9000                 }else{
9001                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9002                 }
9003
9004             }else{
9005                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9006                             where == 'before' ? this.dom : this.dom.nextSibling);
9007                 if(!returnDom){
9008                     rt = Roo.get(rt);
9009                 }
9010             }
9011             return rt;
9012         },
9013
9014         /**
9015          * Creates and wraps this element with another element
9016          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9017          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9018          * @return {HTMLElement/Element} The newly created wrapper element
9019          */
9020         wrap: function(config, returnDom){
9021             if(!config){
9022                 config = {tag: "div"};
9023             }
9024             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9025             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9026             return newEl;
9027         },
9028
9029         /**
9030          * Replaces the passed element with this element
9031          * @param {String/HTMLElement/Element} el The element to replace
9032          * @return {Roo.Element} this
9033          */
9034         replace: function(el){
9035             el = Roo.get(el);
9036             this.insertBefore(el);
9037             el.remove();
9038             return this;
9039         },
9040
9041         /**
9042          * Inserts an html fragment into this element
9043          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9044          * @param {String} html The HTML fragment
9045          * @param {Boolean} returnEl True to return an Roo.Element
9046          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9047          */
9048         insertHtml : function(where, html, returnEl){
9049             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9050             return returnEl ? Roo.get(el) : el;
9051         },
9052
9053         /**
9054          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9055          * @param {Object} o The object with the attributes
9056          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9057          * @return {Roo.Element} this
9058          */
9059         set : function(o, useSet){
9060             var el = this.dom;
9061             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9062             for(var attr in o){
9063                 if(attr == "style" || typeof o[attr] == "function") continue;
9064                 if(attr=="cls"){
9065                     el.className = o["cls"];
9066                 }else{
9067                     if(useSet) el.setAttribute(attr, o[attr]);
9068                     else el[attr] = o[attr];
9069                 }
9070             }
9071             if(o.style){
9072                 Roo.DomHelper.applyStyles(el, o.style);
9073             }
9074             return this;
9075         },
9076
9077         /**
9078          * Convenience method for constructing a KeyMap
9079          * @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:
9080          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9081          * @param {Function} fn The function to call
9082          * @param {Object} scope (optional) The scope of the function
9083          * @return {Roo.KeyMap} The KeyMap created
9084          */
9085         addKeyListener : function(key, fn, scope){
9086             var config;
9087             if(typeof key != "object" || key instanceof Array){
9088                 config = {
9089                     key: key,
9090                     fn: fn,
9091                     scope: scope
9092                 };
9093             }else{
9094                 config = {
9095                     key : key.key,
9096                     shift : key.shift,
9097                     ctrl : key.ctrl,
9098                     alt : key.alt,
9099                     fn: fn,
9100                     scope: scope
9101                 };
9102             }
9103             return new Roo.KeyMap(this, config);
9104         },
9105
9106         /**
9107          * Creates a KeyMap for this element
9108          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9109          * @return {Roo.KeyMap} The KeyMap created
9110          */
9111         addKeyMap : function(config){
9112             return new Roo.KeyMap(this, config);
9113         },
9114
9115         /**
9116          * Returns true if this element is scrollable.
9117          * @return {Boolean}
9118          */
9119          isScrollable : function(){
9120             var dom = this.dom;
9121             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9122         },
9123
9124         /**
9125          * 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().
9126          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9127          * @param {Number} value The new scroll value
9128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9129          * @return {Element} this
9130          */
9131
9132         scrollTo : function(side, value, animate){
9133             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9134             if(!animate || !A){
9135                 this.dom[prop] = value;
9136             }else{
9137                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9138                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9139             }
9140             return this;
9141         },
9142
9143         /**
9144          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9145          * within this element's scrollable range.
9146          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9147          * @param {Number} distance How far to scroll the element in pixels
9148          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9149          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9150          * was scrolled as far as it could go.
9151          */
9152          scroll : function(direction, distance, animate){
9153              if(!this.isScrollable()){
9154                  return;
9155              }
9156              var el = this.dom;
9157              var l = el.scrollLeft, t = el.scrollTop;
9158              var w = el.scrollWidth, h = el.scrollHeight;
9159              var cw = el.clientWidth, ch = el.clientHeight;
9160              direction = direction.toLowerCase();
9161              var scrolled = false;
9162              var a = this.preanim(arguments, 2);
9163              switch(direction){
9164                  case "l":
9165                  case "left":
9166                      if(w - l > cw){
9167                          var v = Math.min(l + distance, w-cw);
9168                          this.scrollTo("left", v, a);
9169                          scrolled = true;
9170                      }
9171                      break;
9172                 case "r":
9173                 case "right":
9174                      if(l > 0){
9175                          var v = Math.max(l - distance, 0);
9176                          this.scrollTo("left", v, a);
9177                          scrolled = true;
9178                      }
9179                      break;
9180                 case "t":
9181                 case "top":
9182                 case "up":
9183                      if(t > 0){
9184                          var v = Math.max(t - distance, 0);
9185                          this.scrollTo("top", v, a);
9186                          scrolled = true;
9187                      }
9188                      break;
9189                 case "b":
9190                 case "bottom":
9191                 case "down":
9192                      if(h - t > ch){
9193                          var v = Math.min(t + distance, h-ch);
9194                          this.scrollTo("top", v, a);
9195                          scrolled = true;
9196                      }
9197                      break;
9198              }
9199              return scrolled;
9200         },
9201
9202         /**
9203          * Translates the passed page coordinates into left/top css values for this element
9204          * @param {Number/Array} x The page x or an array containing [x, y]
9205          * @param {Number} y The page y
9206          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9207          */
9208         translatePoints : function(x, y){
9209             if(typeof x == 'object' || x instanceof Array){
9210                 y = x[1]; x = x[0];
9211             }
9212             var p = this.getStyle('position');
9213             var o = this.getXY();
9214
9215             var l = parseInt(this.getStyle('left'), 10);
9216             var t = parseInt(this.getStyle('top'), 10);
9217
9218             if(isNaN(l)){
9219                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9220             }
9221             if(isNaN(t)){
9222                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9223             }
9224
9225             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9226         },
9227
9228         /**
9229          * Returns the current scroll position of the element.
9230          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9231          */
9232         getScroll : function(){
9233             var d = this.dom, doc = document;
9234             if(d == doc || d == doc.body){
9235                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9236                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9237                 return {left: l, top: t};
9238             }else{
9239                 return {left: d.scrollLeft, top: d.scrollTop};
9240             }
9241         },
9242
9243         /**
9244          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9245          * are convert to standard 6 digit hex color.
9246          * @param {String} attr The css attribute
9247          * @param {String} defaultValue The default value to use when a valid color isn't found
9248          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9249          * YUI color anims.
9250          */
9251         getColor : function(attr, defaultValue, prefix){
9252             var v = this.getStyle(attr);
9253             if(!v || v == "transparent" || v == "inherit") {
9254                 return defaultValue;
9255             }
9256             var color = typeof prefix == "undefined" ? "#" : prefix;
9257             if(v.substr(0, 4) == "rgb("){
9258                 var rvs = v.slice(4, v.length -1).split(",");
9259                 for(var i = 0; i < 3; i++){
9260                     var h = parseInt(rvs[i]).toString(16);
9261                     if(h < 16){
9262                         h = "0" + h;
9263                     }
9264                     color += h;
9265                 }
9266             } else {
9267                 if(v.substr(0, 1) == "#"){
9268                     if(v.length == 4) {
9269                         for(var i = 1; i < 4; i++){
9270                             var c = v.charAt(i);
9271                             color +=  c + c;
9272                         }
9273                     }else if(v.length == 7){
9274                         color += v.substr(1);
9275                     }
9276                 }
9277             }
9278             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9279         },
9280
9281         /**
9282          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9283          * gradient background, rounded corners and a 4-way shadow.
9284          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9285          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9286          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9287          * @return {Roo.Element} this
9288          */
9289         boxWrap : function(cls){
9290             cls = cls || 'x-box';
9291             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9292             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9293             return el;
9294         },
9295
9296         /**
9297          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9298          * @param {String} namespace The namespace in which to look for the attribute
9299          * @param {String} name The attribute name
9300          * @return {String} The attribute value
9301          */
9302         getAttributeNS : Roo.isIE ? function(ns, name){
9303             var d = this.dom;
9304             var type = typeof d[ns+":"+name];
9305             if(type != 'undefined' && type != 'unknown'){
9306                 return d[ns+":"+name];
9307             }
9308             return d[name];
9309         } : function(ns, name){
9310             var d = this.dom;
9311             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9312         }
9313     };
9314
9315     var ep = El.prototype;
9316
9317     /**
9318      * Appends an event handler (Shorthand for addListener)
9319      * @param {String}   eventName     The type of event to append
9320      * @param {Function} fn        The method the event invokes
9321      * @param {Object} scope       (optional) The scope (this object) of the fn
9322      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9323      * @method
9324      */
9325     ep.on = ep.addListener;
9326         // backwards compat
9327     ep.mon = ep.addListener;
9328
9329     /**
9330      * Removes an event handler from this element (shorthand for removeListener)
9331      * @param {String} eventName the type of event to remove
9332      * @param {Function} fn the method the event invokes
9333      * @return {Roo.Element} this
9334      * @method
9335      */
9336     ep.un = ep.removeListener;
9337
9338     /**
9339      * true to automatically adjust width and height settings for box-model issues (default to true)
9340      */
9341     ep.autoBoxAdjust = true;
9342
9343     // private
9344     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9345
9346     // private
9347     El.addUnits = function(v, defaultUnit){
9348         if(v === "" || v == "auto"){
9349             return v;
9350         }
9351         if(v === undefined){
9352             return '';
9353         }
9354         if(typeof v == "number" || !El.unitPattern.test(v)){
9355             return v + (defaultUnit || 'px');
9356         }
9357         return v;
9358     };
9359
9360     // special markup used throughout Roo when box wrapping elements
9361     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>';
9362     /**
9363      * Visibility mode constant - Use visibility to hide element
9364      * @static
9365      * @type Number
9366      */
9367     El.VISIBILITY = 1;
9368     /**
9369      * Visibility mode constant - Use display to hide element
9370      * @static
9371      * @type Number
9372      */
9373     El.DISPLAY = 2;
9374
9375     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9376     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9377     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9378
9379
9380
9381     /**
9382      * @private
9383      */
9384     El.cache = {};
9385
9386     var docEl;
9387
9388     /**
9389      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9390      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9391      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9392      * @return {Element} The Element object
9393      * @static
9394      */
9395     El.get = function(el){
9396         var ex, elm, id;
9397         if(!el){ return null; }
9398         if(typeof el == "string"){ // element id
9399             if(!(elm = document.getElementById(el))){
9400                 return null;
9401             }
9402             if(ex = El.cache[el]){
9403                 ex.dom = elm;
9404             }else{
9405                 ex = El.cache[el] = new El(elm);
9406             }
9407             return ex;
9408         }else if(el.tagName){ // dom element
9409             if(!(id = el.id)){
9410                 id = Roo.id(el);
9411             }
9412             if(ex = El.cache[id]){
9413                 ex.dom = el;
9414             }else{
9415                 ex = El.cache[id] = new El(el);
9416             }
9417             return ex;
9418         }else if(el instanceof El){
9419             if(el != docEl){
9420                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9421                                                               // catch case where it hasn't been appended
9422                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9423             }
9424             return el;
9425         }else if(el.isComposite){
9426             return el;
9427         }else if(el instanceof Array){
9428             return El.select(el);
9429         }else if(el == document){
9430             // create a bogus element object representing the document object
9431             if(!docEl){
9432                 var f = function(){};
9433                 f.prototype = El.prototype;
9434                 docEl = new f();
9435                 docEl.dom = document;
9436             }
9437             return docEl;
9438         }
9439         return null;
9440     };
9441
9442     // private
9443     El.uncache = function(el){
9444         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9445             if(a[i]){
9446                 delete El.cache[a[i].id || a[i]];
9447             }
9448         }
9449     };
9450
9451     // private
9452     // Garbage collection - uncache elements/purge listeners on orphaned elements
9453     // so we don't hold a reference and cause the browser to retain them
9454     El.garbageCollect = function(){
9455         if(!Roo.enableGarbageCollector){
9456             clearInterval(El.collectorThread);
9457             return;
9458         }
9459         for(var eid in El.cache){
9460             var el = El.cache[eid], d = el.dom;
9461             // -------------------------------------------------------
9462             // Determining what is garbage:
9463             // -------------------------------------------------------
9464             // !d
9465             // dom node is null, definitely garbage
9466             // -------------------------------------------------------
9467             // !d.parentNode
9468             // no parentNode == direct orphan, definitely garbage
9469             // -------------------------------------------------------
9470             // !d.offsetParent && !document.getElementById(eid)
9471             // display none elements have no offsetParent so we will
9472             // also try to look it up by it's id. However, check
9473             // offsetParent first so we don't do unneeded lookups.
9474             // This enables collection of elements that are not orphans
9475             // directly, but somewhere up the line they have an orphan
9476             // parent.
9477             // -------------------------------------------------------
9478             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9479                 delete El.cache[eid];
9480                 if(d && Roo.enableListenerCollection){
9481                     E.purgeElement(d);
9482                 }
9483             }
9484         }
9485     }
9486     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9487
9488
9489     // dom is optional
9490     El.Flyweight = function(dom){
9491         this.dom = dom;
9492     };
9493     El.Flyweight.prototype = El.prototype;
9494
9495     El._flyweights = {};
9496     /**
9497      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9498      * the dom node can be overwritten by other code.
9499      * @param {String/HTMLElement} el The dom node or id
9500      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9501      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9502      * @static
9503      * @return {Element} The shared Element object
9504      */
9505     El.fly = function(el, named){
9506         named = named || '_global';
9507         el = Roo.getDom(el);
9508         if(!el){
9509             return null;
9510         }
9511         if(!El._flyweights[named]){
9512             El._flyweights[named] = new El.Flyweight();
9513         }
9514         El._flyweights[named].dom = el;
9515         return El._flyweights[named];
9516     };
9517
9518     /**
9519      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9520      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9521      * Shorthand of {@link Roo.Element#get}
9522      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9523      * @return {Element} The Element object
9524      * @member Roo
9525      * @method get
9526      */
9527     Roo.get = El.get;
9528     /**
9529      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9530      * the dom node can be overwritten by other code.
9531      * Shorthand of {@link Roo.Element#fly}
9532      * @param {String/HTMLElement} el The dom node or id
9533      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9534      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9535      * @static
9536      * @return {Element} The shared Element object
9537      * @member Roo
9538      * @method fly
9539      */
9540     Roo.fly = El.fly;
9541
9542     // speedy lookup for elements never to box adjust
9543     var noBoxAdjust = Roo.isStrict ? {
9544         select:1
9545     } : {
9546         input:1, select:1, textarea:1
9547     };
9548     if(Roo.isIE || Roo.isGecko){
9549         noBoxAdjust['button'] = 1;
9550     }
9551
9552
9553     Roo.EventManager.on(window, 'unload', function(){
9554         delete El.cache;
9555         delete El._flyweights;
9556     });
9557 })();
9558
9559
9560
9561
9562 if(Roo.DomQuery){
9563     Roo.Element.selectorFunction = Roo.DomQuery.select;
9564 }
9565
9566 Roo.Element.select = function(selector, unique, root){
9567     var els;
9568     if(typeof selector == "string"){
9569         els = Roo.Element.selectorFunction(selector, root);
9570     }else if(selector.length !== undefined){
9571         els = selector;
9572     }else{
9573         throw "Invalid selector";
9574     }
9575     if(unique === true){
9576         return new Roo.CompositeElement(els);
9577     }else{
9578         return new Roo.CompositeElementLite(els);
9579     }
9580 };
9581 /**
9582  * Selects elements based on the passed CSS selector to enable working on them as 1.
9583  * @param {String/Array} selector The CSS selector or an array of elements
9584  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9585  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9586  * @return {CompositeElementLite/CompositeElement}
9587  * @member Roo
9588  * @method select
9589  */
9590 Roo.select = Roo.Element.select;
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605 /*
9606  * Based on:
9607  * Ext JS Library 1.1.1
9608  * Copyright(c) 2006-2007, Ext JS, LLC.
9609  *
9610  * Originally Released Under LGPL - original licence link has changed is not relivant.
9611  *
9612  * Fork - LGPL
9613  * <script type="text/javascript">
9614  */
9615
9616
9617
9618 //Notifies Element that fx methods are available
9619 Roo.enableFx = true;
9620
9621 /**
9622  * @class Roo.Fx
9623  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9624  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9625  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9626  * Element effects to work.</p><br/>
9627  *
9628  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9629  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9630  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9631  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9632  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9633  * expected results and should be done with care.</p><br/>
9634  *
9635  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9636  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9637 <pre>
9638 Value  Description
9639 -----  -----------------------------
9640 tl     The top left corner
9641 t      The center of the top edge
9642 tr     The top right corner
9643 l      The center of the left edge
9644 r      The center of the right edge
9645 bl     The bottom left corner
9646 b      The center of the bottom edge
9647 br     The bottom right corner
9648 </pre>
9649  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9650  * below are common options that can be passed to any Fx method.</b>
9651  * @cfg {Function} callback A function called when the effect is finished
9652  * @cfg {Object} scope The scope of the effect function
9653  * @cfg {String} easing A valid Easing value for the effect
9654  * @cfg {String} afterCls A css class to apply after the effect
9655  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9656  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9657  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9658  * effects that end with the element being visually hidden, ignored otherwise)
9659  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9660  * a function which returns such a specification that will be applied to the Element after the effect finishes
9661  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9662  * @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
9663  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9664  */
9665 Roo.Fx = {
9666         /**
9667          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9668          * origin for the slide effect.  This function automatically handles wrapping the element with
9669          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9670          * Usage:
9671          *<pre><code>
9672 // default: slide the element in from the top
9673 el.slideIn();
9674
9675 // custom: slide the element in from the right with a 2-second duration
9676 el.slideIn('r', { duration: 2 });
9677
9678 // common config options shown with default values
9679 el.slideIn('t', {
9680     easing: 'easeOut',
9681     duration: .5
9682 });
9683 </code></pre>
9684          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9685          * @param {Object} options (optional) Object literal with any of the Fx config options
9686          * @return {Roo.Element} The Element
9687          */
9688     slideIn : function(anchor, o){
9689         var el = this.getFxEl();
9690         o = o || {};
9691
9692         el.queueFx(o, function(){
9693
9694             anchor = anchor || "t";
9695
9696             // fix display to visibility
9697             this.fixDisplay();
9698
9699             // restore values after effect
9700             var r = this.getFxRestore();
9701             var b = this.getBox();
9702             // fixed size for slide
9703             this.setSize(b);
9704
9705             // wrap if needed
9706             var wrap = this.fxWrap(r.pos, o, "hidden");
9707
9708             var st = this.dom.style;
9709             st.visibility = "visible";
9710             st.position = "absolute";
9711
9712             // clear out temp styles after slide and unwrap
9713             var after = function(){
9714                 el.fxUnwrap(wrap, r.pos, o);
9715                 st.width = r.width;
9716                 st.height = r.height;
9717                 el.afterFx(o);
9718             };
9719             // time to calc the positions
9720             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9721
9722             switch(anchor.toLowerCase()){
9723                 case "t":
9724                     wrap.setSize(b.width, 0);
9725                     st.left = st.bottom = "0";
9726                     a = {height: bh};
9727                 break;
9728                 case "l":
9729                     wrap.setSize(0, b.height);
9730                     st.right = st.top = "0";
9731                     a = {width: bw};
9732                 break;
9733                 case "r":
9734                     wrap.setSize(0, b.height);
9735                     wrap.setX(b.right);
9736                     st.left = st.top = "0";
9737                     a = {width: bw, points: pt};
9738                 break;
9739                 case "b":
9740                     wrap.setSize(b.width, 0);
9741                     wrap.setY(b.bottom);
9742                     st.left = st.top = "0";
9743                     a = {height: bh, points: pt};
9744                 break;
9745                 case "tl":
9746                     wrap.setSize(0, 0);
9747                     st.right = st.bottom = "0";
9748                     a = {width: bw, height: bh};
9749                 break;
9750                 case "bl":
9751                     wrap.setSize(0, 0);
9752                     wrap.setY(b.y+b.height);
9753                     st.right = st.top = "0";
9754                     a = {width: bw, height: bh, points: pt};
9755                 break;
9756                 case "br":
9757                     wrap.setSize(0, 0);
9758                     wrap.setXY([b.right, b.bottom]);
9759                     st.left = st.top = "0";
9760                     a = {width: bw, height: bh, points: pt};
9761                 break;
9762                 case "tr":
9763                     wrap.setSize(0, 0);
9764                     wrap.setX(b.x+b.width);
9765                     st.left = st.bottom = "0";
9766                     a = {width: bw, height: bh, points: pt};
9767                 break;
9768             }
9769             this.dom.style.visibility = "visible";
9770             wrap.show();
9771
9772             arguments.callee.anim = wrap.fxanim(a,
9773                 o,
9774                 'motion',
9775                 .5,
9776                 'easeOut', after);
9777         });
9778         return this;
9779     },
9780     
9781         /**
9782          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9783          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9784          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9785          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9786          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9787          * Usage:
9788          *<pre><code>
9789 // default: slide the element out to the top
9790 el.slideOut();
9791
9792 // custom: slide the element out to the right with a 2-second duration
9793 el.slideOut('r', { duration: 2 });
9794
9795 // common config options shown with default values
9796 el.slideOut('t', {
9797     easing: 'easeOut',
9798     duration: .5,
9799     remove: false,
9800     useDisplay: false
9801 });
9802 </code></pre>
9803          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9804          * @param {Object} options (optional) Object literal with any of the Fx config options
9805          * @return {Roo.Element} The Element
9806          */
9807     slideOut : function(anchor, o){
9808         var el = this.getFxEl();
9809         o = o || {};
9810
9811         el.queueFx(o, function(){
9812
9813             anchor = anchor || "t";
9814
9815             // restore values after effect
9816             var r = this.getFxRestore();
9817             
9818             var b = this.getBox();
9819             // fixed size for slide
9820             this.setSize(b);
9821
9822             // wrap if needed
9823             var wrap = this.fxWrap(r.pos, o, "visible");
9824
9825             var st = this.dom.style;
9826             st.visibility = "visible";
9827             st.position = "absolute";
9828
9829             wrap.setSize(b);
9830
9831             var after = function(){
9832                 if(o.useDisplay){
9833                     el.setDisplayed(false);
9834                 }else{
9835                     el.hide();
9836                 }
9837
9838                 el.fxUnwrap(wrap, r.pos, o);
9839
9840                 st.width = r.width;
9841                 st.height = r.height;
9842
9843                 el.afterFx(o);
9844             };
9845
9846             var a, zero = {to: 0};
9847             switch(anchor.toLowerCase()){
9848                 case "t":
9849                     st.left = st.bottom = "0";
9850                     a = {height: zero};
9851                 break;
9852                 case "l":
9853                     st.right = st.top = "0";
9854                     a = {width: zero};
9855                 break;
9856                 case "r":
9857                     st.left = st.top = "0";
9858                     a = {width: zero, points: {to:[b.right, b.y]}};
9859                 break;
9860                 case "b":
9861                     st.left = st.top = "0";
9862                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9863                 break;
9864                 case "tl":
9865                     st.right = st.bottom = "0";
9866                     a = {width: zero, height: zero};
9867                 break;
9868                 case "bl":
9869                     st.right = st.top = "0";
9870                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9871                 break;
9872                 case "br":
9873                     st.left = st.top = "0";
9874                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9875                 break;
9876                 case "tr":
9877                     st.left = st.bottom = "0";
9878                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9879                 break;
9880             }
9881
9882             arguments.callee.anim = wrap.fxanim(a,
9883                 o,
9884                 'motion',
9885                 .5,
9886                 "easeOut", after);
9887         });
9888         return this;
9889     },
9890
9891         /**
9892          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9893          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9894          * The element must be removed from the DOM using the 'remove' config option if desired.
9895          * Usage:
9896          *<pre><code>
9897 // default
9898 el.puff();
9899
9900 // common config options shown with default values
9901 el.puff({
9902     easing: 'easeOut',
9903     duration: .5,
9904     remove: false,
9905     useDisplay: false
9906 });
9907 </code></pre>
9908          * @param {Object} options (optional) Object literal with any of the Fx config options
9909          * @return {Roo.Element} The Element
9910          */
9911     puff : function(o){
9912         var el = this.getFxEl();
9913         o = o || {};
9914
9915         el.queueFx(o, function(){
9916             this.clearOpacity();
9917             this.show();
9918
9919             // restore values after effect
9920             var r = this.getFxRestore();
9921             var st = this.dom.style;
9922
9923             var after = function(){
9924                 if(o.useDisplay){
9925                     el.setDisplayed(false);
9926                 }else{
9927                     el.hide();
9928                 }
9929
9930                 el.clearOpacity();
9931
9932                 el.setPositioning(r.pos);
9933                 st.width = r.width;
9934                 st.height = r.height;
9935                 st.fontSize = '';
9936                 el.afterFx(o);
9937             };
9938
9939             var width = this.getWidth();
9940             var height = this.getHeight();
9941
9942             arguments.callee.anim = this.fxanim({
9943                     width : {to: this.adjustWidth(width * 2)},
9944                     height : {to: this.adjustHeight(height * 2)},
9945                     points : {by: [-(width * .5), -(height * .5)]},
9946                     opacity : {to: 0},
9947                     fontSize: {to:200, unit: "%"}
9948                 },
9949                 o,
9950                 'motion',
9951                 .5,
9952                 "easeOut", after);
9953         });
9954         return this;
9955     },
9956
9957         /**
9958          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9959          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9960          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9961          * Usage:
9962          *<pre><code>
9963 // default
9964 el.switchOff();
9965
9966 // all config options shown with default values
9967 el.switchOff({
9968     easing: 'easeIn',
9969     duration: .3,
9970     remove: false,
9971     useDisplay: false
9972 });
9973 </code></pre>
9974          * @param {Object} options (optional) Object literal with any of the Fx config options
9975          * @return {Roo.Element} The Element
9976          */
9977     switchOff : function(o){
9978         var el = this.getFxEl();
9979         o = o || {};
9980
9981         el.queueFx(o, function(){
9982             this.clearOpacity();
9983             this.clip();
9984
9985             // restore values after effect
9986             var r = this.getFxRestore();
9987             var st = this.dom.style;
9988
9989             var after = function(){
9990                 if(o.useDisplay){
9991                     el.setDisplayed(false);
9992                 }else{
9993                     el.hide();
9994                 }
9995
9996                 el.clearOpacity();
9997                 el.setPositioning(r.pos);
9998                 st.width = r.width;
9999                 st.height = r.height;
10000
10001                 el.afterFx(o);
10002             };
10003
10004             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10005                 this.clearOpacity();
10006                 (function(){
10007                     this.fxanim({
10008                         height:{to:1},
10009                         points:{by:[0, this.getHeight() * .5]}
10010                     }, o, 'motion', 0.3, 'easeIn', after);
10011                 }).defer(100, this);
10012             });
10013         });
10014         return this;
10015     },
10016
10017     /**
10018      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10019      * changed using the "attr" config option) and then fading back to the original color. If no original
10020      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10021      * Usage:
10022 <pre><code>
10023 // default: highlight background to yellow
10024 el.highlight();
10025
10026 // custom: highlight foreground text to blue for 2 seconds
10027 el.highlight("0000ff", { attr: 'color', duration: 2 });
10028
10029 // common config options shown with default values
10030 el.highlight("ffff9c", {
10031     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10032     endColor: (current color) or "ffffff",
10033     easing: 'easeIn',
10034     duration: 1
10035 });
10036 </code></pre>
10037      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10038      * @param {Object} options (optional) Object literal with any of the Fx config options
10039      * @return {Roo.Element} The Element
10040      */ 
10041     highlight : function(color, o){
10042         var el = this.getFxEl();
10043         o = o || {};
10044
10045         el.queueFx(o, function(){
10046             color = color || "ffff9c";
10047             attr = o.attr || "backgroundColor";
10048
10049             this.clearOpacity();
10050             this.show();
10051
10052             var origColor = this.getColor(attr);
10053             var restoreColor = this.dom.style[attr];
10054             endColor = (o.endColor || origColor) || "ffffff";
10055
10056             var after = function(){
10057                 el.dom.style[attr] = restoreColor;
10058                 el.afterFx(o);
10059             };
10060
10061             var a = {};
10062             a[attr] = {from: color, to: endColor};
10063             arguments.callee.anim = this.fxanim(a,
10064                 o,
10065                 'color',
10066                 1,
10067                 'easeIn', after);
10068         });
10069         return this;
10070     },
10071
10072    /**
10073     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10074     * Usage:
10075 <pre><code>
10076 // default: a single light blue ripple
10077 el.frame();
10078
10079 // custom: 3 red ripples lasting 3 seconds total
10080 el.frame("ff0000", 3, { duration: 3 });
10081
10082 // common config options shown with default values
10083 el.frame("C3DAF9", 1, {
10084     duration: 1 //duration of entire animation (not each individual ripple)
10085     // Note: Easing is not configurable and will be ignored if included
10086 });
10087 </code></pre>
10088     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10089     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10090     * @param {Object} options (optional) Object literal with any of the Fx config options
10091     * @return {Roo.Element} The Element
10092     */
10093     frame : function(color, count, o){
10094         var el = this.getFxEl();
10095         o = o || {};
10096
10097         el.queueFx(o, function(){
10098             color = color || "#C3DAF9";
10099             if(color.length == 6){
10100                 color = "#" + color;
10101             }
10102             count = count || 1;
10103             duration = o.duration || 1;
10104             this.show();
10105
10106             var b = this.getBox();
10107             var animFn = function(){
10108                 var proxy = this.createProxy({
10109
10110                      style:{
10111                         visbility:"hidden",
10112                         position:"absolute",
10113                         "z-index":"35000", // yee haw
10114                         border:"0px solid " + color
10115                      }
10116                   });
10117                 var scale = Roo.isBorderBox ? 2 : 1;
10118                 proxy.animate({
10119                     top:{from:b.y, to:b.y - 20},
10120                     left:{from:b.x, to:b.x - 20},
10121                     borderWidth:{from:0, to:10},
10122                     opacity:{from:1, to:0},
10123                     height:{from:b.height, to:(b.height + (20*scale))},
10124                     width:{from:b.width, to:(b.width + (20*scale))}
10125                 }, duration, function(){
10126                     proxy.remove();
10127                 });
10128                 if(--count > 0){
10129                      animFn.defer((duration/2)*1000, this);
10130                 }else{
10131                     el.afterFx(o);
10132                 }
10133             };
10134             animFn.call(this);
10135         });
10136         return this;
10137     },
10138
10139    /**
10140     * Creates a pause before any subsequent queued effects begin.  If there are
10141     * no effects queued after the pause it will have no effect.
10142     * Usage:
10143 <pre><code>
10144 el.pause(1);
10145 </code></pre>
10146     * @param {Number} seconds The length of time to pause (in seconds)
10147     * @return {Roo.Element} The Element
10148     */
10149     pause : function(seconds){
10150         var el = this.getFxEl();
10151         var o = {};
10152
10153         el.queueFx(o, function(){
10154             setTimeout(function(){
10155                 el.afterFx(o);
10156             }, seconds * 1000);
10157         });
10158         return this;
10159     },
10160
10161    /**
10162     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10163     * using the "endOpacity" config option.
10164     * Usage:
10165 <pre><code>
10166 // default: fade in from opacity 0 to 100%
10167 el.fadeIn();
10168
10169 // custom: fade in from opacity 0 to 75% over 2 seconds
10170 el.fadeIn({ endOpacity: .75, duration: 2});
10171
10172 // common config options shown with default values
10173 el.fadeIn({
10174     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10175     easing: 'easeOut',
10176     duration: .5
10177 });
10178 </code></pre>
10179     * @param {Object} options (optional) Object literal with any of the Fx config options
10180     * @return {Roo.Element} The Element
10181     */
10182     fadeIn : function(o){
10183         var el = this.getFxEl();
10184         o = o || {};
10185         el.queueFx(o, function(){
10186             this.setOpacity(0);
10187             this.fixDisplay();
10188             this.dom.style.visibility = 'visible';
10189             var to = o.endOpacity || 1;
10190             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10191                 o, null, .5, "easeOut", function(){
10192                 if(to == 1){
10193                     this.clearOpacity();
10194                 }
10195                 el.afterFx(o);
10196             });
10197         });
10198         return this;
10199     },
10200
10201    /**
10202     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10203     * using the "endOpacity" config option.
10204     * Usage:
10205 <pre><code>
10206 // default: fade out from the element's current opacity to 0
10207 el.fadeOut();
10208
10209 // custom: fade out from the element's current opacity to 25% over 2 seconds
10210 el.fadeOut({ endOpacity: .25, duration: 2});
10211
10212 // common config options shown with default values
10213 el.fadeOut({
10214     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10215     easing: 'easeOut',
10216     duration: .5
10217     remove: false,
10218     useDisplay: false
10219 });
10220 </code></pre>
10221     * @param {Object} options (optional) Object literal with any of the Fx config options
10222     * @return {Roo.Element} The Element
10223     */
10224     fadeOut : function(o){
10225         var el = this.getFxEl();
10226         o = o || {};
10227         el.queueFx(o, function(){
10228             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10229                 o, null, .5, "easeOut", function(){
10230                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10231                      this.dom.style.display = "none";
10232                 }else{
10233                      this.dom.style.visibility = "hidden";
10234                 }
10235                 this.clearOpacity();
10236                 el.afterFx(o);
10237             });
10238         });
10239         return this;
10240     },
10241
10242    /**
10243     * Animates the transition of an element's dimensions from a starting height/width
10244     * to an ending height/width.
10245     * Usage:
10246 <pre><code>
10247 // change height and width to 100x100 pixels
10248 el.scale(100, 100);
10249
10250 // common config options shown with default values.  The height and width will default to
10251 // the element's existing values if passed as null.
10252 el.scale(
10253     [element's width],
10254     [element's height], {
10255     easing: 'easeOut',
10256     duration: .35
10257 });
10258 </code></pre>
10259     * @param {Number} width  The new width (pass undefined to keep the original width)
10260     * @param {Number} height  The new height (pass undefined to keep the original height)
10261     * @param {Object} options (optional) Object literal with any of the Fx config options
10262     * @return {Roo.Element} The Element
10263     */
10264     scale : function(w, h, o){
10265         this.shift(Roo.apply({}, o, {
10266             width: w,
10267             height: h
10268         }));
10269         return this;
10270     },
10271
10272    /**
10273     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10274     * Any of these properties not specified in the config object will not be changed.  This effect 
10275     * requires that at least one new dimension, position or opacity setting must be passed in on
10276     * the config object in order for the function to have any effect.
10277     * Usage:
10278 <pre><code>
10279 // slide the element horizontally to x position 200 while changing the height and opacity
10280 el.shift({ x: 200, height: 50, opacity: .8 });
10281
10282 // common config options shown with default values.
10283 el.shift({
10284     width: [element's width],
10285     height: [element's height],
10286     x: [element's x position],
10287     y: [element's y position],
10288     opacity: [element's opacity],
10289     easing: 'easeOut',
10290     duration: .35
10291 });
10292 </code></pre>
10293     * @param {Object} options  Object literal with any of the Fx config options
10294     * @return {Roo.Element} The Element
10295     */
10296     shift : function(o){
10297         var el = this.getFxEl();
10298         o = o || {};
10299         el.queueFx(o, function(){
10300             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10301             if(w !== undefined){
10302                 a.width = {to: this.adjustWidth(w)};
10303             }
10304             if(h !== undefined){
10305                 a.height = {to: this.adjustHeight(h)};
10306             }
10307             if(x !== undefined || y !== undefined){
10308                 a.points = {to: [
10309                     x !== undefined ? x : this.getX(),
10310                     y !== undefined ? y : this.getY()
10311                 ]};
10312             }
10313             if(op !== undefined){
10314                 a.opacity = {to: op};
10315             }
10316             if(o.xy !== undefined){
10317                 a.points = {to: o.xy};
10318             }
10319             arguments.callee.anim = this.fxanim(a,
10320                 o, 'motion', .35, "easeOut", function(){
10321                 el.afterFx(o);
10322             });
10323         });
10324         return this;
10325     },
10326
10327         /**
10328          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10329          * ending point of the effect.
10330          * Usage:
10331          *<pre><code>
10332 // default: slide the element downward while fading out
10333 el.ghost();
10334
10335 // custom: slide the element out to the right with a 2-second duration
10336 el.ghost('r', { duration: 2 });
10337
10338 // common config options shown with default values
10339 el.ghost('b', {
10340     easing: 'easeOut',
10341     duration: .5
10342     remove: false,
10343     useDisplay: false
10344 });
10345 </code></pre>
10346          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10347          * @param {Object} options (optional) Object literal with any of the Fx config options
10348          * @return {Roo.Element} The Element
10349          */
10350     ghost : function(anchor, o){
10351         var el = this.getFxEl();
10352         o = o || {};
10353
10354         el.queueFx(o, function(){
10355             anchor = anchor || "b";
10356
10357             // restore values after effect
10358             var r = this.getFxRestore();
10359             var w = this.getWidth(),
10360                 h = this.getHeight();
10361
10362             var st = this.dom.style;
10363
10364             var after = function(){
10365                 if(o.useDisplay){
10366                     el.setDisplayed(false);
10367                 }else{
10368                     el.hide();
10369                 }
10370
10371                 el.clearOpacity();
10372                 el.setPositioning(r.pos);
10373                 st.width = r.width;
10374                 st.height = r.height;
10375
10376                 el.afterFx(o);
10377             };
10378
10379             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10380             switch(anchor.toLowerCase()){
10381                 case "t":
10382                     pt.by = [0, -h];
10383                 break;
10384                 case "l":
10385                     pt.by = [-w, 0];
10386                 break;
10387                 case "r":
10388                     pt.by = [w, 0];
10389                 break;
10390                 case "b":
10391                     pt.by = [0, h];
10392                 break;
10393                 case "tl":
10394                     pt.by = [-w, -h];
10395                 break;
10396                 case "bl":
10397                     pt.by = [-w, h];
10398                 break;
10399                 case "br":
10400                     pt.by = [w, h];
10401                 break;
10402                 case "tr":
10403                     pt.by = [w, -h];
10404                 break;
10405             }
10406
10407             arguments.callee.anim = this.fxanim(a,
10408                 o,
10409                 'motion',
10410                 .5,
10411                 "easeOut", after);
10412         });
10413         return this;
10414     },
10415
10416         /**
10417          * Ensures that all effects queued after syncFx is called on the element are
10418          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10419          * @return {Roo.Element} The Element
10420          */
10421     syncFx : function(){
10422         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10423             block : false,
10424             concurrent : true,
10425             stopFx : false
10426         });
10427         return this;
10428     },
10429
10430         /**
10431          * Ensures that all effects queued after sequenceFx is called on the element are
10432          * run in sequence.  This is the opposite of {@link #syncFx}.
10433          * @return {Roo.Element} The Element
10434          */
10435     sequenceFx : function(){
10436         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10437             block : false,
10438             concurrent : false,
10439             stopFx : false
10440         });
10441         return this;
10442     },
10443
10444         /* @private */
10445     nextFx : function(){
10446         var ef = this.fxQueue[0];
10447         if(ef){
10448             ef.call(this);
10449         }
10450     },
10451
10452         /**
10453          * Returns true if the element has any effects actively running or queued, else returns false.
10454          * @return {Boolean} True if element has active effects, else false
10455          */
10456     hasActiveFx : function(){
10457         return this.fxQueue && this.fxQueue[0];
10458     },
10459
10460         /**
10461          * Stops any running effects and clears the element's internal effects queue if it contains
10462          * any additional effects that haven't started yet.
10463          * @return {Roo.Element} The Element
10464          */
10465     stopFx : function(){
10466         if(this.hasActiveFx()){
10467             var cur = this.fxQueue[0];
10468             if(cur && cur.anim && cur.anim.isAnimated()){
10469                 this.fxQueue = [cur]; // clear out others
10470                 cur.anim.stop(true);
10471             }
10472         }
10473         return this;
10474     },
10475
10476         /* @private */
10477     beforeFx : function(o){
10478         if(this.hasActiveFx() && !o.concurrent){
10479            if(o.stopFx){
10480                this.stopFx();
10481                return true;
10482            }
10483            return false;
10484         }
10485         return true;
10486     },
10487
10488         /**
10489          * Returns true if the element is currently blocking so that no other effect can be queued
10490          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10491          * used to ensure that an effect initiated by a user action runs to completion prior to the
10492          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10493          * @return {Boolean} True if blocking, else false
10494          */
10495     hasFxBlock : function(){
10496         var q = this.fxQueue;
10497         return q && q[0] && q[0].block;
10498     },
10499
10500         /* @private */
10501     queueFx : function(o, fn){
10502         if(!this.fxQueue){
10503             this.fxQueue = [];
10504         }
10505         if(!this.hasFxBlock()){
10506             Roo.applyIf(o, this.fxDefaults);
10507             if(!o.concurrent){
10508                 var run = this.beforeFx(o);
10509                 fn.block = o.block;
10510                 this.fxQueue.push(fn);
10511                 if(run){
10512                     this.nextFx();
10513                 }
10514             }else{
10515                 fn.call(this);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     fxWrap : function(pos, o, vis){
10523         var wrap;
10524         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10525             var wrapXY;
10526             if(o.fixPosition){
10527                 wrapXY = this.getXY();
10528             }
10529             var div = document.createElement("div");
10530             div.style.visibility = vis;
10531             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10532             wrap.setPositioning(pos);
10533             if(wrap.getStyle("position") == "static"){
10534                 wrap.position("relative");
10535             }
10536             this.clearPositioning('auto');
10537             wrap.clip();
10538             wrap.dom.appendChild(this.dom);
10539             if(wrapXY){
10540                 wrap.setXY(wrapXY);
10541             }
10542         }
10543         return wrap;
10544     },
10545
10546         /* @private */
10547     fxUnwrap : function(wrap, pos, o){
10548         this.clearPositioning();
10549         this.setPositioning(pos);
10550         if(!o.wrap){
10551             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10552             wrap.remove();
10553         }
10554     },
10555
10556         /* @private */
10557     getFxRestore : function(){
10558         var st = this.dom.style;
10559         return {pos: this.getPositioning(), width: st.width, height : st.height};
10560     },
10561
10562         /* @private */
10563     afterFx : function(o){
10564         if(o.afterStyle){
10565             this.applyStyles(o.afterStyle);
10566         }
10567         if(o.afterCls){
10568             this.addClass(o.afterCls);
10569         }
10570         if(o.remove === true){
10571             this.remove();
10572         }
10573         Roo.callback(o.callback, o.scope, [this]);
10574         if(!o.concurrent){
10575             this.fxQueue.shift();
10576             this.nextFx();
10577         }
10578     },
10579
10580         /* @private */
10581     getFxEl : function(){ // support for composite element fx
10582         return Roo.get(this.dom);
10583     },
10584
10585         /* @private */
10586     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10587         animType = animType || 'run';
10588         opt = opt || {};
10589         var anim = Roo.lib.Anim[animType](
10590             this.dom, args,
10591             (opt.duration || defaultDur) || .35,
10592             (opt.easing || defaultEase) || 'easeOut',
10593             function(){
10594                 Roo.callback(cb, this);
10595             },
10596             this
10597         );
10598         opt.anim = anim;
10599         return anim;
10600     }
10601 };
10602
10603 // backwords compat
10604 Roo.Fx.resize = Roo.Fx.scale;
10605
10606 //When included, Roo.Fx is automatically applied to Element so that all basic
10607 //effects are available directly via the Element API
10608 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10609  * Based on:
10610  * Ext JS Library 1.1.1
10611  * Copyright(c) 2006-2007, Ext JS, LLC.
10612  *
10613  * Originally Released Under LGPL - original licence link has changed is not relivant.
10614  *
10615  * Fork - LGPL
10616  * <script type="text/javascript">
10617  */
10618
10619
10620 /**
10621  * @class Roo.CompositeElement
10622  * Standard composite class. Creates a Roo.Element for every element in the collection.
10623  * <br><br>
10624  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10625  * actions will be performed on all the elements in this collection.</b>
10626  * <br><br>
10627  * All methods return <i>this</i> and can be chained.
10628  <pre><code>
10629  var els = Roo.select("#some-el div.some-class", true);
10630  // or select directly from an existing element
10631  var el = Roo.get('some-el');
10632  el.select('div.some-class', true);
10633
10634  els.setWidth(100); // all elements become 100 width
10635  els.hide(true); // all elements fade out and hide
10636  // or
10637  els.setWidth(100).hide(true);
10638  </code></pre>
10639  */
10640 Roo.CompositeElement = function(els){
10641     this.elements = [];
10642     this.addElements(els);
10643 };
10644 Roo.CompositeElement.prototype = {
10645     isComposite: true,
10646     addElements : function(els){
10647         if(!els) return this;
10648         if(typeof els == "string"){
10649             els = Roo.Element.selectorFunction(els);
10650         }
10651         var yels = this.elements;
10652         var index = yels.length-1;
10653         for(var i = 0, len = els.length; i < len; i++) {
10654                 yels[++index] = Roo.get(els[i]);
10655         }
10656         return this;
10657     },
10658
10659     /**
10660     * Clears this composite and adds the elements returned by the passed selector.
10661     * @param {String/Array} els A string CSS selector, an array of elements or an element
10662     * @return {CompositeElement} this
10663     */
10664     fill : function(els){
10665         this.elements = [];
10666         this.add(els);
10667         return this;
10668     },
10669
10670     /**
10671     * Filters this composite to only elements that match the passed selector.
10672     * @param {String} selector A string CSS selector
10673     * @return {CompositeElement} this
10674     */
10675     filter : function(selector){
10676         var els = [];
10677         this.each(function(el){
10678             if(el.is(selector)){
10679                 els[els.length] = el.dom;
10680             }
10681         });
10682         this.fill(els);
10683         return this;
10684     },
10685
10686     invoke : function(fn, args){
10687         var els = this.elements;
10688         for(var i = 0, len = els.length; i < len; i++) {
10689                 Roo.Element.prototype[fn].apply(els[i], args);
10690         }
10691         return this;
10692     },
10693     /**
10694     * Adds elements to this composite.
10695     * @param {String/Array} els A string CSS selector, an array of elements or an element
10696     * @return {CompositeElement} this
10697     */
10698     add : function(els){
10699         if(typeof els == "string"){
10700             this.addElements(Roo.Element.selectorFunction(els));
10701         }else if(els.length !== undefined){
10702             this.addElements(els);
10703         }else{
10704             this.addElements([els]);
10705         }
10706         return this;
10707     },
10708     /**
10709     * Calls the passed function passing (el, this, index) for each element in this composite.
10710     * @param {Function} fn The function to call
10711     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10712     * @return {CompositeElement} this
10713     */
10714     each : function(fn, scope){
10715         var els = this.elements;
10716         for(var i = 0, len = els.length; i < len; i++){
10717             if(fn.call(scope || els[i], els[i], this, i) === false) {
10718                 break;
10719             }
10720         }
10721         return this;
10722     },
10723
10724     /**
10725      * Returns the Element object at the specified index
10726      * @param {Number} index
10727      * @return {Roo.Element}
10728      */
10729     item : function(index){
10730         return this.elements[index] || null;
10731     },
10732
10733     /**
10734      * Returns the first Element
10735      * @return {Roo.Element}
10736      */
10737     first : function(){
10738         return this.item(0);
10739     },
10740
10741     /**
10742      * Returns the last Element
10743      * @return {Roo.Element}
10744      */
10745     last : function(){
10746         return this.item(this.elements.length-1);
10747     },
10748
10749     /**
10750      * Returns the number of elements in this composite
10751      * @return Number
10752      */
10753     getCount : function(){
10754         return this.elements.length;
10755     },
10756
10757     /**
10758      * Returns true if this composite contains the passed element
10759      * @return Boolean
10760      */
10761     contains : function(el){
10762         return this.indexOf(el) !== -1;
10763     },
10764
10765     /**
10766      * Returns true if this composite contains the passed element
10767      * @return Boolean
10768      */
10769     indexOf : function(el){
10770         return this.elements.indexOf(Roo.get(el));
10771     },
10772
10773
10774     /**
10775     * Removes the specified element(s).
10776     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10777     * or an array of any of those.
10778     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10779     * @return {CompositeElement} this
10780     */
10781     removeElement : function(el, removeDom){
10782         if(el instanceof Array){
10783             for(var i = 0, len = el.length; i < len; i++){
10784                 this.removeElement(el[i]);
10785             }
10786             return this;
10787         }
10788         var index = typeof el == 'number' ? el : this.indexOf(el);
10789         if(index !== -1){
10790             if(removeDom){
10791                 var d = this.elements[index];
10792                 if(d.dom){
10793                     d.remove();
10794                 }else{
10795                     d.parentNode.removeChild(d);
10796                 }
10797             }
10798             this.elements.splice(index, 1);
10799         }
10800         return this;
10801     },
10802
10803     /**
10804     * Replaces the specified element with the passed element.
10805     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10806     * to replace.
10807     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10808     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10809     * @return {CompositeElement} this
10810     */
10811     replaceElement : function(el, replacement, domReplace){
10812         var index = typeof el == 'number' ? el : this.indexOf(el);
10813         if(index !== -1){
10814             if(domReplace){
10815                 this.elements[index].replaceWith(replacement);
10816             }else{
10817                 this.elements.splice(index, 1, Roo.get(replacement))
10818             }
10819         }
10820         return this;
10821     },
10822
10823     /**
10824      * Removes all elements.
10825      */
10826     clear : function(){
10827         this.elements = [];
10828     }
10829 };
10830 (function(){
10831     Roo.CompositeElement.createCall = function(proto, fnName){
10832         if(!proto[fnName]){
10833             proto[fnName] = function(){
10834                 return this.invoke(fnName, arguments);
10835             };
10836         }
10837     };
10838     for(var fnName in Roo.Element.prototype){
10839         if(typeof Roo.Element.prototype[fnName] == "function"){
10840             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10841         }
10842     };
10843 })();
10844 /*
10845  * Based on:
10846  * Ext JS Library 1.1.1
10847  * Copyright(c) 2006-2007, Ext JS, LLC.
10848  *
10849  * Originally Released Under LGPL - original licence link has changed is not relivant.
10850  *
10851  * Fork - LGPL
10852  * <script type="text/javascript">
10853  */
10854
10855 /**
10856  * @class Roo.CompositeElementLite
10857  * @extends Roo.CompositeElement
10858  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10859  <pre><code>
10860  var els = Roo.select("#some-el div.some-class");
10861  // or select directly from an existing element
10862  var el = Roo.get('some-el');
10863  el.select('div.some-class');
10864
10865  els.setWidth(100); // all elements become 100 width
10866  els.hide(true); // all elements fade out and hide
10867  // or
10868  els.setWidth(100).hide(true);
10869  </code></pre><br><br>
10870  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10871  * actions will be performed on all the elements in this collection.</b>
10872  */
10873 Roo.CompositeElementLite = function(els){
10874     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10875     this.el = new Roo.Element.Flyweight();
10876 };
10877 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10878     addElements : function(els){
10879         if(els){
10880             if(els instanceof Array){
10881                 this.elements = this.elements.concat(els);
10882             }else{
10883                 var yels = this.elements;
10884                 var index = yels.length-1;
10885                 for(var i = 0, len = els.length; i < len; i++) {
10886                     yels[++index] = els[i];
10887                 }
10888             }
10889         }
10890         return this;
10891     },
10892     invoke : function(fn, args){
10893         var els = this.elements;
10894         var el = this.el;
10895         for(var i = 0, len = els.length; i < len; i++) {
10896             el.dom = els[i];
10897                 Roo.Element.prototype[fn].apply(el, args);
10898         }
10899         return this;
10900     },
10901     /**
10902      * Returns a flyweight Element of the dom element object at the specified index
10903      * @param {Number} index
10904      * @return {Roo.Element}
10905      */
10906     item : function(index){
10907         if(!this.elements[index]){
10908             return null;
10909         }
10910         this.el.dom = this.elements[index];
10911         return this.el;
10912     },
10913
10914     // fixes scope with flyweight
10915     addListener : function(eventName, handler, scope, opt){
10916         var els = this.elements;
10917         for(var i = 0, len = els.length; i < len; i++) {
10918             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10919         }
10920         return this;
10921     },
10922
10923     /**
10924     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10925     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10926     * a reference to the dom node, use el.dom.</b>
10927     * @param {Function} fn The function to call
10928     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10929     * @return {CompositeElement} this
10930     */
10931     each : function(fn, scope){
10932         var els = this.elements;
10933         var el = this.el;
10934         for(var i = 0, len = els.length; i < len; i++){
10935             el.dom = els[i];
10936                 if(fn.call(scope || el, el, this, i) === false){
10937                 break;
10938             }
10939         }
10940         return this;
10941     },
10942
10943     indexOf : function(el){
10944         return this.elements.indexOf(Roo.getDom(el));
10945     },
10946
10947     replaceElement : function(el, replacement, domReplace){
10948         var index = typeof el == 'number' ? el : this.indexOf(el);
10949         if(index !== -1){
10950             replacement = Roo.getDom(replacement);
10951             if(domReplace){
10952                 var d = this.elements[index];
10953                 d.parentNode.insertBefore(replacement, d);
10954                 d.parentNode.removeChild(d);
10955             }
10956             this.elements.splice(index, 1, replacement);
10957         }
10958         return this;
10959     }
10960 });
10961 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10962
10963 /*
10964  * Based on:
10965  * Ext JS Library 1.1.1
10966  * Copyright(c) 2006-2007, Ext JS, LLC.
10967  *
10968  * Originally Released Under LGPL - original licence link has changed is not relivant.
10969  *
10970  * Fork - LGPL
10971  * <script type="text/javascript">
10972  */
10973
10974  
10975
10976 /**
10977  * @class Roo.data.Connection
10978  * @extends Roo.util.Observable
10979  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10980  * either to a configured URL, or to a URL specified at request time.<br><br>
10981  * <p>
10982  * Requests made by this class are asynchronous, and will return immediately. No data from
10983  * the server will be available to the statement immediately following the {@link #request} call.
10984  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10985  * <p>
10986  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10987  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10988  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10989  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10990  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10991  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10992  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10993  * standard DOM methods.
10994  * @constructor
10995  * @param {Object} config a configuration object.
10996  */
10997 Roo.data.Connection = function(config){
10998     Roo.apply(this, config);
10999     this.addEvents({
11000         /**
11001          * @event beforerequest
11002          * Fires before a network request is made to retrieve a data object.
11003          * @param {Connection} conn This Connection object.
11004          * @param {Object} options The options config object passed to the {@link #request} method.
11005          */
11006         "beforerequest" : true,
11007         /**
11008          * @event requestcomplete
11009          * Fires if the request was successfully completed.
11010          * @param {Connection} conn This Connection object.
11011          * @param {Object} response The XHR object containing the response data.
11012          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11013          * @param {Object} options The options config object passed to the {@link #request} method.
11014          */
11015         "requestcomplete" : true,
11016         /**
11017          * @event requestexception
11018          * Fires if an error HTTP status was returned from the server.
11019          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11020          * @param {Connection} conn This Connection object.
11021          * @param {Object} response The XHR object containing the response data.
11022          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11023          * @param {Object} options The options config object passed to the {@link #request} method.
11024          */
11025         "requestexception" : true
11026     });
11027     Roo.data.Connection.superclass.constructor.call(this);
11028 };
11029
11030 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11031     /**
11032      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11033      */
11034     /**
11035      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11036      * extra parameters to each request made by this object. (defaults to undefined)
11037      */
11038     /**
11039      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11040      *  to each request made by this object. (defaults to undefined)
11041      */
11042     /**
11043      * @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)
11044      */
11045     /**
11046      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11047      */
11048     timeout : 30000,
11049     /**
11050      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11051      * @type Boolean
11052      */
11053     autoAbort:false,
11054
11055     /**
11056      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11057      * @type Boolean
11058      */
11059     disableCaching: true,
11060
11061     /**
11062      * Sends an HTTP request to a remote server.
11063      * @param {Object} options An object which may contain the following properties:<ul>
11064      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11065      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11066      * request, a url encoded string or a function to call to get either.</li>
11067      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11068      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11069      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11070      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11071      * <li>options {Object} The parameter to the request call.</li>
11072      * <li>success {Boolean} True if the request succeeded.</li>
11073      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11074      * </ul></li>
11075      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11076      * The callback is passed the following parameters:<ul>
11077      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11078      * <li>options {Object} The parameter to the request call.</li>
11079      * </ul></li>
11080      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11081      * The callback is passed the following parameters:<ul>
11082      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11083      * <li>options {Object} The parameter to the request call.</li>
11084      * </ul></li>
11085      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11086      * for the callback function. Defaults to the browser window.</li>
11087      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11088      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11089      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11090      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11091      * params for the post data. Any params will be appended to the URL.</li>
11092      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11093      * </ul>
11094      * @return {Number} transactionId
11095      */
11096     request : function(o){
11097         if(this.fireEvent("beforerequest", this, o) !== false){
11098             var p = o.params;
11099
11100             if(typeof p == "function"){
11101                 p = p.call(o.scope||window, o);
11102             }
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(o.params);
11105             }
11106             if(this.extraParams){
11107                 var extras = Roo.urlEncode(this.extraParams);
11108                 p = p ? (p + '&' + extras) : extras;
11109             }
11110
11111             var url = o.url || this.url;
11112             if(typeof url == 'function'){
11113                 url = url.call(o.scope||window, o);
11114             }
11115
11116             if(o.form){
11117                 var form = Roo.getDom(o.form);
11118                 url = url || form.action;
11119
11120                 var enctype = form.getAttribute("enctype");
11121                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11122                     return this.doFormUpload(o, p, url);
11123                 }
11124                 var f = Roo.lib.Ajax.serializeForm(form);
11125                 p = p ? (p + '&' + f) : f;
11126             }
11127
11128             var hs = o.headers;
11129             if(this.defaultHeaders){
11130                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11131                 if(!o.headers){
11132                     o.headers = hs;
11133                 }
11134             }
11135
11136             var cb = {
11137                 success: this.handleResponse,
11138                 failure: this.handleFailure,
11139                 scope: this,
11140                 argument: {options: o},
11141                 timeout : this.timeout
11142             };
11143
11144             var method = o.method||this.method||(p ? "POST" : "GET");
11145
11146             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11147                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11148             }
11149
11150             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11151                 if(o.autoAbort){
11152                     this.abort();
11153                 }
11154             }else if(this.autoAbort !== false){
11155                 this.abort();
11156             }
11157
11158             if((method == 'GET' && p) || o.xmlData){
11159                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11160                 p = '';
11161             }
11162             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11163             return this.transId;
11164         }else{
11165             Roo.callback(o.callback, o.scope, [o, null, null]);
11166             return null;
11167         }
11168     },
11169
11170     /**
11171      * Determine whether this object has a request outstanding.
11172      * @param {Number} transactionId (Optional) defaults to the last transaction
11173      * @return {Boolean} True if there is an outstanding request.
11174      */
11175     isLoading : function(transId){
11176         if(transId){
11177             return Roo.lib.Ajax.isCallInProgress(transId);
11178         }else{
11179             return this.transId ? true : false;
11180         }
11181     },
11182
11183     /**
11184      * Aborts any outstanding request.
11185      * @param {Number} transactionId (Optional) defaults to the last transaction
11186      */
11187     abort : function(transId){
11188         if(transId || this.isLoading()){
11189             Roo.lib.Ajax.abort(transId || this.transId);
11190         }
11191     },
11192
11193     // private
11194     handleResponse : function(response){
11195         this.transId = false;
11196         var options = response.argument.options;
11197         response.argument = options ? options.argument : null;
11198         this.fireEvent("requestcomplete", this, response, options);
11199         Roo.callback(options.success, options.scope, [response, options]);
11200         Roo.callback(options.callback, options.scope, [options, true, response]);
11201     },
11202
11203     // private
11204     handleFailure : function(response, e){
11205         this.transId = false;
11206         var options = response.argument.options;
11207         response.argument = options ? options.argument : null;
11208         this.fireEvent("requestexception", this, response, options, e);
11209         Roo.callback(options.failure, options.scope, [response, options]);
11210         Roo.callback(options.callback, options.scope, [options, false, response]);
11211     },
11212
11213     // private
11214     doFormUpload : function(o, ps, url){
11215         var id = Roo.id();
11216         var frame = document.createElement('iframe');
11217         frame.id = id;
11218         frame.name = id;
11219         frame.className = 'x-hidden';
11220         if(Roo.isIE){
11221             frame.src = Roo.SSL_SECURE_URL;
11222         }
11223         document.body.appendChild(frame);
11224
11225         if(Roo.isIE){
11226            document.frames[id].name = id;
11227         }
11228
11229         var form = Roo.getDom(o.form);
11230         form.target = id;
11231         form.method = 'POST';
11232         form.enctype = form.encoding = 'multipart/form-data';
11233         if(url){
11234             form.action = url;
11235         }
11236
11237         var hiddens, hd;
11238         if(ps){ // add dynamic params
11239             hiddens = [];
11240             ps = Roo.urlDecode(ps, false);
11241             for(var k in ps){
11242                 if(ps.hasOwnProperty(k)){
11243                     hd = document.createElement('input');
11244                     hd.type = 'hidden';
11245                     hd.name = k;
11246                     hd.value = ps[k];
11247                     form.appendChild(hd);
11248                     hiddens.push(hd);
11249                 }
11250             }
11251         }
11252
11253         function cb(){
11254             var r = {  // bogus response object
11255                 responseText : '',
11256                 responseXML : null
11257             };
11258
11259             r.argument = o ? o.argument : null;
11260
11261             try { //
11262                 var doc;
11263                 if(Roo.isIE){
11264                     doc = frame.contentWindow.document;
11265                 }else {
11266                     doc = (frame.contentDocument || window.frames[id].document);
11267                 }
11268                 if(doc && doc.body){
11269                     r.responseText = doc.body.innerHTML;
11270                 }
11271                 if(doc && doc.XMLDocument){
11272                     r.responseXML = doc.XMLDocument;
11273                 }else {
11274                     r.responseXML = doc;
11275                 }
11276             }
11277             catch(e) {
11278                 // ignore
11279             }
11280
11281             Roo.EventManager.removeListener(frame, 'load', cb, this);
11282
11283             this.fireEvent("requestcomplete", this, r, o);
11284             Roo.callback(o.success, o.scope, [r, o]);
11285             Roo.callback(o.callback, o.scope, [o, true, r]);
11286
11287             setTimeout(function(){document.body.removeChild(frame);}, 100);
11288         }
11289
11290         Roo.EventManager.on(frame, 'load', cb, this);
11291         form.submit();
11292
11293         if(hiddens){ // remove dynamic params
11294             for(var i = 0, len = hiddens.length; i < len; i++){
11295                 form.removeChild(hiddens[i]);
11296             }
11297         }
11298     }
11299 });
11300
11301 /**
11302  * @class Roo.Ajax
11303  * @extends Roo.data.Connection
11304  * Global Ajax request class.
11305  *
11306  * @singleton
11307  */
11308 Roo.Ajax = new Roo.data.Connection({
11309     // fix up the docs
11310    /**
11311      * @cfg {String} url @hide
11312      */
11313     /**
11314      * @cfg {Object} extraParams @hide
11315      */
11316     /**
11317      * @cfg {Object} defaultHeaders @hide
11318      */
11319     /**
11320      * @cfg {String} method (Optional) @hide
11321      */
11322     /**
11323      * @cfg {Number} timeout (Optional) @hide
11324      */
11325     /**
11326      * @cfg {Boolean} autoAbort (Optional) @hide
11327      */
11328
11329     /**
11330      * @cfg {Boolean} disableCaching (Optional) @hide
11331      */
11332
11333     /**
11334      * @property  disableCaching
11335      * True to add a unique cache-buster param to GET requests. (defaults to true)
11336      * @type Boolean
11337      */
11338     /**
11339      * @property  url
11340      * The default URL to be used for requests to the server. (defaults to undefined)
11341      * @type String
11342      */
11343     /**
11344      * @property  extraParams
11345      * An object containing properties which are used as
11346      * extra parameters to each request made by this object. (defaults to undefined)
11347      * @type Object
11348      */
11349     /**
11350      * @property  defaultHeaders
11351      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11352      * @type Object
11353      */
11354     /**
11355      * @property  method
11356      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11357      * @type String
11358      */
11359     /**
11360      * @property  timeout
11361      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11362      * @type Number
11363      */
11364
11365     /**
11366      * @property  autoAbort
11367      * Whether a new request should abort any pending requests. (defaults to false)
11368      * @type Boolean
11369      */
11370     autoAbort : false,
11371
11372     /**
11373      * Serialize the passed form into a url encoded string
11374      * @param {String/HTMLElement} form
11375      * @return {String}
11376      */
11377     serializeForm : function(form){
11378         return Roo.lib.Ajax.serializeForm(form);
11379     }
11380 });/*
11381  * Based on:
11382  * Ext JS Library 1.1.1
11383  * Copyright(c) 2006-2007, Ext JS, LLC.
11384  *
11385  * Originally Released Under LGPL - original licence link has changed is not relivant.
11386  *
11387  * Fork - LGPL
11388  * <script type="text/javascript">
11389  */
11390  
11391 /**
11392  * @class Roo.Ajax
11393  * @extends Roo.data.Connection
11394  * Global Ajax request class.
11395  *
11396  * @instanceOf  Roo.data.Connection
11397  */
11398 Roo.Ajax = new Roo.data.Connection({
11399     // fix up the docs
11400     
11401     /**
11402      * fix up scoping
11403      * @scope Roo.Ajax
11404      */
11405     
11406    /**
11407      * @cfg {String} url @hide
11408      */
11409     /**
11410      * @cfg {Object} extraParams @hide
11411      */
11412     /**
11413      * @cfg {Object} defaultHeaders @hide
11414      */
11415     /**
11416      * @cfg {String} method (Optional) @hide
11417      */
11418     /**
11419      * @cfg {Number} timeout (Optional) @hide
11420      */
11421     /**
11422      * @cfg {Boolean} autoAbort (Optional) @hide
11423      */
11424
11425     /**
11426      * @cfg {Boolean} disableCaching (Optional) @hide
11427      */
11428
11429     /**
11430      * @property  disableCaching
11431      * True to add a unique cache-buster param to GET requests. (defaults to true)
11432      * @type Boolean
11433      */
11434     /**
11435      * @property  url
11436      * The default URL to be used for requests to the server. (defaults to undefined)
11437      * @type String
11438      */
11439     /**
11440      * @property  extraParams
11441      * An object containing properties which are used as
11442      * extra parameters to each request made by this object. (defaults to undefined)
11443      * @type Object
11444      */
11445     /**
11446      * @property  defaultHeaders
11447      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11448      * @type Object
11449      */
11450     /**
11451      * @property  method
11452      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11453      * @type String
11454      */
11455     /**
11456      * @property  timeout
11457      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11458      * @type Number
11459      */
11460
11461     /**
11462      * @property  autoAbort
11463      * Whether a new request should abort any pending requests. (defaults to false)
11464      * @type Boolean
11465      */
11466     autoAbort : false,
11467
11468     /**
11469      * Serialize the passed form into a url encoded string
11470      * @param {String/HTMLElement} form
11471      * @return {String}
11472      */
11473     serializeForm : function(form){
11474         return Roo.lib.Ajax.serializeForm(form);
11475     }
11476 });/*
11477  * Based on:
11478  * Ext JS Library 1.1.1
11479  * Copyright(c) 2006-2007, Ext JS, LLC.
11480  *
11481  * Originally Released Under LGPL - original licence link has changed is not relivant.
11482  *
11483  * Fork - LGPL
11484  * <script type="text/javascript">
11485  */
11486
11487  
11488 /**
11489  * @class Roo.UpdateManager
11490  * @extends Roo.util.Observable
11491  * Provides AJAX-style update for Element object.<br><br>
11492  * Usage:<br>
11493  * <pre><code>
11494  * // Get it from a Roo.Element object
11495  * var el = Roo.get("foo");
11496  * var mgr = el.getUpdateManager();
11497  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11498  * ...
11499  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11500  * <br>
11501  * // or directly (returns the same UpdateManager instance)
11502  * var mgr = new Roo.UpdateManager("myElementId");
11503  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11504  * mgr.on("update", myFcnNeedsToKnow);
11505  * <br>
11506    // short handed call directly from the element object
11507    Roo.get("foo").load({
11508         url: "bar.php",
11509         scripts:true,
11510         params: "for=bar",
11511         text: "Loading Foo..."
11512    });
11513  * </code></pre>
11514  * @constructor
11515  * Create new UpdateManager directly.
11516  * @param {String/HTMLElement/Roo.Element} el The element to update
11517  * @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).
11518  */
11519 Roo.UpdateManager = function(el, forceNew){
11520     el = Roo.get(el);
11521     if(!forceNew && el.updateManager){
11522         return el.updateManager;
11523     }
11524     /**
11525      * The Element object
11526      * @type Roo.Element
11527      */
11528     this.el = el;
11529     /**
11530      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11531      * @type String
11532      */
11533     this.defaultUrl = null;
11534
11535     this.addEvents({
11536         /**
11537          * @event beforeupdate
11538          * Fired before an update is made, return false from your handler and the update is cancelled.
11539          * @param {Roo.Element} el
11540          * @param {String/Object/Function} url
11541          * @param {String/Object} params
11542          */
11543         "beforeupdate": true,
11544         /**
11545          * @event update
11546          * Fired after successful update is made.
11547          * @param {Roo.Element} el
11548          * @param {Object} oResponseObject The response Object
11549          */
11550         "update": true,
11551         /**
11552          * @event failure
11553          * Fired on update failure.
11554          * @param {Roo.Element} el
11555          * @param {Object} oResponseObject The response Object
11556          */
11557         "failure": true
11558     });
11559     var d = Roo.UpdateManager.defaults;
11560     /**
11561      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11562      * @type String
11563      */
11564     this.sslBlankUrl = d.sslBlankUrl;
11565     /**
11566      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11567      * @type Boolean
11568      */
11569     this.disableCaching = d.disableCaching;
11570     /**
11571      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11572      * @type String
11573      */
11574     this.indicatorText = d.indicatorText;
11575     /**
11576      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11577      * @type String
11578      */
11579     this.showLoadIndicator = d.showLoadIndicator;
11580     /**
11581      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11582      * @type Number
11583      */
11584     this.timeout = d.timeout;
11585
11586     /**
11587      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11588      * @type Boolean
11589      */
11590     this.loadScripts = d.loadScripts;
11591
11592     /**
11593      * Transaction object of current executing transaction
11594      */
11595     this.transaction = null;
11596
11597     /**
11598      * @private
11599      */
11600     this.autoRefreshProcId = null;
11601     /**
11602      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11603      * @type Function
11604      */
11605     this.refreshDelegate = this.refresh.createDelegate(this);
11606     /**
11607      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11608      * @type Function
11609      */
11610     this.updateDelegate = this.update.createDelegate(this);
11611     /**
11612      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11613      * @type Function
11614      */
11615     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11616     /**
11617      * @private
11618      */
11619     this.successDelegate = this.processSuccess.createDelegate(this);
11620     /**
11621      * @private
11622      */
11623     this.failureDelegate = this.processFailure.createDelegate(this);
11624
11625     if(!this.renderer){
11626      /**
11627       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11628       */
11629     this.renderer = new Roo.UpdateManager.BasicRenderer();
11630     }
11631     
11632     Roo.UpdateManager.superclass.constructor.call(this);
11633 };
11634
11635 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11636     /**
11637      * Get the Element this UpdateManager is bound to
11638      * @return {Roo.Element} The element
11639      */
11640     getEl : function(){
11641         return this.el;
11642     },
11643     /**
11644      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11645      * @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:
11646 <pre><code>
11647 um.update({<br/>
11648     url: "your-url.php",<br/>
11649     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11650     callback: yourFunction,<br/>
11651     scope: yourObject, //(optional scope)  <br/>
11652     discardUrl: false, <br/>
11653     nocache: false,<br/>
11654     text: "Loading...",<br/>
11655     timeout: 30,<br/>
11656     scripts: false<br/>
11657 });
11658 </code></pre>
11659      * The only required property is url. The optional properties nocache, text and scripts
11660      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11661      * @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}
11662      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11663      * @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.
11664      */
11665     update : function(url, params, callback, discardUrl){
11666         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11667             var method = this.method, cfg;
11668             if(typeof url == "object"){ // must be config object
11669                 cfg = url;
11670                 url = cfg.url;
11671                 params = params || cfg.params;
11672                 callback = callback || cfg.callback;
11673                 discardUrl = discardUrl || cfg.discardUrl;
11674                 if(callback && cfg.scope){
11675                     callback = callback.createDelegate(cfg.scope);
11676                 }
11677                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11678                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11679                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11680                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11681                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11682             }
11683             this.showLoading();
11684             if(!discardUrl){
11685                 this.defaultUrl = url;
11686             }
11687             if(typeof url == "function"){
11688                 url = url.call(this);
11689             }
11690
11691             method = method || (params ? "POST" : "GET");
11692             if(method == "GET"){
11693                 url = this.prepareUrl(url);
11694             }
11695
11696             var o = Roo.apply(cfg ||{}, {
11697                 url : url,
11698                 params: params,
11699                 success: this.successDelegate,
11700                 failure: this.failureDelegate,
11701                 callback: undefined,
11702                 timeout: (this.timeout*1000),
11703                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11704             });
11705
11706             this.transaction = Roo.Ajax.request(o);
11707         }
11708     },
11709
11710     /**
11711      * 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.
11712      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11713      * @param {String/HTMLElement} form The form Id or form element
11714      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11715      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11716      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11717      */
11718     formUpdate : function(form, url, reset, callback){
11719         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11720             if(typeof url == "function"){
11721                 url = url.call(this);
11722             }
11723             form = Roo.getDom(form);
11724             this.transaction = Roo.Ajax.request({
11725                 form: form,
11726                 url:url,
11727                 success: this.successDelegate,
11728                 failure: this.failureDelegate,
11729                 timeout: (this.timeout*1000),
11730                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11731             });
11732             this.showLoading.defer(1, this);
11733         }
11734     },
11735
11736     /**
11737      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11738      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11739      */
11740     refresh : function(callback){
11741         if(this.defaultUrl == null){
11742             return;
11743         }
11744         this.update(this.defaultUrl, null, callback, true);
11745     },
11746
11747     /**
11748      * Set this element to auto refresh.
11749      * @param {Number} interval How often to update (in seconds).
11750      * @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)
11751      * @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}
11752      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11753      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11754      */
11755     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11756         if(refreshNow){
11757             this.update(url || this.defaultUrl, params, callback, true);
11758         }
11759         if(this.autoRefreshProcId){
11760             clearInterval(this.autoRefreshProcId);
11761         }
11762         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11763     },
11764
11765     /**
11766      * Stop auto refresh on this element.
11767      */
11768      stopAutoRefresh : function(){
11769         if(this.autoRefreshProcId){
11770             clearInterval(this.autoRefreshProcId);
11771             delete this.autoRefreshProcId;
11772         }
11773     },
11774
11775     isAutoRefreshing : function(){
11776        return this.autoRefreshProcId ? true : false;
11777     },
11778     /**
11779      * Called to update the element to "Loading" state. Override to perform custom action.
11780      */
11781     showLoading : function(){
11782         if(this.showLoadIndicator){
11783             this.el.update(this.indicatorText);
11784         }
11785     },
11786
11787     /**
11788      * Adds unique parameter to query string if disableCaching = true
11789      * @private
11790      */
11791     prepareUrl : function(url){
11792         if(this.disableCaching){
11793             var append = "_dc=" + (new Date().getTime());
11794             if(url.indexOf("?") !== -1){
11795                 url += "&" + append;
11796             }else{
11797                 url += "?" + append;
11798             }
11799         }
11800         return url;
11801     },
11802
11803     /**
11804      * @private
11805      */
11806     processSuccess : function(response){
11807         this.transaction = null;
11808         if(response.argument.form && response.argument.reset){
11809             try{ // put in try/catch since some older FF releases had problems with this
11810                 response.argument.form.reset();
11811             }catch(e){}
11812         }
11813         if(this.loadScripts){
11814             this.renderer.render(this.el, response, this,
11815                 this.updateComplete.createDelegate(this, [response]));
11816         }else{
11817             this.renderer.render(this.el, response, this);
11818             this.updateComplete(response);
11819         }
11820     },
11821
11822     updateComplete : function(response){
11823         this.fireEvent("update", this.el, response);
11824         if(typeof response.argument.callback == "function"){
11825             response.argument.callback(this.el, true, response);
11826         }
11827     },
11828
11829     /**
11830      * @private
11831      */
11832     processFailure : function(response){
11833         this.transaction = null;
11834         this.fireEvent("failure", this.el, response);
11835         if(typeof response.argument.callback == "function"){
11836             response.argument.callback(this.el, false, response);
11837         }
11838     },
11839
11840     /**
11841      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11842      * @param {Object} renderer The object implementing the render() method
11843      */
11844     setRenderer : function(renderer){
11845         this.renderer = renderer;
11846     },
11847
11848     getRenderer : function(){
11849        return this.renderer;
11850     },
11851
11852     /**
11853      * Set the defaultUrl used for updates
11854      * @param {String/Function} defaultUrl The url or a function to call to get the url
11855      */
11856     setDefaultUrl : function(defaultUrl){
11857         this.defaultUrl = defaultUrl;
11858     },
11859
11860     /**
11861      * Aborts the executing transaction
11862      */
11863     abort : function(){
11864         if(this.transaction){
11865             Roo.Ajax.abort(this.transaction);
11866         }
11867     },
11868
11869     /**
11870      * Returns true if an update is in progress
11871      * @return {Boolean}
11872      */
11873     isUpdating : function(){
11874         if(this.transaction){
11875             return Roo.Ajax.isLoading(this.transaction);
11876         }
11877         return false;
11878     }
11879 });
11880
11881 /**
11882  * @class Roo.UpdateManager.defaults
11883  * @static (not really - but it helps the doc tool)
11884  * The defaults collection enables customizing the default properties of UpdateManager
11885  */
11886    Roo.UpdateManager.defaults = {
11887        /**
11888          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11889          * @type Number
11890          */
11891          timeout : 30,
11892
11893          /**
11894          * True to process scripts by default (Defaults to false).
11895          * @type Boolean
11896          */
11897         loadScripts : false,
11898
11899         /**
11900         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11901         * @type String
11902         */
11903         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11904         /**
11905          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11906          * @type Boolean
11907          */
11908         disableCaching : false,
11909         /**
11910          * Whether to show indicatorText when loading (Defaults to true).
11911          * @type Boolean
11912          */
11913         showLoadIndicator : true,
11914         /**
11915          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11916          * @type String
11917          */
11918         indicatorText : '<div class="loading-indicator">Loading...</div>'
11919    };
11920
11921 /**
11922  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11923  *Usage:
11924  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11925  * @param {String/HTMLElement/Roo.Element} el The element to update
11926  * @param {String} url The url
11927  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11928  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11929  * @static
11930  * @deprecated
11931  * @member Roo.UpdateManager
11932  */
11933 Roo.UpdateManager.updateElement = function(el, url, params, options){
11934     var um = Roo.get(el, true).getUpdateManager();
11935     Roo.apply(um, options);
11936     um.update(url, params, options ? options.callback : null);
11937 };
11938 // alias for backwards compat
11939 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11940 /**
11941  * @class Roo.UpdateManager.BasicRenderer
11942  * Default Content renderer. Updates the elements innerHTML with the responseText.
11943  */
11944 Roo.UpdateManager.BasicRenderer = function(){};
11945
11946 Roo.UpdateManager.BasicRenderer.prototype = {
11947     /**
11948      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11949      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11950      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11951      * @param {Roo.Element} el The element being rendered
11952      * @param {Object} response The YUI Connect response object
11953      * @param {UpdateManager} updateManager The calling update manager
11954      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11955      */
11956      render : function(el, response, updateManager, callback){
11957         el.update(response.responseText, updateManager.loadScripts, callback);
11958     }
11959 };
11960 /*
11961  * Based on:
11962  * Ext JS Library 1.1.1
11963  * Copyright(c) 2006-2007, Ext JS, LLC.
11964  *
11965  * Originally Released Under LGPL - original licence link has changed is not relivant.
11966  *
11967  * Fork - LGPL
11968  * <script type="text/javascript">
11969  */
11970
11971 /**
11972  * @class Roo.util.DelayedTask
11973  * Provides a convenient method of performing setTimeout where a new
11974  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11975  * You can use this class to buffer
11976  * the keypress events for a certain number of milliseconds, and perform only if they stop
11977  * for that amount of time.
11978  * @constructor The parameters to this constructor serve as defaults and are not required.
11979  * @param {Function} fn (optional) The default function to timeout
11980  * @param {Object} scope (optional) The default scope of that timeout
11981  * @param {Array} args (optional) The default Array of arguments
11982  */
11983 Roo.util.DelayedTask = function(fn, scope, args){
11984     var id = null, d, t;
11985
11986     var call = function(){
11987         var now = new Date().getTime();
11988         if(now - t >= d){
11989             clearInterval(id);
11990             id = null;
11991             fn.apply(scope, args || []);
11992         }
11993     };
11994     /**
11995      * Cancels any pending timeout and queues a new one
11996      * @param {Number} delay The milliseconds to delay
11997      * @param {Function} newFn (optional) Overrides function passed to constructor
11998      * @param {Object} newScope (optional) Overrides scope passed to constructor
11999      * @param {Array} newArgs (optional) Overrides args passed to constructor
12000      */
12001     this.delay = function(delay, newFn, newScope, newArgs){
12002         if(id && delay != d){
12003             this.cancel();
12004         }
12005         d = delay;
12006         t = new Date().getTime();
12007         fn = newFn || fn;
12008         scope = newScope || scope;
12009         args = newArgs || args;
12010         if(!id){
12011             id = setInterval(call, d);
12012         }
12013     };
12014
12015     /**
12016      * Cancel the last queued timeout
12017      */
12018     this.cancel = function(){
12019         if(id){
12020             clearInterval(id);
12021             id = null;
12022         }
12023     };
12024 };/*
12025  * Based on:
12026  * Ext JS Library 1.1.1
12027  * Copyright(c) 2006-2007, Ext JS, LLC.
12028  *
12029  * Originally Released Under LGPL - original licence link has changed is not relivant.
12030  *
12031  * Fork - LGPL
12032  * <script type="text/javascript">
12033  */
12034  
12035  
12036 Roo.util.TaskRunner = function(interval){
12037     interval = interval || 10;
12038     var tasks = [], removeQueue = [];
12039     var id = 0;
12040     var running = false;
12041
12042     var stopThread = function(){
12043         running = false;
12044         clearInterval(id);
12045         id = 0;
12046     };
12047
12048     var startThread = function(){
12049         if(!running){
12050             running = true;
12051             id = setInterval(runTasks, interval);
12052         }
12053     };
12054
12055     var removeTask = function(task){
12056         removeQueue.push(task);
12057         if(task.onStop){
12058             task.onStop();
12059         }
12060     };
12061
12062     var runTasks = function(){
12063         if(removeQueue.length > 0){
12064             for(var i = 0, len = removeQueue.length; i < len; i++){
12065                 tasks.remove(removeQueue[i]);
12066             }
12067             removeQueue = [];
12068             if(tasks.length < 1){
12069                 stopThread();
12070                 return;
12071             }
12072         }
12073         var now = new Date().getTime();
12074         for(var i = 0, len = tasks.length; i < len; ++i){
12075             var t = tasks[i];
12076             var itime = now - t.taskRunTime;
12077             if(t.interval <= itime){
12078                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12079                 t.taskRunTime = now;
12080                 if(rt === false || t.taskRunCount === t.repeat){
12081                     removeTask(t);
12082                     return;
12083                 }
12084             }
12085             if(t.duration && t.duration <= (now - t.taskStartTime)){
12086                 removeTask(t);
12087             }
12088         }
12089     };
12090
12091     /**
12092      * Queues a new task.
12093      * @param {Object} task
12094      */
12095     this.start = function(task){
12096         tasks.push(task);
12097         task.taskStartTime = new Date().getTime();
12098         task.taskRunTime = 0;
12099         task.taskRunCount = 0;
12100         startThread();
12101         return task;
12102     };
12103
12104     this.stop = function(task){
12105         removeTask(task);
12106         return task;
12107     };
12108
12109     this.stopAll = function(){
12110         stopThread();
12111         for(var i = 0, len = tasks.length; i < len; i++){
12112             if(tasks[i].onStop){
12113                 tasks[i].onStop();
12114             }
12115         }
12116         tasks = [];
12117         removeQueue = [];
12118     };
12119 };
12120
12121 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12122  * Based on:
12123  * Ext JS Library 1.1.1
12124  * Copyright(c) 2006-2007, Ext JS, LLC.
12125  *
12126  * Originally Released Under LGPL - original licence link has changed is not relivant.
12127  *
12128  * Fork - LGPL
12129  * <script type="text/javascript">
12130  */
12131
12132  
12133 /**
12134  * @class Roo.util.MixedCollection
12135  * @extends Roo.util.Observable
12136  * A Collection class that maintains both numeric indexes and keys and exposes events.
12137  * @constructor
12138  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12139  * collection (defaults to false)
12140  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12141  * and return the key value for that item.  This is used when available to look up the key on items that
12142  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12143  * equivalent to providing an implementation for the {@link #getKey} method.
12144  */
12145 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12146     this.items = [];
12147     this.map = {};
12148     this.keys = [];
12149     this.length = 0;
12150     this.addEvents({
12151         /**
12152          * @event clear
12153          * Fires when the collection is cleared.
12154          */
12155         "clear" : true,
12156         /**
12157          * @event add
12158          * Fires when an item is added to the collection.
12159          * @param {Number} index The index at which the item was added.
12160          * @param {Object} o The item added.
12161          * @param {String} key The key associated with the added item.
12162          */
12163         "add" : true,
12164         /**
12165          * @event replace
12166          * Fires when an item is replaced in the collection.
12167          * @param {String} key he key associated with the new added.
12168          * @param {Object} old The item being replaced.
12169          * @param {Object} new The new item.
12170          */
12171         "replace" : true,
12172         /**
12173          * @event remove
12174          * Fires when an item is removed from the collection.
12175          * @param {Object} o The item being removed.
12176          * @param {String} key (optional) The key associated with the removed item.
12177          */
12178         "remove" : true,
12179         "sort" : true
12180     });
12181     this.allowFunctions = allowFunctions === true;
12182     if(keyFn){
12183         this.getKey = keyFn;
12184     }
12185     Roo.util.MixedCollection.superclass.constructor.call(this);
12186 };
12187
12188 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12189     allowFunctions : false,
12190     
12191 /**
12192  * Adds an item to the collection.
12193  * @param {String} key The key to associate with the item
12194  * @param {Object} o The item to add.
12195  * @return {Object} The item added.
12196  */
12197     add : function(key, o){
12198         if(arguments.length == 1){
12199             o = arguments[0];
12200             key = this.getKey(o);
12201         }
12202         if(typeof key == "undefined" || key === null){
12203             this.length++;
12204             this.items.push(o);
12205             this.keys.push(null);
12206         }else{
12207             var old = this.map[key];
12208             if(old){
12209                 return this.replace(key, o);
12210             }
12211             this.length++;
12212             this.items.push(o);
12213             this.map[key] = o;
12214             this.keys.push(key);
12215         }
12216         this.fireEvent("add", this.length-1, o, key);
12217         return o;
12218     },
12219    
12220 /**
12221   * MixedCollection has a generic way to fetch keys if you implement getKey.
12222 <pre><code>
12223 // normal way
12224 var mc = new Roo.util.MixedCollection();
12225 mc.add(someEl.dom.id, someEl);
12226 mc.add(otherEl.dom.id, otherEl);
12227 //and so on
12228
12229 // using getKey
12230 var mc = new Roo.util.MixedCollection();
12231 mc.getKey = function(el){
12232    return el.dom.id;
12233 };
12234 mc.add(someEl);
12235 mc.add(otherEl);
12236
12237 // or via the constructor
12238 var mc = new Roo.util.MixedCollection(false, function(el){
12239    return el.dom.id;
12240 });
12241 mc.add(someEl);
12242 mc.add(otherEl);
12243 </code></pre>
12244  * @param o {Object} The item for which to find the key.
12245  * @return {Object} The key for the passed item.
12246  */
12247     getKey : function(o){
12248          return o.id; 
12249     },
12250    
12251 /**
12252  * Replaces an item in the collection.
12253  * @param {String} key The key associated with the item to replace, or the item to replace.
12254  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12255  * @return {Object}  The new item.
12256  */
12257     replace : function(key, o){
12258         if(arguments.length == 1){
12259             o = arguments[0];
12260             key = this.getKey(o);
12261         }
12262         var old = this.item(key);
12263         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12264              return this.add(key, o);
12265         }
12266         var index = this.indexOfKey(key);
12267         this.items[index] = o;
12268         this.map[key] = o;
12269         this.fireEvent("replace", key, old, o);
12270         return o;
12271     },
12272    
12273 /**
12274  * Adds all elements of an Array or an Object to the collection.
12275  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12276  * an Array of values, each of which are added to the collection.
12277  */
12278     addAll : function(objs){
12279         if(arguments.length > 1 || objs instanceof Array){
12280             var args = arguments.length > 1 ? arguments : objs;
12281             for(var i = 0, len = args.length; i < len; i++){
12282                 this.add(args[i]);
12283             }
12284         }else{
12285             for(var key in objs){
12286                 if(this.allowFunctions || typeof objs[key] != "function"){
12287                     this.add(key, objs[key]);
12288                 }
12289             }
12290         }
12291     },
12292    
12293 /**
12294  * Executes the specified function once for every item in the collection, passing each
12295  * item as the first and only parameter. returning false from the function will stop the iteration.
12296  * @param {Function} fn The function to execute for each item.
12297  * @param {Object} scope (optional) The scope in which to execute the function.
12298  */
12299     each : function(fn, scope){
12300         var items = [].concat(this.items); // each safe for removal
12301         for(var i = 0, len = items.length; i < len; i++){
12302             if(fn.call(scope || items[i], items[i], i, len) === false){
12303                 break;
12304             }
12305         }
12306     },
12307    
12308 /**
12309  * Executes the specified function once for every key in the collection, passing each
12310  * key, and its associated item as the first two parameters.
12311  * @param {Function} fn The function to execute for each item.
12312  * @param {Object} scope (optional) The scope in which to execute the function.
12313  */
12314     eachKey : function(fn, scope){
12315         for(var i = 0, len = this.keys.length; i < len; i++){
12316             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12317         }
12318     },
12319    
12320 /**
12321  * Returns the first item in the collection which elicits a true return value from the
12322  * passed selection function.
12323  * @param {Function} fn The selection function to execute for each item.
12324  * @param {Object} scope (optional) The scope in which to execute the function.
12325  * @return {Object} The first item in the collection which returned true from the selection function.
12326  */
12327     find : function(fn, scope){
12328         for(var i = 0, len = this.items.length; i < len; i++){
12329             if(fn.call(scope || window, this.items[i], this.keys[i])){
12330                 return this.items[i];
12331             }
12332         }
12333         return null;
12334     },
12335    
12336 /**
12337  * Inserts an item at the specified index in the collection.
12338  * @param {Number} index The index to insert the item at.
12339  * @param {String} key The key to associate with the new item, or the item itself.
12340  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12341  * @return {Object} The item inserted.
12342  */
12343     insert : function(index, key, o){
12344         if(arguments.length == 2){
12345             o = arguments[1];
12346             key = this.getKey(o);
12347         }
12348         if(index >= this.length){
12349             return this.add(key, o);
12350         }
12351         this.length++;
12352         this.items.splice(index, 0, o);
12353         if(typeof key != "undefined" && key != null){
12354             this.map[key] = o;
12355         }
12356         this.keys.splice(index, 0, key);
12357         this.fireEvent("add", index, o, key);
12358         return o;
12359     },
12360    
12361 /**
12362  * Removed an item from the collection.
12363  * @param {Object} o The item to remove.
12364  * @return {Object} The item removed.
12365  */
12366     remove : function(o){
12367         return this.removeAt(this.indexOf(o));
12368     },
12369    
12370 /**
12371  * Remove an item from a specified index in the collection.
12372  * @param {Number} index The index within the collection of the item to remove.
12373  */
12374     removeAt : function(index){
12375         if(index < this.length && index >= 0){
12376             this.length--;
12377             var o = this.items[index];
12378             this.items.splice(index, 1);
12379             var key = this.keys[index];
12380             if(typeof key != "undefined"){
12381                 delete this.map[key];
12382             }
12383             this.keys.splice(index, 1);
12384             this.fireEvent("remove", o, key);
12385         }
12386     },
12387    
12388 /**
12389  * Removed an item associated with the passed key fom the collection.
12390  * @param {String} key The key of the item to remove.
12391  */
12392     removeKey : function(key){
12393         return this.removeAt(this.indexOfKey(key));
12394     },
12395    
12396 /**
12397  * Returns the number of items in the collection.
12398  * @return {Number} the number of items in the collection.
12399  */
12400     getCount : function(){
12401         return this.length; 
12402     },
12403    
12404 /**
12405  * Returns index within the collection of the passed Object.
12406  * @param {Object} o The item to find the index of.
12407  * @return {Number} index of the item.
12408  */
12409     indexOf : function(o){
12410         if(!this.items.indexOf){
12411             for(var i = 0, len = this.items.length; i < len; i++){
12412                 if(this.items[i] == o) return i;
12413             }
12414             return -1;
12415         }else{
12416             return this.items.indexOf(o);
12417         }
12418     },
12419    
12420 /**
12421  * Returns index within the collection of the passed key.
12422  * @param {String} key The key to find the index of.
12423  * @return {Number} index of the key.
12424  */
12425     indexOfKey : function(key){
12426         if(!this.keys.indexOf){
12427             for(var i = 0, len = this.keys.length; i < len; i++){
12428                 if(this.keys[i] == key) return i;
12429             }
12430             return -1;
12431         }else{
12432             return this.keys.indexOf(key);
12433         }
12434     },
12435    
12436 /**
12437  * Returns the item associated with the passed key OR index. Key has priority over index.
12438  * @param {String/Number} key The key or index of the item.
12439  * @return {Object} The item associated with the passed key.
12440  */
12441     item : function(key){
12442         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12443         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12444     },
12445     
12446 /**
12447  * Returns the item at the specified index.
12448  * @param {Number} index The index of the item.
12449  * @return {Object}
12450  */
12451     itemAt : function(index){
12452         return this.items[index];
12453     },
12454     
12455 /**
12456  * Returns the item associated with the passed key.
12457  * @param {String/Number} key The key of the item.
12458  * @return {Object} The item associated with the passed key.
12459  */
12460     key : function(key){
12461         return this.map[key];
12462     },
12463    
12464 /**
12465  * Returns true if the collection contains the passed Object as an item.
12466  * @param {Object} o  The Object to look for in the collection.
12467  * @return {Boolean} True if the collection contains the Object as an item.
12468  */
12469     contains : function(o){
12470         return this.indexOf(o) != -1;
12471     },
12472    
12473 /**
12474  * Returns true if the collection contains the passed Object as a key.
12475  * @param {String} key The key to look for in the collection.
12476  * @return {Boolean} True if the collection contains the Object as a key.
12477  */
12478     containsKey : function(key){
12479         return typeof this.map[key] != "undefined";
12480     },
12481    
12482 /**
12483  * Removes all items from the collection.
12484  */
12485     clear : function(){
12486         this.length = 0;
12487         this.items = [];
12488         this.keys = [];
12489         this.map = {};
12490         this.fireEvent("clear");
12491     },
12492    
12493 /**
12494  * Returns the first item in the collection.
12495  * @return {Object} the first item in the collection..
12496  */
12497     first : function(){
12498         return this.items[0]; 
12499     },
12500    
12501 /**
12502  * Returns the last item in the collection.
12503  * @return {Object} the last item in the collection..
12504  */
12505     last : function(){
12506         return this.items[this.length-1];   
12507     },
12508     
12509     _sort : function(property, dir, fn){
12510         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12511         fn = fn || function(a, b){
12512             return a-b;
12513         };
12514         var c = [], k = this.keys, items = this.items;
12515         for(var i = 0, len = items.length; i < len; i++){
12516             c[c.length] = {key: k[i], value: items[i], index: i};
12517         }
12518         c.sort(function(a, b){
12519             var v = fn(a[property], b[property]) * dsc;
12520             if(v == 0){
12521                 v = (a.index < b.index ? -1 : 1);
12522             }
12523             return v;
12524         });
12525         for(var i = 0, len = c.length; i < len; i++){
12526             items[i] = c[i].value;
12527             k[i] = c[i].key;
12528         }
12529         this.fireEvent("sort", this);
12530     },
12531     
12532     /**
12533      * Sorts this collection with the passed comparison function
12534      * @param {String} direction (optional) "ASC" or "DESC"
12535      * @param {Function} fn (optional) comparison function
12536      */
12537     sort : function(dir, fn){
12538         this._sort("value", dir, fn);
12539     },
12540     
12541     /**
12542      * Sorts this collection by keys
12543      * @param {String} direction (optional) "ASC" or "DESC"
12544      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12545      */
12546     keySort : function(dir, fn){
12547         this._sort("key", dir, fn || function(a, b){
12548             return String(a).toUpperCase()-String(b).toUpperCase();
12549         });
12550     },
12551     
12552     /**
12553      * Returns a range of items in this collection
12554      * @param {Number} startIndex (optional) defaults to 0
12555      * @param {Number} endIndex (optional) default to the last item
12556      * @return {Array} An array of items
12557      */
12558     getRange : function(start, end){
12559         var items = this.items;
12560         if(items.length < 1){
12561             return [];
12562         }
12563         start = start || 0;
12564         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12565         var r = [];
12566         if(start <= end){
12567             for(var i = start; i <= end; i++) {
12568                     r[r.length] = items[i];
12569             }
12570         }else{
12571             for(var i = start; i >= end; i--) {
12572                     r[r.length] = items[i];
12573             }
12574         }
12575         return r;
12576     },
12577         
12578     /**
12579      * Filter the <i>objects</i> in this collection by a specific property. 
12580      * Returns a new collection that has been filtered.
12581      * @param {String} property A property on your objects
12582      * @param {String/RegExp} value Either string that the property values 
12583      * should start with or a RegExp to test against the property
12584      * @return {MixedCollection} The new filtered collection
12585      */
12586     filter : function(property, value){
12587         if(!value.exec){ // not a regex
12588             value = String(value);
12589             if(value.length == 0){
12590                 return this.clone();
12591             }
12592             value = new RegExp("^" + Roo.escapeRe(value), "i");
12593         }
12594         return this.filterBy(function(o){
12595             return o && value.test(o[property]);
12596         });
12597         },
12598     
12599     /**
12600      * Filter by a function. * Returns a new collection that has been filtered.
12601      * The passed function will be called with each 
12602      * object in the collection. If the function returns true, the value is included 
12603      * otherwise it is filtered.
12604      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12605      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12606      * @return {MixedCollection} The new filtered collection
12607      */
12608     filterBy : function(fn, scope){
12609         var r = new Roo.util.MixedCollection();
12610         r.getKey = this.getKey;
12611         var k = this.keys, it = this.items;
12612         for(var i = 0, len = it.length; i < len; i++){
12613             if(fn.call(scope||this, it[i], k[i])){
12614                                 r.add(k[i], it[i]);
12615                         }
12616         }
12617         return r;
12618     },
12619     
12620     /**
12621      * Creates a duplicate of this collection
12622      * @return {MixedCollection}
12623      */
12624     clone : function(){
12625         var r = new Roo.util.MixedCollection();
12626         var k = this.keys, it = this.items;
12627         for(var i = 0, len = it.length; i < len; i++){
12628             r.add(k[i], it[i]);
12629         }
12630         r.getKey = this.getKey;
12631         return r;
12632     }
12633 });
12634 /**
12635  * Returns the item associated with the passed key or index.
12636  * @method
12637  * @param {String/Number} key The key or index of the item.
12638  * @return {Object} The item associated with the passed key.
12639  */
12640 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12641  * Based on:
12642  * Ext JS Library 1.1.1
12643  * Copyright(c) 2006-2007, Ext JS, LLC.
12644  *
12645  * Originally Released Under LGPL - original licence link has changed is not relivant.
12646  *
12647  * Fork - LGPL
12648  * <script type="text/javascript">
12649  */
12650 /**
12651  * @class Roo.util.JSON
12652  * Modified version of Douglas Crockford"s json.js that doesn"t
12653  * mess with the Object prototype 
12654  * http://www.json.org/js.html
12655  * @singleton
12656  */
12657 Roo.util.JSON = new (function(){
12658     var useHasOwn = {}.hasOwnProperty ? true : false;
12659     
12660     // crashes Safari in some instances
12661     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12662     
12663     var pad = function(n) {
12664         return n < 10 ? "0" + n : n;
12665     };
12666     
12667     var m = {
12668         "\b": '\\b',
12669         "\t": '\\t',
12670         "\n": '\\n',
12671         "\f": '\\f',
12672         "\r": '\\r',
12673         '"' : '\\"',
12674         "\\": '\\\\'
12675     };
12676
12677     var encodeString = function(s){
12678         if (/["\\\x00-\x1f]/.test(s)) {
12679             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12680                 var c = m[b];
12681                 if(c){
12682                     return c;
12683                 }
12684                 c = b.charCodeAt();
12685                 return "\\u00" +
12686                     Math.floor(c / 16).toString(16) +
12687                     (c % 16).toString(16);
12688             }) + '"';
12689         }
12690         return '"' + s + '"';
12691     };
12692     
12693     var encodeArray = function(o){
12694         var a = ["["], b, i, l = o.length, v;
12695             for (i = 0; i < l; i += 1) {
12696                 v = o[i];
12697                 switch (typeof v) {
12698                     case "undefined":
12699                     case "function":
12700                     case "unknown":
12701                         break;
12702                     default:
12703                         if (b) {
12704                             a.push(',');
12705                         }
12706                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12707                         b = true;
12708                 }
12709             }
12710             a.push("]");
12711             return a.join("");
12712     };
12713     
12714     var encodeDate = function(o){
12715         return '"' + o.getFullYear() + "-" +
12716                 pad(o.getMonth() + 1) + "-" +
12717                 pad(o.getDate()) + "T" +
12718                 pad(o.getHours()) + ":" +
12719                 pad(o.getMinutes()) + ":" +
12720                 pad(o.getSeconds()) + '"';
12721     };
12722     
12723     /**
12724      * Encodes an Object, Array or other value
12725      * @param {Mixed} o The variable to encode
12726      * @return {String} The JSON string
12727      */
12728     this.encode = function(o){
12729         if(typeof o == "undefined" || o === null){
12730             return "null";
12731         }else if(o instanceof Array){
12732             return encodeArray(o);
12733         }else if(o instanceof Date){
12734             return encodeDate(o);
12735         }else if(typeof o == "string"){
12736             return encodeString(o);
12737         }else if(typeof o == "number"){
12738             return isFinite(o) ? String(o) : "null";
12739         }else if(typeof o == "boolean"){
12740             return String(o);
12741         }else {
12742             var a = ["{"], b, i, v;
12743             for (i in o) {
12744                 if(!useHasOwn || o.hasOwnProperty(i)) {
12745                     v = o[i];
12746                     switch (typeof v) {
12747                     case "undefined":
12748                     case "function":
12749                     case "unknown":
12750                         break;
12751                     default:
12752                         if(b){
12753                             a.push(',');
12754                         }
12755                         a.push(this.encode(i), ":",
12756                                 v === null ? "null" : this.encode(v));
12757                         b = true;
12758                     }
12759                 }
12760             }
12761             a.push("}");
12762             return a.join("");
12763         }
12764     };
12765     
12766     /**
12767      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12768      * @param {String} json The JSON string
12769      * @return {Object} The resulting object
12770      */
12771     this.decode = function(json){
12772         /**
12773          * eval:var:json
12774          */
12775         return eval("(" + json + ')');
12776     };
12777 })();
12778 /** 
12779  * Shorthand for {@link Roo.util.JSON#encode}
12780  * @member Roo encode 
12781  * @method */
12782 Roo.encode = Roo.util.JSON.encode;
12783 /** 
12784  * Shorthand for {@link Roo.util.JSON#decode}
12785  * @member Roo decode 
12786  * @method */
12787 Roo.decode = Roo.util.JSON.decode;
12788 /*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798  
12799 /**
12800  * @class Roo.util.Format
12801  * Reusable data formatting functions
12802  * @singleton
12803  */
12804 Roo.util.Format = function(){
12805     var trimRe = /^\s+|\s+$/g;
12806     return {
12807         /**
12808          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12809          * @param {String} value The string to truncate
12810          * @param {Number} length The maximum length to allow before truncating
12811          * @return {String} The converted text
12812          */
12813         ellipsis : function(value, len){
12814             if(value && value.length > len){
12815                 return value.substr(0, len-3)+"...";
12816             }
12817             return value;
12818         },
12819
12820         /**
12821          * Checks a reference and converts it to empty string if it is undefined
12822          * @param {Mixed} value Reference to check
12823          * @return {Mixed} Empty string if converted, otherwise the original value
12824          */
12825         undef : function(value){
12826             return typeof value != "undefined" ? value : "";
12827         },
12828
12829         /**
12830          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12831          * @param {String} value The string to encode
12832          * @return {String} The encoded text
12833          */
12834         htmlEncode : function(value){
12835             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12836         },
12837
12838         /**
12839          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12840          * @param {String} value The string to decode
12841          * @return {String} The decoded text
12842          */
12843         htmlDecode : function(value){
12844             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12845         },
12846
12847         /**
12848          * Trims any whitespace from either side of a string
12849          * @param {String} value The text to trim
12850          * @return {String} The trimmed text
12851          */
12852         trim : function(value){
12853             return String(value).replace(trimRe, "");
12854         },
12855
12856         /**
12857          * Returns a substring from within an original string
12858          * @param {String} value The original text
12859          * @param {Number} start The start index of the substring
12860          * @param {Number} length The length of the substring
12861          * @return {String} The substring
12862          */
12863         substr : function(value, start, length){
12864             return String(value).substr(start, length);
12865         },
12866
12867         /**
12868          * Converts a string to all lower case letters
12869          * @param {String} value The text to convert
12870          * @return {String} The converted text
12871          */
12872         lowercase : function(value){
12873             return String(value).toLowerCase();
12874         },
12875
12876         /**
12877          * Converts a string to all upper case letters
12878          * @param {String} value The text to convert
12879          * @return {String} The converted text
12880          */
12881         uppercase : function(value){
12882             return String(value).toUpperCase();
12883         },
12884
12885         /**
12886          * Converts the first character only of a string to upper case
12887          * @param {String} value The text to convert
12888          * @return {String} The converted text
12889          */
12890         capitalize : function(value){
12891             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12892         },
12893
12894         // private
12895         call : function(value, fn){
12896             if(arguments.length > 2){
12897                 var args = Array.prototype.slice.call(arguments, 2);
12898                 args.unshift(value);
12899                  
12900                 return /** eval:var:value */  eval(fn).apply(window, args);
12901             }else{
12902                 /** eval:var:value */
12903                 return /** eval:var:value */ eval(fn).call(window, value);
12904             }
12905         },
12906
12907         /**
12908          * Format a number as US currency
12909          * @param {Number/String} value The numeric value to format
12910          * @return {String} The formatted currency string
12911          */
12912         usMoney : function(v){
12913             v = (Math.round((v-0)*100))/100;
12914             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12915             v = String(v);
12916             var ps = v.split('.');
12917             var whole = ps[0];
12918             var sub = ps[1] ? '.'+ ps[1] : '.00';
12919             var r = /(\d+)(\d{3})/;
12920             while (r.test(whole)) {
12921                 whole = whole.replace(r, '$1' + ',' + '$2');
12922             }
12923             return "$" + whole + sub ;
12924         },
12925
12926         /**
12927          * Parse a value into a formatted date using the specified format pattern.
12928          * @param {Mixed} value The value to format
12929          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12930          * @return {String} The formatted date string
12931          */
12932         date : function(v, format){
12933             if(!v){
12934                 return "";
12935             }
12936             if(!(v instanceof Date)){
12937                 v = new Date(Date.parse(v));
12938             }
12939             return v.dateFormat(format || "m/d/Y");
12940         },
12941
12942         /**
12943          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12944          * @param {String} format Any valid date format string
12945          * @return {Function} The date formatting function
12946          */
12947         dateRenderer : function(format){
12948             return function(v){
12949                 return Roo.util.Format.date(v, format);  
12950             };
12951         },
12952
12953         // private
12954         stripTagsRE : /<\/?[^>]+>/gi,
12955         
12956         /**
12957          * Strips all HTML tags
12958          * @param {Mixed} value The text from which to strip tags
12959          * @return {String} The stripped text
12960          */
12961         stripTags : function(v){
12962             return !v ? v : String(v).replace(this.stripTagsRE, "");
12963         }
12964     };
12965 }();/*
12966  * Based on:
12967  * Ext JS Library 1.1.1
12968  * Copyright(c) 2006-2007, Ext JS, LLC.
12969  *
12970  * Originally Released Under LGPL - original licence link has changed is not relivant.
12971  *
12972  * Fork - LGPL
12973  * <script type="text/javascript">
12974  */
12975
12976
12977  
12978
12979 /**
12980  * @class Roo.MasterTemplate
12981  * @extends Roo.Template
12982  * Provides a template that can have child templates. The syntax is:
12983 <pre><code>
12984 var t = new Roo.MasterTemplate(
12985         '&lt;select name="{name}"&gt;',
12986                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12987         '&lt;/select&gt;'
12988 );
12989 t.add('options', {value: 'foo', text: 'bar'});
12990 // or you can add multiple child elements in one shot
12991 t.addAll('options', [
12992     {value: 'foo', text: 'bar'},
12993     {value: 'foo2', text: 'bar2'},
12994     {value: 'foo3', text: 'bar3'}
12995 ]);
12996 // then append, applying the master template values
12997 t.append('my-form', {name: 'my-select'});
12998 </code></pre>
12999 * A name attribute for the child template is not required if you have only one child
13000 * template or you want to refer to them by index.
13001  */
13002 Roo.MasterTemplate = function(){
13003     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13004     this.originalHtml = this.html;
13005     var st = {};
13006     var m, re = this.subTemplateRe;
13007     re.lastIndex = 0;
13008     var subIndex = 0;
13009     while(m = re.exec(this.html)){
13010         var name = m[1], content = m[2];
13011         st[subIndex] = {
13012             name: name,
13013             index: subIndex,
13014             buffer: [],
13015             tpl : new Roo.Template(content)
13016         };
13017         if(name){
13018             st[name] = st[subIndex];
13019         }
13020         st[subIndex].tpl.compile();
13021         st[subIndex].tpl.call = this.call.createDelegate(this);
13022         subIndex++;
13023     }
13024     this.subCount = subIndex;
13025     this.subs = st;
13026 };
13027 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13028     /**
13029     * The regular expression used to match sub templates
13030     * @type RegExp
13031     * @property
13032     */
13033     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13034
13035     /**
13036      * Applies the passed values to a child template.
13037      * @param {String/Number} name (optional) The name or index of the child template
13038      * @param {Array/Object} values The values to be applied to the template
13039      * @return {MasterTemplate} this
13040      */
13041      add : function(name, values){
13042         if(arguments.length == 1){
13043             values = arguments[0];
13044             name = 0;
13045         }
13046         var s = this.subs[name];
13047         s.buffer[s.buffer.length] = s.tpl.apply(values);
13048         return this;
13049     },
13050
13051     /**
13052      * Applies all the passed values to a child template.
13053      * @param {String/Number} name (optional) The name or index of the child template
13054      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13055      * @param {Boolean} reset (optional) True to reset the template first
13056      * @return {MasterTemplate} this
13057      */
13058     fill : function(name, values, reset){
13059         var a = arguments;
13060         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13061             values = a[0];
13062             name = 0;
13063             reset = a[1];
13064         }
13065         if(reset){
13066             this.reset();
13067         }
13068         for(var i = 0, len = values.length; i < len; i++){
13069             this.add(name, values[i]);
13070         }
13071         return this;
13072     },
13073
13074     /**
13075      * Resets the template for reuse
13076      * @return {MasterTemplate} this
13077      */
13078      reset : function(){
13079         var s = this.subs;
13080         for(var i = 0; i < this.subCount; i++){
13081             s[i].buffer = [];
13082         }
13083         return this;
13084     },
13085
13086     applyTemplate : function(values){
13087         var s = this.subs;
13088         var replaceIndex = -1;
13089         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13090             return s[++replaceIndex].buffer.join("");
13091         });
13092         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13093     },
13094
13095     apply : function(){
13096         return this.applyTemplate.apply(this, arguments);
13097     },
13098
13099     compile : function(){return this;}
13100 });
13101
13102 /**
13103  * Alias for fill().
13104  * @method
13105  */
13106 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13107  /**
13108  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13109  * var tpl = Roo.MasterTemplate.from('element-id');
13110  * @param {String/HTMLElement} el
13111  * @param {Object} config
13112  * @static
13113  */
13114 Roo.MasterTemplate.from = function(el, config){
13115     el = Roo.getDom(el);
13116     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13117 };/*
13118  * Based on:
13119  * Ext JS Library 1.1.1
13120  * Copyright(c) 2006-2007, Ext JS, LLC.
13121  *
13122  * Originally Released Under LGPL - original licence link has changed is not relivant.
13123  *
13124  * Fork - LGPL
13125  * <script type="text/javascript">
13126  */
13127
13128  
13129 /**
13130  * @class Roo.util.CSS
13131  * Utility class for manipulating CSS rules
13132  * @singleton
13133  */
13134 Roo.util.CSS = function(){
13135         var rules = null;
13136         var doc = document;
13137
13138     var camelRe = /(-[a-z])/gi;
13139     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13140
13141    return {
13142    /**
13143     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13144     * tag and appended to the HEAD of the document.
13145     * @param {String} cssText The text containing the css rules
13146     * @param {String} id An id to add to the stylesheet for later removal
13147     * @return {StyleSheet}
13148     */
13149    createStyleSheet : function(cssText, id){
13150        var ss;
13151        var head = doc.getElementsByTagName("head")[0];
13152        var rules = doc.createElement("style");
13153        rules.setAttribute("type", "text/css");
13154        if(id){
13155            rules.setAttribute("id", id);
13156        }
13157        if(Roo.isIE){
13158            head.appendChild(rules);
13159            ss = rules.styleSheet;
13160            ss.cssText = cssText;
13161        }else{
13162            try{
13163                 rules.appendChild(doc.createTextNode(cssText));
13164            }catch(e){
13165                rules.cssText = cssText; 
13166            }
13167            head.appendChild(rules);
13168            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13169        }
13170        this.cacheStyleSheet(ss);
13171        return ss;
13172    },
13173
13174    /**
13175     * Removes a style or link tag by id
13176     * @param {String} id The id of the tag
13177     */
13178    removeStyleSheet : function(id){
13179        var existing = doc.getElementById(id);
13180        if(existing){
13181            existing.parentNode.removeChild(existing);
13182        }
13183    },
13184
13185    /**
13186     * Dynamically swaps an existing stylesheet reference for a new one
13187     * @param {String} id The id of an existing link tag to remove
13188     * @param {String} url The href of the new stylesheet to include
13189     */
13190    swapStyleSheet : function(id, url){
13191        this.removeStyleSheet(id);
13192        var ss = doc.createElement("link");
13193        ss.setAttribute("rel", "stylesheet");
13194        ss.setAttribute("type", "text/css");
13195        ss.setAttribute("id", id);
13196        ss.setAttribute("href", url);
13197        doc.getElementsByTagName("head")[0].appendChild(ss);
13198    },
13199    
13200    /**
13201     * Refresh the rule cache if you have dynamically added stylesheets
13202     * @return {Object} An object (hash) of rules indexed by selector
13203     */
13204    refreshCache : function(){
13205        return this.getRules(true);
13206    },
13207
13208    // private
13209    cacheStyleSheet : function(ss){
13210        if(!rules){
13211            rules = {};
13212        }
13213        try{// try catch for cross domain access issue
13214            var ssRules = ss.cssRules || ss.rules;
13215            for(var j = ssRules.length-1; j >= 0; --j){
13216                rules[ssRules[j].selectorText] = ssRules[j];
13217            }
13218        }catch(e){}
13219    },
13220    
13221    /**
13222     * Gets all css rules for the document
13223     * @param {Boolean} refreshCache true to refresh the internal cache
13224     * @return {Object} An object (hash) of rules indexed by selector
13225     */
13226    getRules : function(refreshCache){
13227                 if(rules == null || refreshCache){
13228                         rules = {};
13229                         var ds = doc.styleSheets;
13230                         for(var i =0, len = ds.length; i < len; i++){
13231                             try{
13232                         this.cacheStyleSheet(ds[i]);
13233                     }catch(e){} 
13234                 }
13235                 }
13236                 return rules;
13237         },
13238         
13239         /**
13240     * Gets an an individual CSS rule by selector(s)
13241     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13242     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13243     * @return {CSSRule} The CSS rule or null if one is not found
13244     */
13245    getRule : function(selector, refreshCache){
13246                 var rs = this.getRules(refreshCache);
13247                 if(!(selector instanceof Array)){
13248                     return rs[selector];
13249                 }
13250                 for(var i = 0; i < selector.length; i++){
13251                         if(rs[selector[i]]){
13252                                 return rs[selector[i]];
13253                         }
13254                 }
13255                 return null;
13256         },
13257         
13258         
13259         /**
13260     * Updates a rule property
13261     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13262     * @param {String} property The css property
13263     * @param {String} value The new value for the property
13264     * @return {Boolean} true If a rule was found and updated
13265     */
13266    updateRule : function(selector, property, value){
13267                 if(!(selector instanceof Array)){
13268                         var rule = this.getRule(selector);
13269                         if(rule){
13270                                 rule.style[property.replace(camelRe, camelFn)] = value;
13271                                 return true;
13272                         }
13273                 }else{
13274                         for(var i = 0; i < selector.length; i++){
13275                                 if(this.updateRule(selector[i], property, value)){
13276                                         return true;
13277                                 }
13278                         }
13279                 }
13280                 return false;
13281         }
13282    };   
13283 }();/*
13284  * Based on:
13285  * Ext JS Library 1.1.1
13286  * Copyright(c) 2006-2007, Ext JS, LLC.
13287  *
13288  * Originally Released Under LGPL - original licence link has changed is not relivant.
13289  *
13290  * Fork - LGPL
13291  * <script type="text/javascript">
13292  */
13293
13294  
13295
13296 /**
13297  * @class Roo.util.ClickRepeater
13298  * @extends Roo.util.Observable
13299  * 
13300  * A wrapper class which can be applied to any element. Fires a "click" event while the
13301  * mouse is pressed. The interval between firings may be specified in the config but
13302  * defaults to 10 milliseconds.
13303  * 
13304  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13305  * 
13306  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13307  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13308  * Similar to an autorepeat key delay.
13309  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13310  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13311  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13312  *           "interval" and "delay" are ignored. "immediate" is honored.
13313  * @cfg {Boolean} preventDefault True to prevent the default click event
13314  * @cfg {Boolean} stopDefault True to stop the default click event
13315  * 
13316  * @history
13317  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13318  *     2007-02-02 jvs Renamed to ClickRepeater
13319  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13320  *
13321  *  @constructor
13322  * @param {String/HTMLElement/Element} el The element to listen on
13323  * @param {Object} config
13324  **/
13325 Roo.util.ClickRepeater = function(el, config)
13326 {
13327     this.el = Roo.get(el);
13328     this.el.unselectable();
13329
13330     Roo.apply(this, config);
13331
13332     this.addEvents({
13333     /**
13334      * @event mousedown
13335      * Fires when the mouse button is depressed.
13336      * @param {Roo.util.ClickRepeater} this
13337      */
13338         "mousedown" : true,
13339     /**
13340      * @event click
13341      * Fires on a specified interval during the time the element is pressed.
13342      * @param {Roo.util.ClickRepeater} this
13343      */
13344         "click" : true,
13345     /**
13346      * @event mouseup
13347      * Fires when the mouse key is released.
13348      * @param {Roo.util.ClickRepeater} this
13349      */
13350         "mouseup" : true
13351     });
13352
13353     this.el.on("mousedown", this.handleMouseDown, this);
13354     if(this.preventDefault || this.stopDefault){
13355         this.el.on("click", function(e){
13356             if(this.preventDefault){
13357                 e.preventDefault();
13358             }
13359             if(this.stopDefault){
13360                 e.stopEvent();
13361             }
13362         }, this);
13363     }
13364
13365     // allow inline handler
13366     if(this.handler){
13367         this.on("click", this.handler,  this.scope || this);
13368     }
13369
13370     Roo.util.ClickRepeater.superclass.constructor.call(this);
13371 };
13372
13373 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13374     interval : 20,
13375     delay: 250,
13376     preventDefault : true,
13377     stopDefault : false,
13378     timer : 0,
13379
13380     // private
13381     handleMouseDown : function(){
13382         clearTimeout(this.timer);
13383         this.el.blur();
13384         if(this.pressClass){
13385             this.el.addClass(this.pressClass);
13386         }
13387         this.mousedownTime = new Date();
13388
13389         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13390         this.el.on("mouseout", this.handleMouseOut, this);
13391
13392         this.fireEvent("mousedown", this);
13393         this.fireEvent("click", this);
13394         
13395         this.timer = this.click.defer(this.delay || this.interval, this);
13396     },
13397
13398     // private
13399     click : function(){
13400         this.fireEvent("click", this);
13401         this.timer = this.click.defer(this.getInterval(), this);
13402     },
13403
13404     // private
13405     getInterval: function(){
13406         if(!this.accelerate){
13407             return this.interval;
13408         }
13409         var pressTime = this.mousedownTime.getElapsed();
13410         if(pressTime < 500){
13411             return 400;
13412         }else if(pressTime < 1700){
13413             return 320;
13414         }else if(pressTime < 2600){
13415             return 250;
13416         }else if(pressTime < 3500){
13417             return 180;
13418         }else if(pressTime < 4400){
13419             return 140;
13420         }else if(pressTime < 5300){
13421             return 80;
13422         }else if(pressTime < 6200){
13423             return 50;
13424         }else{
13425             return 10;
13426         }
13427     },
13428
13429     // private
13430     handleMouseOut : function(){
13431         clearTimeout(this.timer);
13432         if(this.pressClass){
13433             this.el.removeClass(this.pressClass);
13434         }
13435         this.el.on("mouseover", this.handleMouseReturn, this);
13436     },
13437
13438     // private
13439     handleMouseReturn : function(){
13440         this.el.un("mouseover", this.handleMouseReturn);
13441         if(this.pressClass){
13442             this.el.addClass(this.pressClass);
13443         }
13444         this.click();
13445     },
13446
13447     // private
13448     handleMouseUp : function(){
13449         clearTimeout(this.timer);
13450         this.el.un("mouseover", this.handleMouseReturn);
13451         this.el.un("mouseout", this.handleMouseOut);
13452         Roo.get(document).un("mouseup", this.handleMouseUp);
13453         this.el.removeClass(this.pressClass);
13454         this.fireEvent("mouseup", this);
13455     }
13456 });/*
13457  * Based on:
13458  * Ext JS Library 1.1.1
13459  * Copyright(c) 2006-2007, Ext JS, LLC.
13460  *
13461  * Originally Released Under LGPL - original licence link has changed is not relivant.
13462  *
13463  * Fork - LGPL
13464  * <script type="text/javascript">
13465  */
13466
13467  
13468 /**
13469  * @class Roo.KeyNav
13470  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13471  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13472  * way to implement custom navigation schemes for any UI component.</p>
13473  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13474  * pageUp, pageDown, del, home, end.  Usage:</p>
13475  <pre><code>
13476 var nav = new Roo.KeyNav("my-element", {
13477     "left" : function(e){
13478         this.moveLeft(e.ctrlKey);
13479     },
13480     "right" : function(e){
13481         this.moveRight(e.ctrlKey);
13482     },
13483     "enter" : function(e){
13484         this.save();
13485     },
13486     scope : this
13487 });
13488 </code></pre>
13489  * @constructor
13490  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13491  * @param {Object} config The config
13492  */
13493 Roo.KeyNav = function(el, config){
13494     this.el = Roo.get(el);
13495     Roo.apply(this, config);
13496     if(!this.disabled){
13497         this.disabled = true;
13498         this.enable();
13499     }
13500 };
13501
13502 Roo.KeyNav.prototype = {
13503     /**
13504      * @cfg {Boolean} disabled
13505      * True to disable this KeyNav instance (defaults to false)
13506      */
13507     disabled : false,
13508     /**
13509      * @cfg {String} defaultEventAction
13510      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13511      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13512      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13513      */
13514     defaultEventAction: "stopEvent",
13515     /**
13516      * @cfg {Boolean} forceKeyDown
13517      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13518      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13519      * handle keydown instead of keypress.
13520      */
13521     forceKeyDown : false,
13522
13523     // private
13524     prepareEvent : function(e){
13525         var k = e.getKey();
13526         var h = this.keyToHandler[k];
13527         //if(h && this[h]){
13528         //    e.stopPropagation();
13529         //}
13530         if(Roo.isSafari && h && k >= 37 && k <= 40){
13531             e.stopEvent();
13532         }
13533     },
13534
13535     // private
13536     relay : function(e){
13537         var k = e.getKey();
13538         var h = this.keyToHandler[k];
13539         if(h && this[h]){
13540             if(this.doRelay(e, this[h], h) !== true){
13541                 e[this.defaultEventAction]();
13542             }
13543         }
13544     },
13545
13546     // private
13547     doRelay : function(e, h, hname){
13548         return h.call(this.scope || this, e);
13549     },
13550
13551     // possible handlers
13552     enter : false,
13553     left : false,
13554     right : false,
13555     up : false,
13556     down : false,
13557     tab : false,
13558     esc : false,
13559     pageUp : false,
13560     pageDown : false,
13561     del : false,
13562     home : false,
13563     end : false,
13564
13565     // quick lookup hash
13566     keyToHandler : {
13567         37 : "left",
13568         39 : "right",
13569         38 : "up",
13570         40 : "down",
13571         33 : "pageUp",
13572         34 : "pageDown",
13573         46 : "del",
13574         36 : "home",
13575         35 : "end",
13576         13 : "enter",
13577         27 : "esc",
13578         9  : "tab"
13579     },
13580
13581         /**
13582          * Enable this KeyNav
13583          */
13584         enable: function(){
13585                 if(this.disabled){
13586             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13587             // the EventObject will normalize Safari automatically
13588             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13589                 this.el.on("keydown", this.relay,  this);
13590             }else{
13591                 this.el.on("keydown", this.prepareEvent,  this);
13592                 this.el.on("keypress", this.relay,  this);
13593             }
13594                     this.disabled = false;
13595                 }
13596         },
13597
13598         /**
13599          * Disable this KeyNav
13600          */
13601         disable: function(){
13602                 if(!this.disabled){
13603                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13604                 this.el.un("keydown", this.relay);
13605             }else{
13606                 this.el.un("keydown", this.prepareEvent);
13607                 this.el.un("keypress", this.relay);
13608             }
13609                     this.disabled = true;
13610                 }
13611         }
13612 };/*
13613  * Based on:
13614  * Ext JS Library 1.1.1
13615  * Copyright(c) 2006-2007, Ext JS, LLC.
13616  *
13617  * Originally Released Under LGPL - original licence link has changed is not relivant.
13618  *
13619  * Fork - LGPL
13620  * <script type="text/javascript">
13621  */
13622
13623  
13624 /**
13625  * @class Roo.KeyMap
13626  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13627  * The constructor accepts the same config object as defined by {@link #addBinding}.
13628  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13629  * combination it will call the function with this signature (if the match is a multi-key
13630  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13631  * A KeyMap can also handle a string representation of keys.<br />
13632  * Usage:
13633  <pre><code>
13634 // map one key by key code
13635 var map = new Roo.KeyMap("my-element", {
13636     key: 13, // or Roo.EventObject.ENTER
13637     fn: myHandler,
13638     scope: myObject
13639 });
13640
13641 // map multiple keys to one action by string
13642 var map = new Roo.KeyMap("my-element", {
13643     key: "a\r\n\t",
13644     fn: myHandler,
13645     scope: myObject
13646 });
13647
13648 // map multiple keys to multiple actions by strings and array of codes
13649 var map = new Roo.KeyMap("my-element", [
13650     {
13651         key: [10,13],
13652         fn: function(){ alert("Return was pressed"); }
13653     }, {
13654         key: "abc",
13655         fn: function(){ alert('a, b or c was pressed'); }
13656     }, {
13657         key: "\t",
13658         ctrl:true,
13659         shift:true,
13660         fn: function(){ alert('Control + shift + tab was pressed.'); }
13661     }
13662 ]);
13663 </code></pre>
13664  * <b>Note: A KeyMap starts enabled</b>
13665  * @constructor
13666  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13667  * @param {Object} config The config (see {@link #addBinding})
13668  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13669  */
13670 Roo.KeyMap = function(el, config, eventName){
13671     this.el  = Roo.get(el);
13672     this.eventName = eventName || "keydown";
13673     this.bindings = [];
13674     if(config){
13675         this.addBinding(config);
13676     }
13677     this.enable();
13678 };
13679
13680 Roo.KeyMap.prototype = {
13681     /**
13682      * True to stop the event from bubbling and prevent the default browser action if the
13683      * key was handled by the KeyMap (defaults to false)
13684      * @type Boolean
13685      */
13686     stopEvent : false,
13687
13688     /**
13689      * Add a new binding to this KeyMap. The following config object properties are supported:
13690      * <pre>
13691 Property    Type             Description
13692 ----------  ---------------  ----------------------------------------------------------------------
13693 key         String/Array     A single keycode or an array of keycodes to handle
13694 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13695 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13696 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13697 fn          Function         The function to call when KeyMap finds the expected key combination
13698 scope       Object           The scope of the callback function
13699 </pre>
13700      *
13701      * Usage:
13702      * <pre><code>
13703 // Create a KeyMap
13704 var map = new Roo.KeyMap(document, {
13705     key: Roo.EventObject.ENTER,
13706     fn: handleKey,
13707     scope: this
13708 });
13709
13710 //Add a new binding to the existing KeyMap later
13711 map.addBinding({
13712     key: 'abc',
13713     shift: true,
13714     fn: handleKey,
13715     scope: this
13716 });
13717 </code></pre>
13718      * @param {Object/Array} config A single KeyMap config or an array of configs
13719      */
13720         addBinding : function(config){
13721         if(config instanceof Array){
13722             for(var i = 0, len = config.length; i < len; i++){
13723                 this.addBinding(config[i]);
13724             }
13725             return;
13726         }
13727         var keyCode = config.key,
13728             shift = config.shift, 
13729             ctrl = config.ctrl, 
13730             alt = config.alt,
13731             fn = config.fn,
13732             scope = config.scope;
13733         if(typeof keyCode == "string"){
13734             var ks = [];
13735             var keyString = keyCode.toUpperCase();
13736             for(var j = 0, len = keyString.length; j < len; j++){
13737                 ks.push(keyString.charCodeAt(j));
13738             }
13739             keyCode = ks;
13740         }
13741         var keyArray = keyCode instanceof Array;
13742         var handler = function(e){
13743             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13744                 var k = e.getKey();
13745                 if(keyArray){
13746                     for(var i = 0, len = keyCode.length; i < len; i++){
13747                         if(keyCode[i] == k){
13748                           if(this.stopEvent){
13749                               e.stopEvent();
13750                           }
13751                           fn.call(scope || window, k, e);
13752                           return;
13753                         }
13754                     }
13755                 }else{
13756                     if(k == keyCode){
13757                         if(this.stopEvent){
13758                            e.stopEvent();
13759                         }
13760                         fn.call(scope || window, k, e);
13761                     }
13762                 }
13763             }
13764         };
13765         this.bindings.push(handler);  
13766         },
13767
13768     /**
13769      * Shorthand for adding a single key listener
13770      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13771      * following options:
13772      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13773      * @param {Function} fn The function to call
13774      * @param {Object} scope (optional) The scope of the function
13775      */
13776     on : function(key, fn, scope){
13777         var keyCode, shift, ctrl, alt;
13778         if(typeof key == "object" && !(key instanceof Array)){
13779             keyCode = key.key;
13780             shift = key.shift;
13781             ctrl = key.ctrl;
13782             alt = key.alt;
13783         }else{
13784             keyCode = key;
13785         }
13786         this.addBinding({
13787             key: keyCode,
13788             shift: shift,
13789             ctrl: ctrl,
13790             alt: alt,
13791             fn: fn,
13792             scope: scope
13793         })
13794     },
13795
13796     // private
13797     handleKeyDown : function(e){
13798             if(this.enabled){ //just in case
13799             var b = this.bindings;
13800             for(var i = 0, len = b.length; i < len; i++){
13801                 b[i].call(this, e);
13802             }
13803             }
13804         },
13805         
13806         /**
13807          * Returns true if this KeyMap is enabled
13808          * @return {Boolean} 
13809          */
13810         isEnabled : function(){
13811             return this.enabled;  
13812         },
13813         
13814         /**
13815          * Enables this KeyMap
13816          */
13817         enable: function(){
13818                 if(!this.enabled){
13819                     this.el.on(this.eventName, this.handleKeyDown, this);
13820                     this.enabled = true;
13821                 }
13822         },
13823
13824         /**
13825          * Disable this KeyMap
13826          */
13827         disable: function(){
13828                 if(this.enabled){
13829                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13830                     this.enabled = false;
13831                 }
13832         }
13833 };/*
13834  * Based on:
13835  * Ext JS Library 1.1.1
13836  * Copyright(c) 2006-2007, Ext JS, LLC.
13837  *
13838  * Originally Released Under LGPL - original licence link has changed is not relivant.
13839  *
13840  * Fork - LGPL
13841  * <script type="text/javascript">
13842  */
13843
13844  
13845 /**
13846  * @class Roo.util.TextMetrics
13847  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13848  * wide, in pixels, a given block of text will be.
13849  * @singleton
13850  */
13851 Roo.util.TextMetrics = function(){
13852     var shared;
13853     return {
13854         /**
13855          * Measures the size of the specified text
13856          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13857          * that can affect the size of the rendered text
13858          * @param {String} text The text to measure
13859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13860          * in order to accurately measure the text height
13861          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13862          */
13863         measure : function(el, text, fixedWidth){
13864             if(!shared){
13865                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13866             }
13867             shared.bind(el);
13868             shared.setFixedWidth(fixedWidth || 'auto');
13869             return shared.getSize(text);
13870         },
13871
13872         /**
13873          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13874          * the overhead of multiple calls to initialize the style properties on each measurement.
13875          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13876          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13877          * in order to accurately measure the text height
13878          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13879          */
13880         createInstance : function(el, fixedWidth){
13881             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13882         }
13883     };
13884 }();
13885
13886  
13887
13888 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13889     var ml = new Roo.Element(document.createElement('div'));
13890     document.body.appendChild(ml.dom);
13891     ml.position('absolute');
13892     ml.setLeftTop(-1000, -1000);
13893     ml.hide();
13894
13895     if(fixedWidth){
13896         ml.setWidth(fixedWidth);
13897     }
13898      
13899     var instance = {
13900         /**
13901          * Returns the size of the specified text based on the internal element's style and width properties
13902          * @memberOf Roo.util.TextMetrics.Instance#
13903          * @param {String} text The text to measure
13904          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13905          */
13906         getSize : function(text){
13907             ml.update(text);
13908             var s = ml.getSize();
13909             ml.update('');
13910             return s;
13911         },
13912
13913         /**
13914          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13915          * that can affect the size of the rendered text
13916          * @memberOf Roo.util.TextMetrics.Instance#
13917          * @param {String/HTMLElement} el The element, dom node or id
13918          */
13919         bind : function(el){
13920             ml.setStyle(
13921                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13922             );
13923         },
13924
13925         /**
13926          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13927          * to set a fixed width in order to accurately measure the text height.
13928          * @memberOf Roo.util.TextMetrics.Instance#
13929          * @param {Number} width The width to set on the element
13930          */
13931         setFixedWidth : function(width){
13932             ml.setWidth(width);
13933         },
13934
13935         /**
13936          * Returns the measured width of the specified text
13937          * @memberOf Roo.util.TextMetrics.Instance#
13938          * @param {String} text The text to measure
13939          * @return {Number} width The width in pixels
13940          */
13941         getWidth : function(text){
13942             ml.dom.style.width = 'auto';
13943             return this.getSize(text).width;
13944         },
13945
13946         /**
13947          * Returns the measured height of the specified text.  For multiline text, be sure to call
13948          * {@link #setFixedWidth} if necessary.
13949          * @memberOf Roo.util.TextMetrics.Instance#
13950          * @param {String} text The text to measure
13951          * @return {Number} height The height in pixels
13952          */
13953         getHeight : function(text){
13954             return this.getSize(text).height;
13955         }
13956     };
13957
13958     instance.bind(bindTo);
13959
13960     return instance;
13961 };
13962
13963 // backwards compat
13964 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975 /**
13976  * @class Roo.state.Provider
13977  * Abstract base class for state provider implementations. This class provides methods
13978  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13979  * Provider interface.
13980  */
13981 Roo.state.Provider = function(){
13982     /**
13983      * @event statechange
13984      * Fires when a state change occurs.
13985      * @param {Provider} this This state provider
13986      * @param {String} key The state key which was changed
13987      * @param {String} value The encoded value for the state
13988      */
13989     this.addEvents({
13990         "statechange": true
13991     });
13992     this.state = {};
13993     Roo.state.Provider.superclass.constructor.call(this);
13994 };
13995 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13996     /**
13997      * Returns the current value for a key
13998      * @param {String} name The key name
13999      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14000      * @return {Mixed} The state data
14001      */
14002     get : function(name, defaultValue){
14003         return typeof this.state[name] == "undefined" ?
14004             defaultValue : this.state[name];
14005     },
14006     
14007     /**
14008      * Clears a value from the state
14009      * @param {String} name The key name
14010      */
14011     clear : function(name){
14012         delete this.state[name];
14013         this.fireEvent("statechange", this, name, null);
14014     },
14015     
14016     /**
14017      * Sets the value for a key
14018      * @param {String} name The key name
14019      * @param {Mixed} value The value to set
14020      */
14021     set : function(name, value){
14022         this.state[name] = value;
14023         this.fireEvent("statechange", this, name, value);
14024     },
14025     
14026     /**
14027      * Decodes a string previously encoded with {@link #encodeValue}.
14028      * @param {String} value The value to decode
14029      * @return {Mixed} The decoded value
14030      */
14031     decodeValue : function(cookie){
14032         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14033         var matches = re.exec(unescape(cookie));
14034         if(!matches || !matches[1]) return; // non state cookie
14035         var type = matches[1];
14036         var v = matches[2];
14037         switch(type){
14038             case "n":
14039                 return parseFloat(v);
14040             case "d":
14041                 return new Date(Date.parse(v));
14042             case "b":
14043                 return (v == "1");
14044             case "a":
14045                 var all = [];
14046                 var values = v.split("^");
14047                 for(var i = 0, len = values.length; i < len; i++){
14048                     all.push(this.decodeValue(values[i]));
14049                 }
14050                 return all;
14051            case "o":
14052                 var all = {};
14053                 var values = v.split("^");
14054                 for(var i = 0, len = values.length; i < len; i++){
14055                     var kv = values[i].split("=");
14056                     all[kv[0]] = this.decodeValue(kv[1]);
14057                 }
14058                 return all;
14059            default:
14060                 return v;
14061         }
14062     },
14063     
14064     /**
14065      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14066      * @param {Mixed} value The value to encode
14067      * @return {String} The encoded value
14068      */
14069     encodeValue : function(v){
14070         var enc;
14071         if(typeof v == "number"){
14072             enc = "n:" + v;
14073         }else if(typeof v == "boolean"){
14074             enc = "b:" + (v ? "1" : "0");
14075         }else if(v instanceof Date){
14076             enc = "d:" + v.toGMTString();
14077         }else if(v instanceof Array){
14078             var flat = "";
14079             for(var i = 0, len = v.length; i < len; i++){
14080                 flat += this.encodeValue(v[i]);
14081                 if(i != len-1) flat += "^";
14082             }
14083             enc = "a:" + flat;
14084         }else if(typeof v == "object"){
14085             var flat = "";
14086             for(var key in v){
14087                 if(typeof v[key] != "function"){
14088                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14089                 }
14090             }
14091             enc = "o:" + flat.substring(0, flat.length-1);
14092         }else{
14093             enc = "s:" + v;
14094         }
14095         return escape(enc);        
14096     }
14097 });
14098
14099 /*
14100  * Based on:
14101  * Ext JS Library 1.1.1
14102  * Copyright(c) 2006-2007, Ext JS, LLC.
14103  *
14104  * Originally Released Under LGPL - original licence link has changed is not relivant.
14105  *
14106  * Fork - LGPL
14107  * <script type="text/javascript">
14108  */
14109 /**
14110  * @class Roo.state.Manager
14111  * This is the global state manager. By default all components that are "state aware" check this class
14112  * for state information if you don't pass them a custom state provider. In order for this class
14113  * to be useful, it must be initialized with a provider when your application initializes.
14114  <pre><code>
14115 // in your initialization function
14116 init : function(){
14117    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14118    ...
14119    // supposed you have a {@link Roo.BorderLayout}
14120    var layout = new Roo.BorderLayout(...);
14121    layout.restoreState();
14122    // or a {Roo.BasicDialog}
14123    var dialog = new Roo.BasicDialog(...);
14124    dialog.restoreState();
14125  </code></pre>
14126  * @singleton
14127  */
14128 Roo.state.Manager = function(){
14129     var provider = new Roo.state.Provider();
14130     
14131     return {
14132         /**
14133          * Configures the default state provider for your application
14134          * @param {Provider} stateProvider The state provider to set
14135          */
14136         setProvider : function(stateProvider){
14137             provider = stateProvider;
14138         },
14139         
14140         /**
14141          * Returns the current value for a key
14142          * @param {String} name The key name
14143          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14144          * @return {Mixed} The state data
14145          */
14146         get : function(key, defaultValue){
14147             return provider.get(key, defaultValue);
14148         },
14149         
14150         /**
14151          * Sets the value for a key
14152          * @param {String} name The key name
14153          * @param {Mixed} value The state data
14154          */
14155          set : function(key, value){
14156             provider.set(key, value);
14157         },
14158         
14159         /**
14160          * Clears a value from the state
14161          * @param {String} name The key name
14162          */
14163         clear : function(key){
14164             provider.clear(key);
14165         },
14166         
14167         /**
14168          * Gets the currently configured state provider
14169          * @return {Provider} The state provider
14170          */
14171         getProvider : function(){
14172             return provider;
14173         }
14174     };
14175 }();
14176 /*
14177  * Based on:
14178  * Ext JS Library 1.1.1
14179  * Copyright(c) 2006-2007, Ext JS, LLC.
14180  *
14181  * Originally Released Under LGPL - original licence link has changed is not relivant.
14182  *
14183  * Fork - LGPL
14184  * <script type="text/javascript">
14185  */
14186 /**
14187  * @class Roo.state.CookieProvider
14188  * @extends Roo.state.Provider
14189  * The default Provider implementation which saves state via cookies.
14190  * <br />Usage:
14191  <pre><code>
14192    var cp = new Roo.state.CookieProvider({
14193        path: "/cgi-bin/",
14194        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14195        domain: "roojs.com"
14196    })
14197    Roo.state.Manager.setProvider(cp);
14198  </code></pre>
14199  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14200  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14201  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14202  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14203  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14204  * domain the page is running on including the 'www' like 'www.roojs.com')
14205  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14206  * @constructor
14207  * Create a new CookieProvider
14208  * @param {Object} config The configuration object
14209  */
14210 Roo.state.CookieProvider = function(config){
14211     Roo.state.CookieProvider.superclass.constructor.call(this);
14212     this.path = "/";
14213     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14214     this.domain = null;
14215     this.secure = false;
14216     Roo.apply(this, config);
14217     this.state = this.readCookies();
14218 };
14219
14220 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14221     // private
14222     set : function(name, value){
14223         if(typeof value == "undefined" || value === null){
14224             this.clear(name);
14225             return;
14226         }
14227         this.setCookie(name, value);
14228         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14229     },
14230
14231     // private
14232     clear : function(name){
14233         this.clearCookie(name);
14234         Roo.state.CookieProvider.superclass.clear.call(this, name);
14235     },
14236
14237     // private
14238     readCookies : function(){
14239         var cookies = {};
14240         var c = document.cookie + ";";
14241         var re = /\s?(.*?)=(.*?);/g;
14242         var matches;
14243         while((matches = re.exec(c)) != null){
14244             var name = matches[1];
14245             var value = matches[2];
14246             if(name && name.substring(0,3) == "ys-"){
14247                 cookies[name.substr(3)] = this.decodeValue(value);
14248             }
14249         }
14250         return cookies;
14251     },
14252
14253     // private
14254     setCookie : function(name, value){
14255         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14256            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14257            ((this.path == null) ? "" : ("; path=" + this.path)) +
14258            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14259            ((this.secure == true) ? "; secure" : "");
14260     },
14261
14262     // private
14263     clearCookie : function(name){
14264         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14265            ((this.path == null) ? "" : ("; path=" + this.path)) +
14266            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14267            ((this.secure == true) ? "; secure" : "");
14268     }
14269 });/*
14270  * Based on:
14271  * Ext JS Library 1.1.1
14272  * Copyright(c) 2006-2007, Ext JS, LLC.
14273  *
14274  * Originally Released Under LGPL - original licence link has changed is not relivant.
14275  *
14276  * Fork - LGPL
14277  * <script type="text/javascript">
14278  */
14279
14280
14281
14282 /*
14283  * These classes are derivatives of the similarly named classes in the YUI Library.
14284  * The original license:
14285  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14286  * Code licensed under the BSD License:
14287  * http://developer.yahoo.net/yui/license.txt
14288  */
14289
14290 (function() {
14291
14292 var Event=Roo.EventManager;
14293 var Dom=Roo.lib.Dom;
14294
14295 /**
14296  * @class Roo.dd.DragDrop
14297  * Defines the interface and base operation of items that that can be
14298  * dragged or can be drop targets.  It was designed to be extended, overriding
14299  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14300  * Up to three html elements can be associated with a DragDrop instance:
14301  * <ul>
14302  * <li>linked element: the element that is passed into the constructor.
14303  * This is the element which defines the boundaries for interaction with
14304  * other DragDrop objects.</li>
14305  * <li>handle element(s): The drag operation only occurs if the element that
14306  * was clicked matches a handle element.  By default this is the linked
14307  * element, but there are times that you will want only a portion of the
14308  * linked element to initiate the drag operation, and the setHandleElId()
14309  * method provides a way to define this.</li>
14310  * <li>drag element: this represents the element that would be moved along
14311  * with the cursor during a drag operation.  By default, this is the linked
14312  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14313  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14314  * </li>
14315  * </ul>
14316  * This class should not be instantiated until the onload event to ensure that
14317  * the associated elements are available.
14318  * The following would define a DragDrop obj that would interact with any
14319  * other DragDrop obj in the "group1" group:
14320  * <pre>
14321  *  dd = new Roo.dd.DragDrop("div1", "group1");
14322  * </pre>
14323  * Since none of the event handlers have been implemented, nothing would
14324  * actually happen if you were to run the code above.  Normally you would
14325  * override this class or one of the default implementations, but you can
14326  * also override the methods you want on an instance of the class...
14327  * <pre>
14328  *  dd.onDragDrop = function(e, id) {
14329  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14330  *  }
14331  * </pre>
14332  * @constructor
14333  * @param {String} id of the element that is linked to this instance
14334  * @param {String} sGroup the group of related DragDrop objects
14335  * @param {object} config an object containing configurable attributes
14336  *                Valid properties for DragDrop:
14337  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14338  */
14339 Roo.dd.DragDrop = function(id, sGroup, config) {
14340     if (id) {
14341         this.init(id, sGroup, config);
14342     }
14343 };
14344
14345 Roo.dd.DragDrop.prototype = {
14346
14347     /**
14348      * The id of the element associated with this object.  This is what we
14349      * refer to as the "linked element" because the size and position of
14350      * this element is used to determine when the drag and drop objects have
14351      * interacted.
14352      * @property id
14353      * @type String
14354      */
14355     id: null,
14356
14357     /**
14358      * Configuration attributes passed into the constructor
14359      * @property config
14360      * @type object
14361      */
14362     config: null,
14363
14364     /**
14365      * The id of the element that will be dragged.  By default this is same
14366      * as the linked element , but could be changed to another element. Ex:
14367      * Roo.dd.DDProxy
14368      * @property dragElId
14369      * @type String
14370      * @private
14371      */
14372     dragElId: null,
14373
14374     /**
14375      * the id of the element that initiates the drag operation.  By default
14376      * this is the linked element, but could be changed to be a child of this
14377      * element.  This lets us do things like only starting the drag when the
14378      * header element within the linked html element is clicked.
14379      * @property handleElId
14380      * @type String
14381      * @private
14382      */
14383     handleElId: null,
14384
14385     /**
14386      * An associative array of HTML tags that will be ignored if clicked.
14387      * @property invalidHandleTypes
14388      * @type {string: string}
14389      */
14390     invalidHandleTypes: null,
14391
14392     /**
14393      * An associative array of ids for elements that will be ignored if clicked
14394      * @property invalidHandleIds
14395      * @type {string: string}
14396      */
14397     invalidHandleIds: null,
14398
14399     /**
14400      * An indexted array of css class names for elements that will be ignored
14401      * if clicked.
14402      * @property invalidHandleClasses
14403      * @type string[]
14404      */
14405     invalidHandleClasses: null,
14406
14407     /**
14408      * The linked element's absolute X position at the time the drag was
14409      * started
14410      * @property startPageX
14411      * @type int
14412      * @private
14413      */
14414     startPageX: 0,
14415
14416     /**
14417      * The linked element's absolute X position at the time the drag was
14418      * started
14419      * @property startPageY
14420      * @type int
14421      * @private
14422      */
14423     startPageY: 0,
14424
14425     /**
14426      * The group defines a logical collection of DragDrop objects that are
14427      * related.  Instances only get events when interacting with other
14428      * DragDrop object in the same group.  This lets us define multiple
14429      * groups using a single DragDrop subclass if we want.
14430      * @property groups
14431      * @type {string: string}
14432      */
14433     groups: null,
14434
14435     /**
14436      * Individual drag/drop instances can be locked.  This will prevent
14437      * onmousedown start drag.
14438      * @property locked
14439      * @type boolean
14440      * @private
14441      */
14442     locked: false,
14443
14444     /**
14445      * Lock this instance
14446      * @method lock
14447      */
14448     lock: function() { this.locked = true; },
14449
14450     /**
14451      * Unlock this instace
14452      * @method unlock
14453      */
14454     unlock: function() { this.locked = false; },
14455
14456     /**
14457      * By default, all insances can be a drop target.  This can be disabled by
14458      * setting isTarget to false.
14459      * @method isTarget
14460      * @type boolean
14461      */
14462     isTarget: true,
14463
14464     /**
14465      * The padding configured for this drag and drop object for calculating
14466      * the drop zone intersection with this object.
14467      * @method padding
14468      * @type int[]
14469      */
14470     padding: null,
14471
14472     /**
14473      * Cached reference to the linked element
14474      * @property _domRef
14475      * @private
14476      */
14477     _domRef: null,
14478
14479     /**
14480      * Internal typeof flag
14481      * @property __ygDragDrop
14482      * @private
14483      */
14484     __ygDragDrop: true,
14485
14486     /**
14487      * Set to true when horizontal contraints are applied
14488      * @property constrainX
14489      * @type boolean
14490      * @private
14491      */
14492     constrainX: false,
14493
14494     /**
14495      * Set to true when vertical contraints are applied
14496      * @property constrainY
14497      * @type boolean
14498      * @private
14499      */
14500     constrainY: false,
14501
14502     /**
14503      * The left constraint
14504      * @property minX
14505      * @type int
14506      * @private
14507      */
14508     minX: 0,
14509
14510     /**
14511      * The right constraint
14512      * @property maxX
14513      * @type int
14514      * @private
14515      */
14516     maxX: 0,
14517
14518     /**
14519      * The up constraint
14520      * @property minY
14521      * @type int
14522      * @type int
14523      * @private
14524      */
14525     minY: 0,
14526
14527     /**
14528      * The down constraint
14529      * @property maxY
14530      * @type int
14531      * @private
14532      */
14533     maxY: 0,
14534
14535     /**
14536      * Maintain offsets when we resetconstraints.  Set to true when you want
14537      * the position of the element relative to its parent to stay the same
14538      * when the page changes
14539      *
14540      * @property maintainOffset
14541      * @type boolean
14542      */
14543     maintainOffset: false,
14544
14545     /**
14546      * Array of pixel locations the element will snap to if we specified a
14547      * horizontal graduation/interval.  This array is generated automatically
14548      * when you define a tick interval.
14549      * @property xTicks
14550      * @type int[]
14551      */
14552     xTicks: null,
14553
14554     /**
14555      * Array of pixel locations the element will snap to if we specified a
14556      * vertical graduation/interval.  This array is generated automatically
14557      * when you define a tick interval.
14558      * @property yTicks
14559      * @type int[]
14560      */
14561     yTicks: null,
14562
14563     /**
14564      * By default the drag and drop instance will only respond to the primary
14565      * button click (left button for a right-handed mouse).  Set to true to
14566      * allow drag and drop to start with any mouse click that is propogated
14567      * by the browser
14568      * @property primaryButtonOnly
14569      * @type boolean
14570      */
14571     primaryButtonOnly: true,
14572
14573     /**
14574      * The availabe property is false until the linked dom element is accessible.
14575      * @property available
14576      * @type boolean
14577      */
14578     available: false,
14579
14580     /**
14581      * By default, drags can only be initiated if the mousedown occurs in the
14582      * region the linked element is.  This is done in part to work around a
14583      * bug in some browsers that mis-report the mousedown if the previous
14584      * mouseup happened outside of the window.  This property is set to true
14585      * if outer handles are defined.
14586      *
14587      * @property hasOuterHandles
14588      * @type boolean
14589      * @default false
14590      */
14591     hasOuterHandles: false,
14592
14593     /**
14594      * Code that executes immediately before the startDrag event
14595      * @method b4StartDrag
14596      * @private
14597      */
14598     b4StartDrag: function(x, y) { },
14599
14600     /**
14601      * Abstract method called after a drag/drop object is clicked
14602      * and the drag or mousedown time thresholds have beeen met.
14603      * @method startDrag
14604      * @param {int} X click location
14605      * @param {int} Y click location
14606      */
14607     startDrag: function(x, y) { /* override this */ },
14608
14609     /**
14610      * Code that executes immediately before the onDrag event
14611      * @method b4Drag
14612      * @private
14613      */
14614     b4Drag: function(e) { },
14615
14616     /**
14617      * Abstract method called during the onMouseMove event while dragging an
14618      * object.
14619      * @method onDrag
14620      * @param {Event} e the mousemove event
14621      */
14622     onDrag: function(e) { /* override this */ },
14623
14624     /**
14625      * Abstract method called when this element fist begins hovering over
14626      * another DragDrop obj
14627      * @method onDragEnter
14628      * @param {Event} e the mousemove event
14629      * @param {String|DragDrop[]} id In POINT mode, the element
14630      * id this is hovering over.  In INTERSECT mode, an array of one or more
14631      * dragdrop items being hovered over.
14632      */
14633     onDragEnter: function(e, id) { /* override this */ },
14634
14635     /**
14636      * Code that executes immediately before the onDragOver event
14637      * @method b4DragOver
14638      * @private
14639      */
14640     b4DragOver: function(e) { },
14641
14642     /**
14643      * Abstract method called when this element is hovering over another
14644      * DragDrop obj
14645      * @method onDragOver
14646      * @param {Event} e the mousemove event
14647      * @param {String|DragDrop[]} id In POINT mode, the element
14648      * id this is hovering over.  In INTERSECT mode, an array of dd items
14649      * being hovered over.
14650      */
14651     onDragOver: function(e, id) { /* override this */ },
14652
14653     /**
14654      * Code that executes immediately before the onDragOut event
14655      * @method b4DragOut
14656      * @private
14657      */
14658     b4DragOut: function(e) { },
14659
14660     /**
14661      * Abstract method called when we are no longer hovering over an element
14662      * @method onDragOut
14663      * @param {Event} e the mousemove event
14664      * @param {String|DragDrop[]} id In POINT mode, the element
14665      * id this was hovering over.  In INTERSECT mode, an array of dd items
14666      * that the mouse is no longer over.
14667      */
14668     onDragOut: function(e, id) { /* override this */ },
14669
14670     /**
14671      * Code that executes immediately before the onDragDrop event
14672      * @method b4DragDrop
14673      * @private
14674      */
14675     b4DragDrop: function(e) { },
14676
14677     /**
14678      * Abstract method called when this item is dropped on another DragDrop
14679      * obj
14680      * @method onDragDrop
14681      * @param {Event} e the mouseup event
14682      * @param {String|DragDrop[]} id In POINT mode, the element
14683      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14684      * was dropped on.
14685      */
14686     onDragDrop: function(e, id) { /* override this */ },
14687
14688     /**
14689      * Abstract method called when this item is dropped on an area with no
14690      * drop target
14691      * @method onInvalidDrop
14692      * @param {Event} e the mouseup event
14693      */
14694     onInvalidDrop: function(e) { /* override this */ },
14695
14696     /**
14697      * Code that executes immediately before the endDrag event
14698      * @method b4EndDrag
14699      * @private
14700      */
14701     b4EndDrag: function(e) { },
14702
14703     /**
14704      * Fired when we are done dragging the object
14705      * @method endDrag
14706      * @param {Event} e the mouseup event
14707      */
14708     endDrag: function(e) { /* override this */ },
14709
14710     /**
14711      * Code executed immediately before the onMouseDown event
14712      * @method b4MouseDown
14713      * @param {Event} e the mousedown event
14714      * @private
14715      */
14716     b4MouseDown: function(e) {  },
14717
14718     /**
14719      * Event handler that fires when a drag/drop obj gets a mousedown
14720      * @method onMouseDown
14721      * @param {Event} e the mousedown event
14722      */
14723     onMouseDown: function(e) { /* override this */ },
14724
14725     /**
14726      * Event handler that fires when a drag/drop obj gets a mouseup
14727      * @method onMouseUp
14728      * @param {Event} e the mouseup event
14729      */
14730     onMouseUp: function(e) { /* override this */ },
14731
14732     /**
14733      * Override the onAvailable method to do what is needed after the initial
14734      * position was determined.
14735      * @method onAvailable
14736      */
14737     onAvailable: function () {
14738     },
14739
14740     /*
14741      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14742      * @type Object
14743      */
14744     defaultPadding : {left:0, right:0, top:0, bottom:0},
14745
14746     /*
14747      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14748  *
14749  * Usage:
14750  <pre><code>
14751  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14752                 { dragElId: "existingProxyDiv" });
14753  dd.startDrag = function(){
14754      this.constrainTo("parent-id");
14755  };
14756  </code></pre>
14757  * Or you can initalize it using the {@link Roo.Element} object:
14758  <pre><code>
14759  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14760      startDrag : function(){
14761          this.constrainTo("parent-id");
14762      }
14763  });
14764  </code></pre>
14765      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14766      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14767      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14768      * an object containing the sides to pad. For example: {right:10, bottom:10}
14769      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14770      */
14771     constrainTo : function(constrainTo, pad, inContent){
14772         if(typeof pad == "number"){
14773             pad = {left: pad, right:pad, top:pad, bottom:pad};
14774         }
14775         pad = pad || this.defaultPadding;
14776         var b = Roo.get(this.getEl()).getBox();
14777         var ce = Roo.get(constrainTo);
14778         var s = ce.getScroll();
14779         var c, cd = ce.dom;
14780         if(cd == document.body){
14781             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14782         }else{
14783             xy = ce.getXY();
14784             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14785         }
14786
14787
14788         var topSpace = b.y - c.y;
14789         var leftSpace = b.x - c.x;
14790
14791         this.resetConstraints();
14792         this.setXConstraint(leftSpace - (pad.left||0), // left
14793                 c.width - leftSpace - b.width - (pad.right||0) //right
14794         );
14795         this.setYConstraint(topSpace - (pad.top||0), //top
14796                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14797         );
14798     },
14799
14800     /**
14801      * Returns a reference to the linked element
14802      * @method getEl
14803      * @return {HTMLElement} the html element
14804      */
14805     getEl: function() {
14806         if (!this._domRef) {
14807             this._domRef = Roo.getDom(this.id);
14808         }
14809
14810         return this._domRef;
14811     },
14812
14813     /**
14814      * Returns a reference to the actual element to drag.  By default this is
14815      * the same as the html element, but it can be assigned to another
14816      * element. An example of this can be found in Roo.dd.DDProxy
14817      * @method getDragEl
14818      * @return {HTMLElement} the html element
14819      */
14820     getDragEl: function() {
14821         return Roo.getDom(this.dragElId);
14822     },
14823
14824     /**
14825      * Sets up the DragDrop object.  Must be called in the constructor of any
14826      * Roo.dd.DragDrop subclass
14827      * @method init
14828      * @param id the id of the linked element
14829      * @param {String} sGroup the group of related items
14830      * @param {object} config configuration attributes
14831      */
14832     init: function(id, sGroup, config) {
14833         this.initTarget(id, sGroup, config);
14834         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14835         // Event.on(this.id, "selectstart", Event.preventDefault);
14836     },
14837
14838     /**
14839      * Initializes Targeting functionality only... the object does not
14840      * get a mousedown handler.
14841      * @method initTarget
14842      * @param id the id of the linked element
14843      * @param {String} sGroup the group of related items
14844      * @param {object} config configuration attributes
14845      */
14846     initTarget: function(id, sGroup, config) {
14847
14848         // configuration attributes
14849         this.config = config || {};
14850
14851         // create a local reference to the drag and drop manager
14852         this.DDM = Roo.dd.DDM;
14853         // initialize the groups array
14854         this.groups = {};
14855
14856         // assume that we have an element reference instead of an id if the
14857         // parameter is not a string
14858         if (typeof id !== "string") {
14859             id = Roo.id(id);
14860         }
14861
14862         // set the id
14863         this.id = id;
14864
14865         // add to an interaction group
14866         this.addToGroup((sGroup) ? sGroup : "default");
14867
14868         // We don't want to register this as the handle with the manager
14869         // so we just set the id rather than calling the setter.
14870         this.handleElId = id;
14871
14872         // the linked element is the element that gets dragged by default
14873         this.setDragElId(id);
14874
14875         // by default, clicked anchors will not start drag operations.
14876         this.invalidHandleTypes = { A: "A" };
14877         this.invalidHandleIds = {};
14878         this.invalidHandleClasses = [];
14879
14880         this.applyConfig();
14881
14882         this.handleOnAvailable();
14883     },
14884
14885     /**
14886      * Applies the configuration parameters that were passed into the constructor.
14887      * This is supposed to happen at each level through the inheritance chain.  So
14888      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14889      * DragDrop in order to get all of the parameters that are available in
14890      * each object.
14891      * @method applyConfig
14892      */
14893     applyConfig: function() {
14894
14895         // configurable properties:
14896         //    padding, isTarget, maintainOffset, primaryButtonOnly
14897         this.padding           = this.config.padding || [0, 0, 0, 0];
14898         this.isTarget          = (this.config.isTarget !== false);
14899         this.maintainOffset    = (this.config.maintainOffset);
14900         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14901
14902     },
14903
14904     /**
14905      * Executed when the linked element is available
14906      * @method handleOnAvailable
14907      * @private
14908      */
14909     handleOnAvailable: function() {
14910         this.available = true;
14911         this.resetConstraints();
14912         this.onAvailable();
14913     },
14914
14915      /**
14916      * Configures the padding for the target zone in px.  Effectively expands
14917      * (or reduces) the virtual object size for targeting calculations.
14918      * Supports css-style shorthand; if only one parameter is passed, all sides
14919      * will have that padding, and if only two are passed, the top and bottom
14920      * will have the first param, the left and right the second.
14921      * @method setPadding
14922      * @param {int} iTop    Top pad
14923      * @param {int} iRight  Right pad
14924      * @param {int} iBot    Bot pad
14925      * @param {int} iLeft   Left pad
14926      */
14927     setPadding: function(iTop, iRight, iBot, iLeft) {
14928         // this.padding = [iLeft, iRight, iTop, iBot];
14929         if (!iRight && 0 !== iRight) {
14930             this.padding = [iTop, iTop, iTop, iTop];
14931         } else if (!iBot && 0 !== iBot) {
14932             this.padding = [iTop, iRight, iTop, iRight];
14933         } else {
14934             this.padding = [iTop, iRight, iBot, iLeft];
14935         }
14936     },
14937
14938     /**
14939      * Stores the initial placement of the linked element.
14940      * @method setInitialPosition
14941      * @param {int} diffX   the X offset, default 0
14942      * @param {int} diffY   the Y offset, default 0
14943      */
14944     setInitPosition: function(diffX, diffY) {
14945         var el = this.getEl();
14946
14947         if (!this.DDM.verifyEl(el)) {
14948             return;
14949         }
14950
14951         var dx = diffX || 0;
14952         var dy = diffY || 0;
14953
14954         var p = Dom.getXY( el );
14955
14956         this.initPageX = p[0] - dx;
14957         this.initPageY = p[1] - dy;
14958
14959         this.lastPageX = p[0];
14960         this.lastPageY = p[1];
14961
14962
14963         this.setStartPosition(p);
14964     },
14965
14966     /**
14967      * Sets the start position of the element.  This is set when the obj
14968      * is initialized, the reset when a drag is started.
14969      * @method setStartPosition
14970      * @param pos current position (from previous lookup)
14971      * @private
14972      */
14973     setStartPosition: function(pos) {
14974         var p = pos || Dom.getXY( this.getEl() );
14975         this.deltaSetXY = null;
14976
14977         this.startPageX = p[0];
14978         this.startPageY = p[1];
14979     },
14980
14981     /**
14982      * Add this instance to a group of related drag/drop objects.  All
14983      * instances belong to at least one group, and can belong to as many
14984      * groups as needed.
14985      * @method addToGroup
14986      * @param sGroup {string} the name of the group
14987      */
14988     addToGroup: function(sGroup) {
14989         this.groups[sGroup] = true;
14990         this.DDM.regDragDrop(this, sGroup);
14991     },
14992
14993     /**
14994      * Remove's this instance from the supplied interaction group
14995      * @method removeFromGroup
14996      * @param {string}  sGroup  The group to drop
14997      */
14998     removeFromGroup: function(sGroup) {
14999         if (this.groups[sGroup]) {
15000             delete this.groups[sGroup];
15001         }
15002
15003         this.DDM.removeDDFromGroup(this, sGroup);
15004     },
15005
15006     /**
15007      * Allows you to specify that an element other than the linked element
15008      * will be moved with the cursor during a drag
15009      * @method setDragElId
15010      * @param id {string} the id of the element that will be used to initiate the drag
15011      */
15012     setDragElId: function(id) {
15013         this.dragElId = id;
15014     },
15015
15016     /**
15017      * Allows you to specify a child of the linked element that should be
15018      * used to initiate the drag operation.  An example of this would be if
15019      * you have a content div with text and links.  Clicking anywhere in the
15020      * content area would normally start the drag operation.  Use this method
15021      * to specify that an element inside of the content div is the element
15022      * that starts the drag operation.
15023      * @method setHandleElId
15024      * @param id {string} the id of the element that will be used to
15025      * initiate the drag.
15026      */
15027     setHandleElId: function(id) {
15028         if (typeof id !== "string") {
15029             id = Roo.id(id);
15030         }
15031         this.handleElId = id;
15032         this.DDM.regHandle(this.id, id);
15033     },
15034
15035     /**
15036      * Allows you to set an element outside of the linked element as a drag
15037      * handle
15038      * @method setOuterHandleElId
15039      * @param id the id of the element that will be used to initiate the drag
15040      */
15041     setOuterHandleElId: function(id) {
15042         if (typeof id !== "string") {
15043             id = Roo.id(id);
15044         }
15045         Event.on(id, "mousedown",
15046                 this.handleMouseDown, this);
15047         this.setHandleElId(id);
15048
15049         this.hasOuterHandles = true;
15050     },
15051
15052     /**
15053      * Remove all drag and drop hooks for this element
15054      * @method unreg
15055      */
15056     unreg: function() {
15057         Event.un(this.id, "mousedown",
15058                 this.handleMouseDown);
15059         this._domRef = null;
15060         this.DDM._remove(this);
15061     },
15062
15063     destroy : function(){
15064         this.unreg();
15065     },
15066
15067     /**
15068      * Returns true if this instance is locked, or the drag drop mgr is locked
15069      * (meaning that all drag/drop is disabled on the page.)
15070      * @method isLocked
15071      * @return {boolean} true if this obj or all drag/drop is locked, else
15072      * false
15073      */
15074     isLocked: function() {
15075         return (this.DDM.isLocked() || this.locked);
15076     },
15077
15078     /**
15079      * Fired when this object is clicked
15080      * @method handleMouseDown
15081      * @param {Event} e
15082      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15083      * @private
15084      */
15085     handleMouseDown: function(e, oDD){
15086         if (this.primaryButtonOnly && e.button != 0) {
15087             return;
15088         }
15089
15090         if (this.isLocked()) {
15091             return;
15092         }
15093
15094         this.DDM.refreshCache(this.groups);
15095
15096         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15097         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15098         } else {
15099             if (this.clickValidator(e)) {
15100
15101                 // set the initial element position
15102                 this.setStartPosition();
15103
15104
15105                 this.b4MouseDown(e);
15106                 this.onMouseDown(e);
15107
15108                 this.DDM.handleMouseDown(e, this);
15109
15110                 this.DDM.stopEvent(e);
15111             } else {
15112
15113
15114             }
15115         }
15116     },
15117
15118     clickValidator: function(e) {
15119         var target = e.getTarget();
15120         return ( this.isValidHandleChild(target) &&
15121                     (this.id == this.handleElId ||
15122                         this.DDM.handleWasClicked(target, this.id)) );
15123     },
15124
15125     /**
15126      * Allows you to specify a tag name that should not start a drag operation
15127      * when clicked.  This is designed to facilitate embedding links within a
15128      * drag handle that do something other than start the drag.
15129      * @method addInvalidHandleType
15130      * @param {string} tagName the type of element to exclude
15131      */
15132     addInvalidHandleType: function(tagName) {
15133         var type = tagName.toUpperCase();
15134         this.invalidHandleTypes[type] = type;
15135     },
15136
15137     /**
15138      * Lets you to specify an element id for a child of a drag handle
15139      * that should not initiate a drag
15140      * @method addInvalidHandleId
15141      * @param {string} id the element id of the element you wish to ignore
15142      */
15143     addInvalidHandleId: function(id) {
15144         if (typeof id !== "string") {
15145             id = Roo.id(id);
15146         }
15147         this.invalidHandleIds[id] = id;
15148     },
15149
15150     /**
15151      * Lets you specify a css class of elements that will not initiate a drag
15152      * @method addInvalidHandleClass
15153      * @param {string} cssClass the class of the elements you wish to ignore
15154      */
15155     addInvalidHandleClass: function(cssClass) {
15156         this.invalidHandleClasses.push(cssClass);
15157     },
15158
15159     /**
15160      * Unsets an excluded tag name set by addInvalidHandleType
15161      * @method removeInvalidHandleType
15162      * @param {string} tagName the type of element to unexclude
15163      */
15164     removeInvalidHandleType: function(tagName) {
15165         var type = tagName.toUpperCase();
15166         // this.invalidHandleTypes[type] = null;
15167         delete this.invalidHandleTypes[type];
15168     },
15169
15170     /**
15171      * Unsets an invalid handle id
15172      * @method removeInvalidHandleId
15173      * @param {string} id the id of the element to re-enable
15174      */
15175     removeInvalidHandleId: function(id) {
15176         if (typeof id !== "string") {
15177             id = Roo.id(id);
15178         }
15179         delete this.invalidHandleIds[id];
15180     },
15181
15182     /**
15183      * Unsets an invalid css class
15184      * @method removeInvalidHandleClass
15185      * @param {string} cssClass the class of the element(s) you wish to
15186      * re-enable
15187      */
15188     removeInvalidHandleClass: function(cssClass) {
15189         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15190             if (this.invalidHandleClasses[i] == cssClass) {
15191                 delete this.invalidHandleClasses[i];
15192             }
15193         }
15194     },
15195
15196     /**
15197      * Checks the tag exclusion list to see if this click should be ignored
15198      * @method isValidHandleChild
15199      * @param {HTMLElement} node the HTMLElement to evaluate
15200      * @return {boolean} true if this is a valid tag type, false if not
15201      */
15202     isValidHandleChild: function(node) {
15203
15204         var valid = true;
15205         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15206         var nodeName;
15207         try {
15208             nodeName = node.nodeName.toUpperCase();
15209         } catch(e) {
15210             nodeName = node.nodeName;
15211         }
15212         valid = valid && !this.invalidHandleTypes[nodeName];
15213         valid = valid && !this.invalidHandleIds[node.id];
15214
15215         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15216             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15217         }
15218
15219
15220         return valid;
15221
15222     },
15223
15224     /**
15225      * Create the array of horizontal tick marks if an interval was specified
15226      * in setXConstraint().
15227      * @method setXTicks
15228      * @private
15229      */
15230     setXTicks: function(iStartX, iTickSize) {
15231         this.xTicks = [];
15232         this.xTickSize = iTickSize;
15233
15234         var tickMap = {};
15235
15236         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.xTicks[this.xTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15244             if (!tickMap[i]) {
15245                 this.xTicks[this.xTicks.length] = i;
15246                 tickMap[i] = true;
15247             }
15248         }
15249
15250         this.xTicks.sort(this.DDM.numericSort) ;
15251     },
15252
15253     /**
15254      * Create the array of vertical tick marks if an interval was specified in
15255      * setYConstraint().
15256      * @method setYTicks
15257      * @private
15258      */
15259     setYTicks: function(iStartY, iTickSize) {
15260         this.yTicks = [];
15261         this.yTickSize = iTickSize;
15262
15263         var tickMap = {};
15264
15265         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15266             if (!tickMap[i]) {
15267                 this.yTicks[this.yTicks.length] = i;
15268                 tickMap[i] = true;
15269             }
15270         }
15271
15272         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15273             if (!tickMap[i]) {
15274                 this.yTicks[this.yTicks.length] = i;
15275                 tickMap[i] = true;
15276             }
15277         }
15278
15279         this.yTicks.sort(this.DDM.numericSort) ;
15280     },
15281
15282     /**
15283      * By default, the element can be dragged any place on the screen.  Use
15284      * this method to limit the horizontal travel of the element.  Pass in
15285      * 0,0 for the parameters if you want to lock the drag to the y axis.
15286      * @method setXConstraint
15287      * @param {int} iLeft the number of pixels the element can move to the left
15288      * @param {int} iRight the number of pixels the element can move to the
15289      * right
15290      * @param {int} iTickSize optional parameter for specifying that the
15291      * element
15292      * should move iTickSize pixels at a time.
15293      */
15294     setXConstraint: function(iLeft, iRight, iTickSize) {
15295         this.leftConstraint = iLeft;
15296         this.rightConstraint = iRight;
15297
15298         this.minX = this.initPageX - iLeft;
15299         this.maxX = this.initPageX + iRight;
15300         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15301
15302         this.constrainX = true;
15303     },
15304
15305     /**
15306      * Clears any constraints applied to this instance.  Also clears ticks
15307      * since they can't exist independent of a constraint at this time.
15308      * @method clearConstraints
15309      */
15310     clearConstraints: function() {
15311         this.constrainX = false;
15312         this.constrainY = false;
15313         this.clearTicks();
15314     },
15315
15316     /**
15317      * Clears any tick interval defined for this instance
15318      * @method clearTicks
15319      */
15320     clearTicks: function() {
15321         this.xTicks = null;
15322         this.yTicks = null;
15323         this.xTickSize = 0;
15324         this.yTickSize = 0;
15325     },
15326
15327     /**
15328      * By default, the element can be dragged any place on the screen.  Set
15329      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15330      * parameters if you want to lock the drag to the x axis.
15331      * @method setYConstraint
15332      * @param {int} iUp the number of pixels the element can move up
15333      * @param {int} iDown the number of pixels the element can move down
15334      * @param {int} iTickSize optional parameter for specifying that the
15335      * element should move iTickSize pixels at a time.
15336      */
15337     setYConstraint: function(iUp, iDown, iTickSize) {
15338         this.topConstraint = iUp;
15339         this.bottomConstraint = iDown;
15340
15341         this.minY = this.initPageY - iUp;
15342         this.maxY = this.initPageY + iDown;
15343         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15344
15345         this.constrainY = true;
15346
15347     },
15348
15349     /**
15350      * resetConstraints must be called if you manually reposition a dd element.
15351      * @method resetConstraints
15352      * @param {boolean} maintainOffset
15353      */
15354     resetConstraints: function() {
15355
15356
15357         // Maintain offsets if necessary
15358         if (this.initPageX || this.initPageX === 0) {
15359             // figure out how much this thing has moved
15360             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15361             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15362
15363             this.setInitPosition(dx, dy);
15364
15365         // This is the first time we have detected the element's position
15366         } else {
15367             this.setInitPosition();
15368         }
15369
15370         if (this.constrainX) {
15371             this.setXConstraint( this.leftConstraint,
15372                                  this.rightConstraint,
15373                                  this.xTickSize        );
15374         }
15375
15376         if (this.constrainY) {
15377             this.setYConstraint( this.topConstraint,
15378                                  this.bottomConstraint,
15379                                  this.yTickSize         );
15380         }
15381     },
15382
15383     /**
15384      * Normally the drag element is moved pixel by pixel, but we can specify
15385      * that it move a number of pixels at a time.  This method resolves the
15386      * location when we have it set up like this.
15387      * @method getTick
15388      * @param {int} val where we want to place the object
15389      * @param {int[]} tickArray sorted array of valid points
15390      * @return {int} the closest tick
15391      * @private
15392      */
15393     getTick: function(val, tickArray) {
15394
15395         if (!tickArray) {
15396             // If tick interval is not defined, it is effectively 1 pixel,
15397             // so we return the value passed to us.
15398             return val;
15399         } else if (tickArray[0] >= val) {
15400             // The value is lower than the first tick, so we return the first
15401             // tick.
15402             return tickArray[0];
15403         } else {
15404             for (var i=0, len=tickArray.length; i<len; ++i) {
15405                 var next = i + 1;
15406                 if (tickArray[next] && tickArray[next] >= val) {
15407                     var diff1 = val - tickArray[i];
15408                     var diff2 = tickArray[next] - val;
15409                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15410                 }
15411             }
15412
15413             // The value is larger than the last tick, so we return the last
15414             // tick.
15415             return tickArray[tickArray.length - 1];
15416         }
15417     },
15418
15419     /**
15420      * toString method
15421      * @method toString
15422      * @return {string} string representation of the dd obj
15423      */
15424     toString: function() {
15425         return ("DragDrop " + this.id);
15426     }
15427
15428 };
15429
15430 })();
15431 /*
15432  * Based on:
15433  * Ext JS Library 1.1.1
15434  * Copyright(c) 2006-2007, Ext JS, LLC.
15435  *
15436  * Originally Released Under LGPL - original licence link has changed is not relivant.
15437  *
15438  * Fork - LGPL
15439  * <script type="text/javascript">
15440  */
15441
15442
15443 /**
15444  * The drag and drop utility provides a framework for building drag and drop
15445  * applications.  In addition to enabling drag and drop for specific elements,
15446  * the drag and drop elements are tracked by the manager class, and the
15447  * interactions between the various elements are tracked during the drag and
15448  * the implementing code is notified about these important moments.
15449  */
15450
15451 // Only load the library once.  Rewriting the manager class would orphan
15452 // existing drag and drop instances.
15453 if (!Roo.dd.DragDropMgr) {
15454
15455 /**
15456  * @class Roo.dd.DragDropMgr
15457  * DragDropMgr is a singleton that tracks the element interaction for
15458  * all DragDrop items in the window.  Generally, you will not call
15459  * this class directly, but it does have helper methods that could
15460  * be useful in your DragDrop implementations.
15461  * @singleton
15462  */
15463 Roo.dd.DragDropMgr = function() {
15464
15465     var Event = Roo.EventManager;
15466
15467     return {
15468
15469         /**
15470          * Two dimensional Array of registered DragDrop objects.  The first
15471          * dimension is the DragDrop item group, the second the DragDrop
15472          * object.
15473          * @property ids
15474          * @type {string: string}
15475          * @private
15476          * @static
15477          */
15478         ids: {},
15479
15480         /**
15481          * Array of element ids defined as drag handles.  Used to determine
15482          * if the element that generated the mousedown event is actually the
15483          * handle and not the html element itself.
15484          * @property handleIds
15485          * @type {string: string}
15486          * @private
15487          * @static
15488          */
15489         handleIds: {},
15490
15491         /**
15492          * the DragDrop object that is currently being dragged
15493          * @property dragCurrent
15494          * @type DragDrop
15495          * @private
15496          * @static
15497          **/
15498         dragCurrent: null,
15499
15500         /**
15501          * the DragDrop object(s) that are being hovered over
15502          * @property dragOvers
15503          * @type Array
15504          * @private
15505          * @static
15506          */
15507         dragOvers: {},
15508
15509         /**
15510          * the X distance between the cursor and the object being dragged
15511          * @property deltaX
15512          * @type int
15513          * @private
15514          * @static
15515          */
15516         deltaX: 0,
15517
15518         /**
15519          * the Y distance between the cursor and the object being dragged
15520          * @property deltaY
15521          * @type int
15522          * @private
15523          * @static
15524          */
15525         deltaY: 0,
15526
15527         /**
15528          * Flag to determine if we should prevent the default behavior of the
15529          * events we define. By default this is true, but this can be set to
15530          * false if you need the default behavior (not recommended)
15531          * @property preventDefault
15532          * @type boolean
15533          * @static
15534          */
15535         preventDefault: true,
15536
15537         /**
15538          * Flag to determine if we should stop the propagation of the events
15539          * we generate. This is true by default but you may want to set it to
15540          * false if the html element contains other features that require the
15541          * mouse click.
15542          * @property stopPropagation
15543          * @type boolean
15544          * @static
15545          */
15546         stopPropagation: true,
15547
15548         /**
15549          * Internal flag that is set to true when drag and drop has been
15550          * intialized
15551          * @property initialized
15552          * @private
15553          * @static
15554          */
15555         initalized: false,
15556
15557         /**
15558          * All drag and drop can be disabled.
15559          * @property locked
15560          * @private
15561          * @static
15562          */
15563         locked: false,
15564
15565         /**
15566          * Called the first time an element is registered.
15567          * @method init
15568          * @private
15569          * @static
15570          */
15571         init: function() {
15572             this.initialized = true;
15573         },
15574
15575         /**
15576          * In point mode, drag and drop interaction is defined by the
15577          * location of the cursor during the drag/drop
15578          * @property POINT
15579          * @type int
15580          * @static
15581          */
15582         POINT: 0,
15583
15584         /**
15585          * In intersect mode, drag and drop interactio nis defined by the
15586          * overlap of two or more drag and drop objects.
15587          * @property INTERSECT
15588          * @type int
15589          * @static
15590          */
15591         INTERSECT: 1,
15592
15593         /**
15594          * The current drag and drop mode.  Default: POINT
15595          * @property mode
15596          * @type int
15597          * @static
15598          */
15599         mode: 0,
15600
15601         /**
15602          * Runs method on all drag and drop objects
15603          * @method _execOnAll
15604          * @private
15605          * @static
15606          */
15607         _execOnAll: function(sMethod, args) {
15608             for (var i in this.ids) {
15609                 for (var j in this.ids[i]) {
15610                     var oDD = this.ids[i][j];
15611                     if (! this.isTypeOfDD(oDD)) {
15612                         continue;
15613                     }
15614                     oDD[sMethod].apply(oDD, args);
15615                 }
15616             }
15617         },
15618
15619         /**
15620          * Drag and drop initialization.  Sets up the global event handlers
15621          * @method _onLoad
15622          * @private
15623          * @static
15624          */
15625         _onLoad: function() {
15626
15627             this.init();
15628
15629
15630             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15631             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15632             Event.on(window,   "unload",    this._onUnload, this, true);
15633             Event.on(window,   "resize",    this._onResize, this, true);
15634             // Event.on(window,   "mouseout",    this._test);
15635
15636         },
15637
15638         /**
15639          * Reset constraints on all drag and drop objs
15640          * @method _onResize
15641          * @private
15642          * @static
15643          */
15644         _onResize: function(e) {
15645             this._execOnAll("resetConstraints", []);
15646         },
15647
15648         /**
15649          * Lock all drag and drop functionality
15650          * @method lock
15651          * @static
15652          */
15653         lock: function() { this.locked = true; },
15654
15655         /**
15656          * Unlock all drag and drop functionality
15657          * @method unlock
15658          * @static
15659          */
15660         unlock: function() { this.locked = false; },
15661
15662         /**
15663          * Is drag and drop locked?
15664          * @method isLocked
15665          * @return {boolean} True if drag and drop is locked, false otherwise.
15666          * @static
15667          */
15668         isLocked: function() { return this.locked; },
15669
15670         /**
15671          * Location cache that is set for all drag drop objects when a drag is
15672          * initiated, cleared when the drag is finished.
15673          * @property locationCache
15674          * @private
15675          * @static
15676          */
15677         locationCache: {},
15678
15679         /**
15680          * Set useCache to false if you want to force object the lookup of each
15681          * drag and drop linked element constantly during a drag.
15682          * @property useCache
15683          * @type boolean
15684          * @static
15685          */
15686         useCache: true,
15687
15688         /**
15689          * The number of pixels that the mouse needs to move after the
15690          * mousedown before the drag is initiated.  Default=3;
15691          * @property clickPixelThresh
15692          * @type int
15693          * @static
15694          */
15695         clickPixelThresh: 3,
15696
15697         /**
15698          * The number of milliseconds after the mousedown event to initiate the
15699          * drag if we don't get a mouseup event. Default=1000
15700          * @property clickTimeThresh
15701          * @type int
15702          * @static
15703          */
15704         clickTimeThresh: 350,
15705
15706         /**
15707          * Flag that indicates that either the drag pixel threshold or the
15708          * mousdown time threshold has been met
15709          * @property dragThreshMet
15710          * @type boolean
15711          * @private
15712          * @static
15713          */
15714         dragThreshMet: false,
15715
15716         /**
15717          * Timeout used for the click time threshold
15718          * @property clickTimeout
15719          * @type Object
15720          * @private
15721          * @static
15722          */
15723         clickTimeout: null,
15724
15725         /**
15726          * The X position of the mousedown event stored for later use when a
15727          * drag threshold is met.
15728          * @property startX
15729          * @type int
15730          * @private
15731          * @static
15732          */
15733         startX: 0,
15734
15735         /**
15736          * The Y position of the mousedown event stored for later use when a
15737          * drag threshold is met.
15738          * @property startY
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         startY: 0,
15744
15745         /**
15746          * Each DragDrop instance must be registered with the DragDropMgr.
15747          * This is executed in DragDrop.init()
15748          * @method regDragDrop
15749          * @param {DragDrop} oDD the DragDrop object to register
15750          * @param {String} sGroup the name of the group this element belongs to
15751          * @static
15752          */
15753         regDragDrop: function(oDD, sGroup) {
15754             if (!this.initialized) { this.init(); }
15755
15756             if (!this.ids[sGroup]) {
15757                 this.ids[sGroup] = {};
15758             }
15759             this.ids[sGroup][oDD.id] = oDD;
15760         },
15761
15762         /**
15763          * Removes the supplied dd instance from the supplied group. Executed
15764          * by DragDrop.removeFromGroup, so don't call this function directly.
15765          * @method removeDDFromGroup
15766          * @private
15767          * @static
15768          */
15769         removeDDFromGroup: function(oDD, sGroup) {
15770             if (!this.ids[sGroup]) {
15771                 this.ids[sGroup] = {};
15772             }
15773
15774             var obj = this.ids[sGroup];
15775             if (obj && obj[oDD.id]) {
15776                 delete obj[oDD.id];
15777             }
15778         },
15779
15780         /**
15781          * Unregisters a drag and drop item.  This is executed in
15782          * DragDrop.unreg, use that method instead of calling this directly.
15783          * @method _remove
15784          * @private
15785          * @static
15786          */
15787         _remove: function(oDD) {
15788             for (var g in oDD.groups) {
15789                 if (g && this.ids[g][oDD.id]) {
15790                     delete this.ids[g][oDD.id];
15791                 }
15792             }
15793             delete this.handleIds[oDD.id];
15794         },
15795
15796         /**
15797          * Each DragDrop handle element must be registered.  This is done
15798          * automatically when executing DragDrop.setHandleElId()
15799          * @method regHandle
15800          * @param {String} sDDId the DragDrop id this element is a handle for
15801          * @param {String} sHandleId the id of the element that is the drag
15802          * handle
15803          * @static
15804          */
15805         regHandle: function(sDDId, sHandleId) {
15806             if (!this.handleIds[sDDId]) {
15807                 this.handleIds[sDDId] = {};
15808             }
15809             this.handleIds[sDDId][sHandleId] = sHandleId;
15810         },
15811
15812         /**
15813          * Utility function to determine if a given element has been
15814          * registered as a drag drop item.
15815          * @method isDragDrop
15816          * @param {String} id the element id to check
15817          * @return {boolean} true if this element is a DragDrop item,
15818          * false otherwise
15819          * @static
15820          */
15821         isDragDrop: function(id) {
15822             return ( this.getDDById(id) ) ? true : false;
15823         },
15824
15825         /**
15826          * Returns the drag and drop instances that are in all groups the
15827          * passed in instance belongs to.
15828          * @method getRelated
15829          * @param {DragDrop} p_oDD the obj to get related data for
15830          * @param {boolean} bTargetsOnly if true, only return targetable objs
15831          * @return {DragDrop[]} the related instances
15832          * @static
15833          */
15834         getRelated: function(p_oDD, bTargetsOnly) {
15835             var oDDs = [];
15836             for (var i in p_oDD.groups) {
15837                 for (j in this.ids[i]) {
15838                     var dd = this.ids[i][j];
15839                     if (! this.isTypeOfDD(dd)) {
15840                         continue;
15841                     }
15842                     if (!bTargetsOnly || dd.isTarget) {
15843                         oDDs[oDDs.length] = dd;
15844                     }
15845                 }
15846             }
15847
15848             return oDDs;
15849         },
15850
15851         /**
15852          * Returns true if the specified dd target is a legal target for
15853          * the specifice drag obj
15854          * @method isLegalTarget
15855          * @param {DragDrop} the drag obj
15856          * @param {DragDrop} the target
15857          * @return {boolean} true if the target is a legal target for the
15858          * dd obj
15859          * @static
15860          */
15861         isLegalTarget: function (oDD, oTargetDD) {
15862             var targets = this.getRelated(oDD, true);
15863             for (var i=0, len=targets.length;i<len;++i) {
15864                 if (targets[i].id == oTargetDD.id) {
15865                     return true;
15866                 }
15867             }
15868
15869             return false;
15870         },
15871
15872         /**
15873          * My goal is to be able to transparently determine if an object is
15874          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15875          * returns "object", oDD.constructor.toString() always returns
15876          * "DragDrop" and not the name of the subclass.  So for now it just
15877          * evaluates a well-known variable in DragDrop.
15878          * @method isTypeOfDD
15879          * @param {Object} the object to evaluate
15880          * @return {boolean} true if typeof oDD = DragDrop
15881          * @static
15882          */
15883         isTypeOfDD: function (oDD) {
15884             return (oDD && oDD.__ygDragDrop);
15885         },
15886
15887         /**
15888          * Utility function to determine if a given element has been
15889          * registered as a drag drop handle for the given Drag Drop object.
15890          * @method isHandle
15891          * @param {String} id the element id to check
15892          * @return {boolean} true if this element is a DragDrop handle, false
15893          * otherwise
15894          * @static
15895          */
15896         isHandle: function(sDDId, sHandleId) {
15897             return ( this.handleIds[sDDId] &&
15898                             this.handleIds[sDDId][sHandleId] );
15899         },
15900
15901         /**
15902          * Returns the DragDrop instance for a given id
15903          * @method getDDById
15904          * @param {String} id the id of the DragDrop object
15905          * @return {DragDrop} the drag drop object, null if it is not found
15906          * @static
15907          */
15908         getDDById: function(id) {
15909             for (var i in this.ids) {
15910                 if (this.ids[i][id]) {
15911                     return this.ids[i][id];
15912                 }
15913             }
15914             return null;
15915         },
15916
15917         /**
15918          * Fired after a registered DragDrop object gets the mousedown event.
15919          * Sets up the events required to track the object being dragged
15920          * @method handleMouseDown
15921          * @param {Event} e the event
15922          * @param oDD the DragDrop object being dragged
15923          * @private
15924          * @static
15925          */
15926         handleMouseDown: function(e, oDD) {
15927             if(Roo.QuickTips){
15928                 Roo.QuickTips.disable();
15929             }
15930             this.currentTarget = e.getTarget();
15931
15932             this.dragCurrent = oDD;
15933
15934             var el = oDD.getEl();
15935
15936             // track start position
15937             this.startX = e.getPageX();
15938             this.startY = e.getPageY();
15939
15940             this.deltaX = this.startX - el.offsetLeft;
15941             this.deltaY = this.startY - el.offsetTop;
15942
15943             this.dragThreshMet = false;
15944
15945             this.clickTimeout = setTimeout(
15946                     function() {
15947                         var DDM = Roo.dd.DDM;
15948                         DDM.startDrag(DDM.startX, DDM.startY);
15949                     },
15950                     this.clickTimeThresh );
15951         },
15952
15953         /**
15954          * Fired when either the drag pixel threshol or the mousedown hold
15955          * time threshold has been met.
15956          * @method startDrag
15957          * @param x {int} the X position of the original mousedown
15958          * @param y {int} the Y position of the original mousedown
15959          * @static
15960          */
15961         startDrag: function(x, y) {
15962             clearTimeout(this.clickTimeout);
15963             if (this.dragCurrent) {
15964                 this.dragCurrent.b4StartDrag(x, y);
15965                 this.dragCurrent.startDrag(x, y);
15966             }
15967             this.dragThreshMet = true;
15968         },
15969
15970         /**
15971          * Internal function to handle the mouseup event.  Will be invoked
15972          * from the context of the document.
15973          * @method handleMouseUp
15974          * @param {Event} e the event
15975          * @private
15976          * @static
15977          */
15978         handleMouseUp: function(e) {
15979
15980             if(Roo.QuickTips){
15981                 Roo.QuickTips.enable();
15982             }
15983             if (! this.dragCurrent) {
15984                 return;
15985             }
15986
15987             clearTimeout(this.clickTimeout);
15988
15989             if (this.dragThreshMet) {
15990                 this.fireEvents(e, true);
15991             } else {
15992             }
15993
15994             this.stopDrag(e);
15995
15996             this.stopEvent(e);
15997         },
15998
15999         /**
16000          * Utility to stop event propagation and event default, if these
16001          * features are turned on.
16002          * @method stopEvent
16003          * @param {Event} e the event as returned by this.getEvent()
16004          * @static
16005          */
16006         stopEvent: function(e){
16007             if(this.stopPropagation) {
16008                 e.stopPropagation();
16009             }
16010
16011             if (this.preventDefault) {
16012                 e.preventDefault();
16013             }
16014         },
16015
16016         /**
16017          * Internal function to clean up event handlers after the drag
16018          * operation is complete
16019          * @method stopDrag
16020          * @param {Event} e the event
16021          * @private
16022          * @static
16023          */
16024         stopDrag: function(e) {
16025             // Fire the drag end event for the item that was dragged
16026             if (this.dragCurrent) {
16027                 if (this.dragThreshMet) {
16028                     this.dragCurrent.b4EndDrag(e);
16029                     this.dragCurrent.endDrag(e);
16030                 }
16031
16032                 this.dragCurrent.onMouseUp(e);
16033             }
16034
16035             this.dragCurrent = null;
16036             this.dragOvers = {};
16037         },
16038
16039         /**
16040          * Internal function to handle the mousemove event.  Will be invoked
16041          * from the context of the html element.
16042          *
16043          * @TODO figure out what we can do about mouse events lost when the
16044          * user drags objects beyond the window boundary.  Currently we can
16045          * detect this in internet explorer by verifying that the mouse is
16046          * down during the mousemove event.  Firefox doesn't give us the
16047          * button state on the mousemove event.
16048          * @method handleMouseMove
16049          * @param {Event} e the event
16050          * @private
16051          * @static
16052          */
16053         handleMouseMove: function(e) {
16054             if (! this.dragCurrent) {
16055                 return true;
16056             }
16057
16058             // var button = e.which || e.button;
16059
16060             // check for IE mouseup outside of page boundary
16061             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16062                 this.stopEvent(e);
16063                 return this.handleMouseUp(e);
16064             }
16065
16066             if (!this.dragThreshMet) {
16067                 var diffX = Math.abs(this.startX - e.getPageX());
16068                 var diffY = Math.abs(this.startY - e.getPageY());
16069                 if (diffX > this.clickPixelThresh ||
16070                             diffY > this.clickPixelThresh) {
16071                     this.startDrag(this.startX, this.startY);
16072                 }
16073             }
16074
16075             if (this.dragThreshMet) {
16076                 this.dragCurrent.b4Drag(e);
16077                 this.dragCurrent.onDrag(e);
16078                 if(!this.dragCurrent.moveOnly){
16079                     this.fireEvents(e, false);
16080                 }
16081             }
16082
16083             this.stopEvent(e);
16084
16085             return true;
16086         },
16087
16088         /**
16089          * Iterates over all of the DragDrop elements to find ones we are
16090          * hovering over or dropping on
16091          * @method fireEvents
16092          * @param {Event} e the event
16093          * @param {boolean} isDrop is this a drop op or a mouseover op?
16094          * @private
16095          * @static
16096          */
16097         fireEvents: function(e, isDrop) {
16098             var dc = this.dragCurrent;
16099
16100             // If the user did the mouse up outside of the window, we could
16101             // get here even though we have ended the drag.
16102             if (!dc || dc.isLocked()) {
16103                 return;
16104             }
16105
16106             var pt = e.getPoint();
16107
16108             // cache the previous dragOver array
16109             var oldOvers = [];
16110
16111             var outEvts   = [];
16112             var overEvts  = [];
16113             var dropEvts  = [];
16114             var enterEvts = [];
16115
16116             // Check to see if the object(s) we were hovering over is no longer
16117             // being hovered over so we can fire the onDragOut event
16118             for (var i in this.dragOvers) {
16119
16120                 var ddo = this.dragOvers[i];
16121
16122                 if (! this.isTypeOfDD(ddo)) {
16123                     continue;
16124                 }
16125
16126                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16127                     outEvts.push( ddo );
16128                 }
16129
16130                 oldOvers[i] = true;
16131                 delete this.dragOvers[i];
16132             }
16133
16134             for (var sGroup in dc.groups) {
16135
16136                 if ("string" != typeof sGroup) {
16137                     continue;
16138                 }
16139
16140                 for (i in this.ids[sGroup]) {
16141                     var oDD = this.ids[sGroup][i];
16142                     if (! this.isTypeOfDD(oDD)) {
16143                         continue;
16144                     }
16145
16146                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16147                         if (this.isOverTarget(pt, oDD, this.mode)) {
16148                             // look for drop interactions
16149                             if (isDrop) {
16150                                 dropEvts.push( oDD );
16151                             // look for drag enter and drag over interactions
16152                             } else {
16153
16154                                 // initial drag over: dragEnter fires
16155                                 if (!oldOvers[oDD.id]) {
16156                                     enterEvts.push( oDD );
16157                                 // subsequent drag overs: dragOver fires
16158                                 } else {
16159                                     overEvts.push( oDD );
16160                                 }
16161
16162                                 this.dragOvers[oDD.id] = oDD;
16163                             }
16164                         }
16165                     }
16166                 }
16167             }
16168
16169             if (this.mode) {
16170                 if (outEvts.length) {
16171                     dc.b4DragOut(e, outEvts);
16172                     dc.onDragOut(e, outEvts);
16173                 }
16174
16175                 if (enterEvts.length) {
16176                     dc.onDragEnter(e, enterEvts);
16177                 }
16178
16179                 if (overEvts.length) {
16180                     dc.b4DragOver(e, overEvts);
16181                     dc.onDragOver(e, overEvts);
16182                 }
16183
16184                 if (dropEvts.length) {
16185                     dc.b4DragDrop(e, dropEvts);
16186                     dc.onDragDrop(e, dropEvts);
16187                 }
16188
16189             } else {
16190                 // fire dragout events
16191                 var len = 0;
16192                 for (i=0, len=outEvts.length; i<len; ++i) {
16193                     dc.b4DragOut(e, outEvts[i].id);
16194                     dc.onDragOut(e, outEvts[i].id);
16195                 }
16196
16197                 // fire enter events
16198                 for (i=0,len=enterEvts.length; i<len; ++i) {
16199                     // dc.b4DragEnter(e, oDD.id);
16200                     dc.onDragEnter(e, enterEvts[i].id);
16201                 }
16202
16203                 // fire over events
16204                 for (i=0,len=overEvts.length; i<len; ++i) {
16205                     dc.b4DragOver(e, overEvts[i].id);
16206                     dc.onDragOver(e, overEvts[i].id);
16207                 }
16208
16209                 // fire drop events
16210                 for (i=0, len=dropEvts.length; i<len; ++i) {
16211                     dc.b4DragDrop(e, dropEvts[i].id);
16212                     dc.onDragDrop(e, dropEvts[i].id);
16213                 }
16214
16215             }
16216
16217             // notify about a drop that did not find a target
16218             if (isDrop && !dropEvts.length) {
16219                 dc.onInvalidDrop(e);
16220             }
16221
16222         },
16223
16224         /**
16225          * Helper function for getting the best match from the list of drag
16226          * and drop objects returned by the drag and drop events when we are
16227          * in INTERSECT mode.  It returns either the first object that the
16228          * cursor is over, or the object that has the greatest overlap with
16229          * the dragged element.
16230          * @method getBestMatch
16231          * @param  {DragDrop[]} dds The array of drag and drop objects
16232          * targeted
16233          * @return {DragDrop}       The best single match
16234          * @static
16235          */
16236         getBestMatch: function(dds) {
16237             var winner = null;
16238             // Return null if the input is not what we expect
16239             //if (!dds || !dds.length || dds.length == 0) {
16240                // winner = null;
16241             // If there is only one item, it wins
16242             //} else if (dds.length == 1) {
16243
16244             var len = dds.length;
16245
16246             if (len == 1) {
16247                 winner = dds[0];
16248             } else {
16249                 // Loop through the targeted items
16250                 for (var i=0; i<len; ++i) {
16251                     var dd = dds[i];
16252                     // If the cursor is over the object, it wins.  If the
16253                     // cursor is over multiple matches, the first one we come
16254                     // to wins.
16255                     if (dd.cursorIsOver) {
16256                         winner = dd;
16257                         break;
16258                     // Otherwise the object with the most overlap wins
16259                     } else {
16260                         if (!winner ||
16261                             winner.overlap.getArea() < dd.overlap.getArea()) {
16262                             winner = dd;
16263                         }
16264                     }
16265                 }
16266             }
16267
16268             return winner;
16269         },
16270
16271         /**
16272          * Refreshes the cache of the top-left and bottom-right points of the
16273          * drag and drop objects in the specified group(s).  This is in the
16274          * format that is stored in the drag and drop instance, so typical
16275          * usage is:
16276          * <code>
16277          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16278          * </code>
16279          * Alternatively:
16280          * <code>
16281          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16282          * </code>
16283          * @TODO this really should be an indexed array.  Alternatively this
16284          * method could accept both.
16285          * @method refreshCache
16286          * @param {Object} groups an associative array of groups to refresh
16287          * @static
16288          */
16289         refreshCache: function(groups) {
16290             for (var sGroup in groups) {
16291                 if ("string" != typeof sGroup) {
16292                     continue;
16293                 }
16294                 for (var i in this.ids[sGroup]) {
16295                     var oDD = this.ids[sGroup][i];
16296
16297                     if (this.isTypeOfDD(oDD)) {
16298                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16299                         var loc = this.getLocation(oDD);
16300                         if (loc) {
16301                             this.locationCache[oDD.id] = loc;
16302                         } else {
16303                             delete this.locationCache[oDD.id];
16304                             // this will unregister the drag and drop object if
16305                             // the element is not in a usable state
16306                             // oDD.unreg();
16307                         }
16308                     }
16309                 }
16310             }
16311         },
16312
16313         /**
16314          * This checks to make sure an element exists and is in the DOM.  The
16315          * main purpose is to handle cases where innerHTML is used to remove
16316          * drag and drop objects from the DOM.  IE provides an 'unspecified
16317          * error' when trying to access the offsetParent of such an element
16318          * @method verifyEl
16319          * @param {HTMLElement} el the element to check
16320          * @return {boolean} true if the element looks usable
16321          * @static
16322          */
16323         verifyEl: function(el) {
16324             if (el) {
16325                 var parent;
16326                 if(Roo.isIE){
16327                     try{
16328                         parent = el.offsetParent;
16329                     }catch(e){}
16330                 }else{
16331                     parent = el.offsetParent;
16332                 }
16333                 if (parent) {
16334                     return true;
16335                 }
16336             }
16337
16338             return false;
16339         },
16340
16341         /**
16342          * Returns a Region object containing the drag and drop element's position
16343          * and size, including the padding configured for it
16344          * @method getLocation
16345          * @param {DragDrop} oDD the drag and drop object to get the
16346          *                       location for
16347          * @return {Roo.lib.Region} a Region object representing the total area
16348          *                             the element occupies, including any padding
16349          *                             the instance is configured for.
16350          * @static
16351          */
16352         getLocation: function(oDD) {
16353             if (! this.isTypeOfDD(oDD)) {
16354                 return null;
16355             }
16356
16357             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16358
16359             try {
16360                 pos= Roo.lib.Dom.getXY(el);
16361             } catch (e) { }
16362
16363             if (!pos) {
16364                 return null;
16365             }
16366
16367             x1 = pos[0];
16368             x2 = x1 + el.offsetWidth;
16369             y1 = pos[1];
16370             y2 = y1 + el.offsetHeight;
16371
16372             t = y1 - oDD.padding[0];
16373             r = x2 + oDD.padding[1];
16374             b = y2 + oDD.padding[2];
16375             l = x1 - oDD.padding[3];
16376
16377             return new Roo.lib.Region( t, r, b, l );
16378         },
16379
16380         /**
16381          * Checks the cursor location to see if it over the target
16382          * @method isOverTarget
16383          * @param {Roo.lib.Point} pt The point to evaluate
16384          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16385          * @return {boolean} true if the mouse is over the target
16386          * @private
16387          * @static
16388          */
16389         isOverTarget: function(pt, oTarget, intersect) {
16390             // use cache if available
16391             var loc = this.locationCache[oTarget.id];
16392             if (!loc || !this.useCache) {
16393                 loc = this.getLocation(oTarget);
16394                 this.locationCache[oTarget.id] = loc;
16395
16396             }
16397
16398             if (!loc) {
16399                 return false;
16400             }
16401
16402             oTarget.cursorIsOver = loc.contains( pt );
16403
16404             // DragDrop is using this as a sanity check for the initial mousedown
16405             // in this case we are done.  In POINT mode, if the drag obj has no
16406             // contraints, we are also done. Otherwise we need to evaluate the
16407             // location of the target as related to the actual location of the
16408             // dragged element.
16409             var dc = this.dragCurrent;
16410             if (!dc || !dc.getTargetCoord ||
16411                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16412                 return oTarget.cursorIsOver;
16413             }
16414
16415             oTarget.overlap = null;
16416
16417             // Get the current location of the drag element, this is the
16418             // location of the mouse event less the delta that represents
16419             // where the original mousedown happened on the element.  We
16420             // need to consider constraints and ticks as well.
16421             var pos = dc.getTargetCoord(pt.x, pt.y);
16422
16423             var el = dc.getDragEl();
16424             var curRegion = new Roo.lib.Region( pos.y,
16425                                                    pos.x + el.offsetWidth,
16426                                                    pos.y + el.offsetHeight,
16427                                                    pos.x );
16428
16429             var overlap = curRegion.intersect(loc);
16430
16431             if (overlap) {
16432                 oTarget.overlap = overlap;
16433                 return (intersect) ? true : oTarget.cursorIsOver;
16434             } else {
16435                 return false;
16436             }
16437         },
16438
16439         /**
16440          * unload event handler
16441          * @method _onUnload
16442          * @private
16443          * @static
16444          */
16445         _onUnload: function(e, me) {
16446             Roo.dd.DragDropMgr.unregAll();
16447         },
16448
16449         /**
16450          * Cleans up the drag and drop events and objects.
16451          * @method unregAll
16452          * @private
16453          * @static
16454          */
16455         unregAll: function() {
16456
16457             if (this.dragCurrent) {
16458                 this.stopDrag();
16459                 this.dragCurrent = null;
16460             }
16461
16462             this._execOnAll("unreg", []);
16463
16464             for (i in this.elementCache) {
16465                 delete this.elementCache[i];
16466             }
16467
16468             this.elementCache = {};
16469             this.ids = {};
16470         },
16471
16472         /**
16473          * A cache of DOM elements
16474          * @property elementCache
16475          * @private
16476          * @static
16477          */
16478         elementCache: {},
16479
16480         /**
16481          * Get the wrapper for the DOM element specified
16482          * @method getElWrapper
16483          * @param {String} id the id of the element to get
16484          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16485          * @private
16486          * @deprecated This wrapper isn't that useful
16487          * @static
16488          */
16489         getElWrapper: function(id) {
16490             var oWrapper = this.elementCache[id];
16491             if (!oWrapper || !oWrapper.el) {
16492                 oWrapper = this.elementCache[id] =
16493                     new this.ElementWrapper(Roo.getDom(id));
16494             }
16495             return oWrapper;
16496         },
16497
16498         /**
16499          * Returns the actual DOM element
16500          * @method getElement
16501          * @param {String} id the id of the elment to get
16502          * @return {Object} The element
16503          * @deprecated use Roo.getDom instead
16504          * @static
16505          */
16506         getElement: function(id) {
16507             return Roo.getDom(id);
16508         },
16509
16510         /**
16511          * Returns the style property for the DOM element (i.e.,
16512          * document.getElById(id).style)
16513          * @method getCss
16514          * @param {String} id the id of the elment to get
16515          * @return {Object} The style property of the element
16516          * @deprecated use Roo.getDom instead
16517          * @static
16518          */
16519         getCss: function(id) {
16520             var el = Roo.getDom(id);
16521             return (el) ? el.style : null;
16522         },
16523
16524         /**
16525          * Inner class for cached elements
16526          * @class DragDropMgr.ElementWrapper
16527          * @for DragDropMgr
16528          * @private
16529          * @deprecated
16530          */
16531         ElementWrapper: function(el) {
16532                 /**
16533                  * The element
16534                  * @property el
16535                  */
16536                 this.el = el || null;
16537                 /**
16538                  * The element id
16539                  * @property id
16540                  */
16541                 this.id = this.el && el.id;
16542                 /**
16543                  * A reference to the style property
16544                  * @property css
16545                  */
16546                 this.css = this.el && el.style;
16547             },
16548
16549         /**
16550          * Returns the X position of an html element
16551          * @method getPosX
16552          * @param el the element for which to get the position
16553          * @return {int} the X coordinate
16554          * @for DragDropMgr
16555          * @deprecated use Roo.lib.Dom.getX instead
16556          * @static
16557          */
16558         getPosX: function(el) {
16559             return Roo.lib.Dom.getX(el);
16560         },
16561
16562         /**
16563          * Returns the Y position of an html element
16564          * @method getPosY
16565          * @param el the element for which to get the position
16566          * @return {int} the Y coordinate
16567          * @deprecated use Roo.lib.Dom.getY instead
16568          * @static
16569          */
16570         getPosY: function(el) {
16571             return Roo.lib.Dom.getY(el);
16572         },
16573
16574         /**
16575          * Swap two nodes.  In IE, we use the native method, for others we
16576          * emulate the IE behavior
16577          * @method swapNode
16578          * @param n1 the first node to swap
16579          * @param n2 the other node to swap
16580          * @static
16581          */
16582         swapNode: function(n1, n2) {
16583             if (n1.swapNode) {
16584                 n1.swapNode(n2);
16585             } else {
16586                 var p = n2.parentNode;
16587                 var s = n2.nextSibling;
16588
16589                 if (s == n1) {
16590                     p.insertBefore(n1, n2);
16591                 } else if (n2 == n1.nextSibling) {
16592                     p.insertBefore(n2, n1);
16593                 } else {
16594                     n1.parentNode.replaceChild(n2, n1);
16595                     p.insertBefore(n1, s);
16596                 }
16597             }
16598         },
16599
16600         /**
16601          * Returns the current scroll position
16602          * @method getScroll
16603          * @private
16604          * @static
16605          */
16606         getScroll: function () {
16607             var t, l, dde=document.documentElement, db=document.body;
16608             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16609                 t = dde.scrollTop;
16610                 l = dde.scrollLeft;
16611             } else if (db) {
16612                 t = db.scrollTop;
16613                 l = db.scrollLeft;
16614             } else {
16615
16616             }
16617             return { top: t, left: l };
16618         },
16619
16620         /**
16621          * Returns the specified element style property
16622          * @method getStyle
16623          * @param {HTMLElement} el          the element
16624          * @param {string}      styleProp   the style property
16625          * @return {string} The value of the style property
16626          * @deprecated use Roo.lib.Dom.getStyle
16627          * @static
16628          */
16629         getStyle: function(el, styleProp) {
16630             return Roo.fly(el).getStyle(styleProp);
16631         },
16632
16633         /**
16634          * Gets the scrollTop
16635          * @method getScrollTop
16636          * @return {int} the document's scrollTop
16637          * @static
16638          */
16639         getScrollTop: function () { return this.getScroll().top; },
16640
16641         /**
16642          * Gets the scrollLeft
16643          * @method getScrollLeft
16644          * @return {int} the document's scrollTop
16645          * @static
16646          */
16647         getScrollLeft: function () { return this.getScroll().left; },
16648
16649         /**
16650          * Sets the x/y position of an element to the location of the
16651          * target element.
16652          * @method moveToEl
16653          * @param {HTMLElement} moveEl      The element to move
16654          * @param {HTMLElement} targetEl    The position reference element
16655          * @static
16656          */
16657         moveToEl: function (moveEl, targetEl) {
16658             var aCoord = Roo.lib.Dom.getXY(targetEl);
16659             Roo.lib.Dom.setXY(moveEl, aCoord);
16660         },
16661
16662         /**
16663          * Numeric array sort function
16664          * @method numericSort
16665          * @static
16666          */
16667         numericSort: function(a, b) { return (a - b); },
16668
16669         /**
16670          * Internal counter
16671          * @property _timeoutCount
16672          * @private
16673          * @static
16674          */
16675         _timeoutCount: 0,
16676
16677         /**
16678          * Trying to make the load order less important.  Without this we get
16679          * an error if this file is loaded before the Event Utility.
16680          * @method _addListeners
16681          * @private
16682          * @static
16683          */
16684         _addListeners: function() {
16685             var DDM = Roo.dd.DDM;
16686             if ( Roo.lib.Event && document ) {
16687                 DDM._onLoad();
16688             } else {
16689                 if (DDM._timeoutCount > 2000) {
16690                 } else {
16691                     setTimeout(DDM._addListeners, 10);
16692                     if (document && document.body) {
16693                         DDM._timeoutCount += 1;
16694                     }
16695                 }
16696             }
16697         },
16698
16699         /**
16700          * Recursively searches the immediate parent and all child nodes for
16701          * the handle element in order to determine wheter or not it was
16702          * clicked.
16703          * @method handleWasClicked
16704          * @param node the html element to inspect
16705          * @static
16706          */
16707         handleWasClicked: function(node, id) {
16708             if (this.isHandle(id, node.id)) {
16709                 return true;
16710             } else {
16711                 // check to see if this is a text node child of the one we want
16712                 var p = node.parentNode;
16713
16714                 while (p) {
16715                     if (this.isHandle(id, p.id)) {
16716                         return true;
16717                     } else {
16718                         p = p.parentNode;
16719                     }
16720                 }
16721             }
16722
16723             return false;
16724         }
16725
16726     };
16727
16728 }();
16729
16730 // shorter alias, save a few bytes
16731 Roo.dd.DDM = Roo.dd.DragDropMgr;
16732 Roo.dd.DDM._addListeners();
16733
16734 }/*
16735  * Based on:
16736  * Ext JS Library 1.1.1
16737  * Copyright(c) 2006-2007, Ext JS, LLC.
16738  *
16739  * Originally Released Under LGPL - original licence link has changed is not relivant.
16740  *
16741  * Fork - LGPL
16742  * <script type="text/javascript">
16743  */
16744
16745 /**
16746  * @class Roo.dd.DD
16747  * A DragDrop implementation where the linked element follows the
16748  * mouse cursor during a drag.
16749  * @extends Roo.dd.DragDrop
16750  * @constructor
16751  * @param {String} id the id of the linked element
16752  * @param {String} sGroup the group of related DragDrop items
16753  * @param {object} config an object containing configurable attributes
16754  *                Valid properties for DD:
16755  *                    scroll
16756  */
16757 Roo.dd.DD = function(id, sGroup, config) {
16758     if (id) {
16759         this.init(id, sGroup, config);
16760     }
16761 };
16762
16763 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16764
16765     /**
16766      * When set to true, the utility automatically tries to scroll the browser
16767      * window wehn a drag and drop element is dragged near the viewport boundary.
16768      * Defaults to true.
16769      * @property scroll
16770      * @type boolean
16771      */
16772     scroll: true,
16773
16774     /**
16775      * Sets the pointer offset to the distance between the linked element's top
16776      * left corner and the location the element was clicked
16777      * @method autoOffset
16778      * @param {int} iPageX the X coordinate of the click
16779      * @param {int} iPageY the Y coordinate of the click
16780      */
16781     autoOffset: function(iPageX, iPageY) {
16782         var x = iPageX - this.startPageX;
16783         var y = iPageY - this.startPageY;
16784         this.setDelta(x, y);
16785     },
16786
16787     /**
16788      * Sets the pointer offset.  You can call this directly to force the
16789      * offset to be in a particular location (e.g., pass in 0,0 to set it
16790      * to the center of the object)
16791      * @method setDelta
16792      * @param {int} iDeltaX the distance from the left
16793      * @param {int} iDeltaY the distance from the top
16794      */
16795     setDelta: function(iDeltaX, iDeltaY) {
16796         this.deltaX = iDeltaX;
16797         this.deltaY = iDeltaY;
16798     },
16799
16800     /**
16801      * Sets the drag element to the location of the mousedown or click event,
16802      * maintaining the cursor location relative to the location on the element
16803      * that was clicked.  Override this if you want to place the element in a
16804      * location other than where the cursor is.
16805      * @method setDragElPos
16806      * @param {int} iPageX the X coordinate of the mousedown or drag event
16807      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16808      */
16809     setDragElPos: function(iPageX, iPageY) {
16810         // the first time we do this, we are going to check to make sure
16811         // the element has css positioning
16812
16813         var el = this.getDragEl();
16814         this.alignElWithMouse(el, iPageX, iPageY);
16815     },
16816
16817     /**
16818      * Sets the element to the location of the mousedown or click event,
16819      * maintaining the cursor location relative to the location on the element
16820      * that was clicked.  Override this if you want to place the element in a
16821      * location other than where the cursor is.
16822      * @method alignElWithMouse
16823      * @param {HTMLElement} el the element to move
16824      * @param {int} iPageX the X coordinate of the mousedown or drag event
16825      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16826      */
16827     alignElWithMouse: function(el, iPageX, iPageY) {
16828         var oCoord = this.getTargetCoord(iPageX, iPageY);
16829         var fly = el.dom ? el : Roo.fly(el);
16830         if (!this.deltaSetXY) {
16831             var aCoord = [oCoord.x, oCoord.y];
16832             fly.setXY(aCoord);
16833             var newLeft = fly.getLeft(true);
16834             var newTop  = fly.getTop(true);
16835             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16836         } else {
16837             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16838         }
16839
16840         this.cachePosition(oCoord.x, oCoord.y);
16841         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16842         return oCoord;
16843     },
16844
16845     /**
16846      * Saves the most recent position so that we can reset the constraints and
16847      * tick marks on-demand.  We need to know this so that we can calculate the
16848      * number of pixels the element is offset from its original position.
16849      * @method cachePosition
16850      * @param iPageX the current x position (optional, this just makes it so we
16851      * don't have to look it up again)
16852      * @param iPageY the current y position (optional, this just makes it so we
16853      * don't have to look it up again)
16854      */
16855     cachePosition: function(iPageX, iPageY) {
16856         if (iPageX) {
16857             this.lastPageX = iPageX;
16858             this.lastPageY = iPageY;
16859         } else {
16860             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16861             this.lastPageX = aCoord[0];
16862             this.lastPageY = aCoord[1];
16863         }
16864     },
16865
16866     /**
16867      * Auto-scroll the window if the dragged object has been moved beyond the
16868      * visible window boundary.
16869      * @method autoScroll
16870      * @param {int} x the drag element's x position
16871      * @param {int} y the drag element's y position
16872      * @param {int} h the height of the drag element
16873      * @param {int} w the width of the drag element
16874      * @private
16875      */
16876     autoScroll: function(x, y, h, w) {
16877
16878         if (this.scroll) {
16879             // The client height
16880             var clientH = Roo.lib.Dom.getViewWidth();
16881
16882             // The client width
16883             var clientW = Roo.lib.Dom.getViewHeight();
16884
16885             // The amt scrolled down
16886             var st = this.DDM.getScrollTop();
16887
16888             // The amt scrolled right
16889             var sl = this.DDM.getScrollLeft();
16890
16891             // Location of the bottom of the element
16892             var bot = h + y;
16893
16894             // Location of the right of the element
16895             var right = w + x;
16896
16897             // The distance from the cursor to the bottom of the visible area,
16898             // adjusted so that we don't scroll if the cursor is beyond the
16899             // element drag constraints
16900             var toBot = (clientH + st - y - this.deltaY);
16901
16902             // The distance from the cursor to the right of the visible area
16903             var toRight = (clientW + sl - x - this.deltaX);
16904
16905
16906             // How close to the edge the cursor must be before we scroll
16907             // var thresh = (document.all) ? 100 : 40;
16908             var thresh = 40;
16909
16910             // How many pixels to scroll per autoscroll op.  This helps to reduce
16911             // clunky scrolling. IE is more sensitive about this ... it needs this
16912             // value to be higher.
16913             var scrAmt = (document.all) ? 80 : 30;
16914
16915             // Scroll down if we are near the bottom of the visible page and the
16916             // obj extends below the crease
16917             if ( bot > clientH && toBot < thresh ) {
16918                 window.scrollTo(sl, st + scrAmt);
16919             }
16920
16921             // Scroll up if the window is scrolled down and the top of the object
16922             // goes above the top border
16923             if ( y < st && st > 0 && y - st < thresh ) {
16924                 window.scrollTo(sl, st - scrAmt);
16925             }
16926
16927             // Scroll right if the obj is beyond the right border and the cursor is
16928             // near the border.
16929             if ( right > clientW && toRight < thresh ) {
16930                 window.scrollTo(sl + scrAmt, st);
16931             }
16932
16933             // Scroll left if the window has been scrolled to the right and the obj
16934             // extends past the left border
16935             if ( x < sl && sl > 0 && x - sl < thresh ) {
16936                 window.scrollTo(sl - scrAmt, st);
16937             }
16938         }
16939     },
16940
16941     /**
16942      * Finds the location the element should be placed if we want to move
16943      * it to where the mouse location less the click offset would place us.
16944      * @method getTargetCoord
16945      * @param {int} iPageX the X coordinate of the click
16946      * @param {int} iPageY the Y coordinate of the click
16947      * @return an object that contains the coordinates (Object.x and Object.y)
16948      * @private
16949      */
16950     getTargetCoord: function(iPageX, iPageY) {
16951
16952
16953         var x = iPageX - this.deltaX;
16954         var y = iPageY - this.deltaY;
16955
16956         if (this.constrainX) {
16957             if (x < this.minX) { x = this.minX; }
16958             if (x > this.maxX) { x = this.maxX; }
16959         }
16960
16961         if (this.constrainY) {
16962             if (y < this.minY) { y = this.minY; }
16963             if (y > this.maxY) { y = this.maxY; }
16964         }
16965
16966         x = this.getTick(x, this.xTicks);
16967         y = this.getTick(y, this.yTicks);
16968
16969
16970         return {x:x, y:y};
16971     },
16972
16973     /*
16974      * Sets up config options specific to this class. Overrides
16975      * Roo.dd.DragDrop, but all versions of this method through the
16976      * inheritance chain are called
16977      */
16978     applyConfig: function() {
16979         Roo.dd.DD.superclass.applyConfig.call(this);
16980         this.scroll = (this.config.scroll !== false);
16981     },
16982
16983     /*
16984      * Event that fires prior to the onMouseDown event.  Overrides
16985      * Roo.dd.DragDrop.
16986      */
16987     b4MouseDown: function(e) {
16988         // this.resetConstraints();
16989         this.autoOffset(e.getPageX(),
16990                             e.getPageY());
16991     },
16992
16993     /*
16994      * Event that fires prior to the onDrag event.  Overrides
16995      * Roo.dd.DragDrop.
16996      */
16997     b4Drag: function(e) {
16998         this.setDragElPos(e.getPageX(),
16999                             e.getPageY());
17000     },
17001
17002     toString: function() {
17003         return ("DD " + this.id);
17004     }
17005
17006     //////////////////////////////////////////////////////////////////////////
17007     // Debugging ygDragDrop events that can be overridden
17008     //////////////////////////////////////////////////////////////////////////
17009     /*
17010     startDrag: function(x, y) {
17011     },
17012
17013     onDrag: function(e) {
17014     },
17015
17016     onDragEnter: function(e, id) {
17017     },
17018
17019     onDragOver: function(e, id) {
17020     },
17021
17022     onDragOut: function(e, id) {
17023     },
17024
17025     onDragDrop: function(e, id) {
17026     },
17027
17028     endDrag: function(e) {
17029     }
17030
17031     */
17032
17033 });/*
17034  * Based on:
17035  * Ext JS Library 1.1.1
17036  * Copyright(c) 2006-2007, Ext JS, LLC.
17037  *
17038  * Originally Released Under LGPL - original licence link has changed is not relivant.
17039  *
17040  * Fork - LGPL
17041  * <script type="text/javascript">
17042  */
17043
17044 /**
17045  * @class Roo.dd.DDProxy
17046  * A DragDrop implementation that inserts an empty, bordered div into
17047  * the document that follows the cursor during drag operations.  At the time of
17048  * the click, the frame div is resized to the dimensions of the linked html
17049  * element, and moved to the exact location of the linked element.
17050  *
17051  * References to the "frame" element refer to the single proxy element that
17052  * was created to be dragged in place of all DDProxy elements on the
17053  * page.
17054  *
17055  * @extends Roo.dd.DD
17056  * @constructor
17057  * @param {String} id the id of the linked html element
17058  * @param {String} sGroup the group of related DragDrop objects
17059  * @param {object} config an object containing configurable attributes
17060  *                Valid properties for DDProxy in addition to those in DragDrop:
17061  *                   resizeFrame, centerFrame, dragElId
17062  */
17063 Roo.dd.DDProxy = function(id, sGroup, config) {
17064     if (id) {
17065         this.init(id, sGroup, config);
17066         this.initFrame();
17067     }
17068 };
17069
17070 /**
17071  * The default drag frame div id
17072  * @property Roo.dd.DDProxy.dragElId
17073  * @type String
17074  * @static
17075  */
17076 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17077
17078 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17079
17080     /**
17081      * By default we resize the drag frame to be the same size as the element
17082      * we want to drag (this is to get the frame effect).  We can turn it off
17083      * if we want a different behavior.
17084      * @property resizeFrame
17085      * @type boolean
17086      */
17087     resizeFrame: true,
17088
17089     /**
17090      * By default the frame is positioned exactly where the drag element is, so
17091      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17092      * you do not have constraints on the obj is to have the drag frame centered
17093      * around the cursor.  Set centerFrame to true for this effect.
17094      * @property centerFrame
17095      * @type boolean
17096      */
17097     centerFrame: false,
17098
17099     /**
17100      * Creates the proxy element if it does not yet exist
17101      * @method createFrame
17102      */
17103     createFrame: function() {
17104         var self = this;
17105         var body = document.body;
17106
17107         if (!body || !body.firstChild) {
17108             setTimeout( function() { self.createFrame(); }, 50 );
17109             return;
17110         }
17111
17112         var div = this.getDragEl();
17113
17114         if (!div) {
17115             div    = document.createElement("div");
17116             div.id = this.dragElId;
17117             var s  = div.style;
17118
17119             s.position   = "absolute";
17120             s.visibility = "hidden";
17121             s.cursor     = "move";
17122             s.border     = "2px solid #aaa";
17123             s.zIndex     = 999;
17124
17125             // appendChild can blow up IE if invoked prior to the window load event
17126             // while rendering a table.  It is possible there are other scenarios
17127             // that would cause this to happen as well.
17128             body.insertBefore(div, body.firstChild);
17129         }
17130     },
17131
17132     /**
17133      * Initialization for the drag frame element.  Must be called in the
17134      * constructor of all subclasses
17135      * @method initFrame
17136      */
17137     initFrame: function() {
17138         this.createFrame();
17139     },
17140
17141     applyConfig: function() {
17142         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17143
17144         this.resizeFrame = (this.config.resizeFrame !== false);
17145         this.centerFrame = (this.config.centerFrame);
17146         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17147     },
17148
17149     /**
17150      * Resizes the drag frame to the dimensions of the clicked object, positions
17151      * it over the object, and finally displays it
17152      * @method showFrame
17153      * @param {int} iPageX X click position
17154      * @param {int} iPageY Y click position
17155      * @private
17156      */
17157     showFrame: function(iPageX, iPageY) {
17158         var el = this.getEl();
17159         var dragEl = this.getDragEl();
17160         var s = dragEl.style;
17161
17162         this._resizeProxy();
17163
17164         if (this.centerFrame) {
17165             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17166                            Math.round(parseInt(s.height, 10)/2) );
17167         }
17168
17169         this.setDragElPos(iPageX, iPageY);
17170
17171         Roo.fly(dragEl).show();
17172     },
17173
17174     /**
17175      * The proxy is automatically resized to the dimensions of the linked
17176      * element when a drag is initiated, unless resizeFrame is set to false
17177      * @method _resizeProxy
17178      * @private
17179      */
17180     _resizeProxy: function() {
17181         if (this.resizeFrame) {
17182             var el = this.getEl();
17183             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17184         }
17185     },
17186
17187     // overrides Roo.dd.DragDrop
17188     b4MouseDown: function(e) {
17189         var x = e.getPageX();
17190         var y = e.getPageY();
17191         this.autoOffset(x, y);
17192         this.setDragElPos(x, y);
17193     },
17194
17195     // overrides Roo.dd.DragDrop
17196     b4StartDrag: function(x, y) {
17197         // show the drag frame
17198         this.showFrame(x, y);
17199     },
17200
17201     // overrides Roo.dd.DragDrop
17202     b4EndDrag: function(e) {
17203         Roo.fly(this.getDragEl()).hide();
17204     },
17205
17206     // overrides Roo.dd.DragDrop
17207     // By default we try to move the element to the last location of the frame.
17208     // This is so that the default behavior mirrors that of Roo.dd.DD.
17209     endDrag: function(e) {
17210
17211         var lel = this.getEl();
17212         var del = this.getDragEl();
17213
17214         // Show the drag frame briefly so we can get its position
17215         del.style.visibility = "";
17216
17217         this.beforeMove();
17218         // Hide the linked element before the move to get around a Safari
17219         // rendering bug.
17220         lel.style.visibility = "hidden";
17221         Roo.dd.DDM.moveToEl(lel, del);
17222         del.style.visibility = "hidden";
17223         lel.style.visibility = "";
17224
17225         this.afterDrag();
17226     },
17227
17228     beforeMove : function(){
17229
17230     },
17231
17232     afterDrag : function(){
17233
17234     },
17235
17236     toString: function() {
17237         return ("DDProxy " + this.id);
17238     }
17239
17240 });
17241 /*
17242  * Based on:
17243  * Ext JS Library 1.1.1
17244  * Copyright(c) 2006-2007, Ext JS, LLC.
17245  *
17246  * Originally Released Under LGPL - original licence link has changed is not relivant.
17247  *
17248  * Fork - LGPL
17249  * <script type="text/javascript">
17250  */
17251
17252  /**
17253  * @class Roo.dd.DDTarget
17254  * A DragDrop implementation that does not move, but can be a drop
17255  * target.  You would get the same result by simply omitting implementation
17256  * for the event callbacks, but this way we reduce the processing cost of the
17257  * event listener and the callbacks.
17258  * @extends Roo.dd.DragDrop
17259  * @constructor
17260  * @param {String} id the id of the element that is a drop target
17261  * @param {String} sGroup the group of related DragDrop objects
17262  * @param {object} config an object containing configurable attributes
17263  *                 Valid properties for DDTarget in addition to those in
17264  *                 DragDrop:
17265  *                    none
17266  */
17267 Roo.dd.DDTarget = function(id, sGroup, config) {
17268     if (id) {
17269         this.initTarget(id, sGroup, config);
17270     }
17271 };
17272
17273 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17274 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17275     toString: function() {
17276         return ("DDTarget " + this.id);
17277     }
17278 });
17279 /*
17280  * Based on:
17281  * Ext JS Library 1.1.1
17282  * Copyright(c) 2006-2007, Ext JS, LLC.
17283  *
17284  * Originally Released Under LGPL - original licence link has changed is not relivant.
17285  *
17286  * Fork - LGPL
17287  * <script type="text/javascript">
17288  */
17289  
17290
17291 /**
17292  * @class Roo.dd.ScrollManager
17293  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17294  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17295  * @singleton
17296  */
17297 Roo.dd.ScrollManager = function(){
17298     var ddm = Roo.dd.DragDropMgr;
17299     var els = {};
17300     var dragEl = null;
17301     var proc = {};
17302     
17303     var onStop = function(e){
17304         dragEl = null;
17305         clearProc();
17306     };
17307     
17308     var triggerRefresh = function(){
17309         if(ddm.dragCurrent){
17310              ddm.refreshCache(ddm.dragCurrent.groups);
17311         }
17312     };
17313     
17314     var doScroll = function(){
17315         if(ddm.dragCurrent){
17316             var dds = Roo.dd.ScrollManager;
17317             if(!dds.animate){
17318                 if(proc.el.scroll(proc.dir, dds.increment)){
17319                     triggerRefresh();
17320                 }
17321             }else{
17322                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17323             }
17324         }
17325     };
17326     
17327     var clearProc = function(){
17328         if(proc.id){
17329             clearInterval(proc.id);
17330         }
17331         proc.id = 0;
17332         proc.el = null;
17333         proc.dir = "";
17334     };
17335     
17336     var startProc = function(el, dir){
17337         clearProc();
17338         proc.el = el;
17339         proc.dir = dir;
17340         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17341     };
17342     
17343     var onFire = function(e, isDrop){
17344         if(isDrop || !ddm.dragCurrent){ return; }
17345         var dds = Roo.dd.ScrollManager;
17346         if(!dragEl || dragEl != ddm.dragCurrent){
17347             dragEl = ddm.dragCurrent;
17348             // refresh regions on drag start
17349             dds.refreshCache();
17350         }
17351         
17352         var xy = Roo.lib.Event.getXY(e);
17353         var pt = new Roo.lib.Point(xy[0], xy[1]);
17354         for(var id in els){
17355             var el = els[id], r = el._region;
17356             if(r && r.contains(pt) && el.isScrollable()){
17357                 if(r.bottom - pt.y <= dds.thresh){
17358                     if(proc.el != el){
17359                         startProc(el, "down");
17360                     }
17361                     return;
17362                 }else if(r.right - pt.x <= dds.thresh){
17363                     if(proc.el != el){
17364                         startProc(el, "left");
17365                     }
17366                     return;
17367                 }else if(pt.y - r.top <= dds.thresh){
17368                     if(proc.el != el){
17369                         startProc(el, "up");
17370                     }
17371                     return;
17372                 }else if(pt.x - r.left <= dds.thresh){
17373                     if(proc.el != el){
17374                         startProc(el, "right");
17375                     }
17376                     return;
17377                 }
17378             }
17379         }
17380         clearProc();
17381     };
17382     
17383     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17384     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17385     
17386     return {
17387         /**
17388          * Registers new overflow element(s) to auto scroll
17389          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17390          */
17391         register : function(el){
17392             if(el instanceof Array){
17393                 for(var i = 0, len = el.length; i < len; i++) {
17394                         this.register(el[i]);
17395                 }
17396             }else{
17397                 el = Roo.get(el);
17398                 els[el.id] = el;
17399             }
17400         },
17401         
17402         /**
17403          * Unregisters overflow element(s) so they are no longer scrolled
17404          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17405          */
17406         unregister : function(el){
17407             if(el instanceof Array){
17408                 for(var i = 0, len = el.length; i < len; i++) {
17409                         this.unregister(el[i]);
17410                 }
17411             }else{
17412                 el = Roo.get(el);
17413                 delete els[el.id];
17414             }
17415         },
17416         
17417         /**
17418          * The number of pixels from the edge of a container the pointer needs to be to 
17419          * trigger scrolling (defaults to 25)
17420          * @type Number
17421          */
17422         thresh : 25,
17423         
17424         /**
17425          * The number of pixels to scroll in each scroll increment (defaults to 50)
17426          * @type Number
17427          */
17428         increment : 100,
17429         
17430         /**
17431          * The frequency of scrolls in milliseconds (defaults to 500)
17432          * @type Number
17433          */
17434         frequency : 500,
17435         
17436         /**
17437          * True to animate the scroll (defaults to true)
17438          * @type Boolean
17439          */
17440         animate: true,
17441         
17442         /**
17443          * The animation duration in seconds - 
17444          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17445          * @type Number
17446          */
17447         animDuration: .4,
17448         
17449         /**
17450          * Manually trigger a cache refresh.
17451          */
17452         refreshCache : function(){
17453             for(var id in els){
17454                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17455                     els[id]._region = els[id].getRegion();
17456                 }
17457             }
17458         }
17459     };
17460 }();/*
17461  * Based on:
17462  * Ext JS Library 1.1.1
17463  * Copyright(c) 2006-2007, Ext JS, LLC.
17464  *
17465  * Originally Released Under LGPL - original licence link has changed is not relivant.
17466  *
17467  * Fork - LGPL
17468  * <script type="text/javascript">
17469  */
17470  
17471
17472 /**
17473  * @class Roo.dd.Registry
17474  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17475  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17476  * @singleton
17477  */
17478 Roo.dd.Registry = function(){
17479     var elements = {}; 
17480     var handles = {}; 
17481     var autoIdSeed = 0;
17482
17483     var getId = function(el, autogen){
17484         if(typeof el == "string"){
17485             return el;
17486         }
17487         var id = el.id;
17488         if(!id && autogen !== false){
17489             id = "roodd-" + (++autoIdSeed);
17490             el.id = id;
17491         }
17492         return id;
17493     };
17494     
17495     return {
17496     /**
17497      * Register a drag drop element
17498      * @param {String|HTMLElement} element The id or DOM node to register
17499      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17500      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17501      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17502      * populated in the data object (if applicable):
17503      * <pre>
17504 Value      Description<br />
17505 ---------  ------------------------------------------<br />
17506 handles    Array of DOM nodes that trigger dragging<br />
17507            for the element being registered<br />
17508 isHandle   True if the element passed in triggers<br />
17509            dragging itself, else false
17510 </pre>
17511      */
17512         register : function(el, data){
17513             data = data || {};
17514             if(typeof el == "string"){
17515                 el = document.getElementById(el);
17516             }
17517             data.ddel = el;
17518             elements[getId(el)] = data;
17519             if(data.isHandle !== false){
17520                 handles[data.ddel.id] = data;
17521             }
17522             if(data.handles){
17523                 var hs = data.handles;
17524                 for(var i = 0, len = hs.length; i < len; i++){
17525                         handles[getId(hs[i])] = data;
17526                 }
17527             }
17528         },
17529
17530     /**
17531      * Unregister a drag drop element
17532      * @param {String|HTMLElement}  element The id or DOM node to unregister
17533      */
17534         unregister : function(el){
17535             var id = getId(el, false);
17536             var data = elements[id];
17537             if(data){
17538                 delete elements[id];
17539                 if(data.handles){
17540                     var hs = data.handles;
17541                     for(var i = 0, len = hs.length; i < len; i++){
17542                         delete handles[getId(hs[i], false)];
17543                     }
17544                 }
17545             }
17546         },
17547
17548     /**
17549      * Returns the handle registered for a DOM Node by id
17550      * @param {String|HTMLElement} id The DOM node or id to look up
17551      * @return {Object} handle The custom handle data
17552      */
17553         getHandle : function(id){
17554             if(typeof id != "string"){ // must be element?
17555                 id = id.id;
17556             }
17557             return handles[id];
17558         },
17559
17560     /**
17561      * Returns the handle that is registered for the DOM node that is the target of the event
17562      * @param {Event} e The event
17563      * @return {Object} handle The custom handle data
17564      */
17565         getHandleFromEvent : function(e){
17566             var t = Roo.lib.Event.getTarget(e);
17567             return t ? handles[t.id] : null;
17568         },
17569
17570     /**
17571      * Returns a custom data object that is registered for a DOM node by id
17572      * @param {String|HTMLElement} id The DOM node or id to look up
17573      * @return {Object} data The custom data
17574      */
17575         getTarget : function(id){
17576             if(typeof id != "string"){ // must be element?
17577                 id = id.id;
17578             }
17579             return elements[id];
17580         },
17581
17582     /**
17583      * Returns a custom data object that is registered for the DOM node that is the target of the event
17584      * @param {Event} e The event
17585      * @return {Object} data The custom data
17586      */
17587         getTargetFromEvent : function(e){
17588             var t = Roo.lib.Event.getTarget(e);
17589             return t ? elements[t.id] || handles[t.id] : null;
17590         }
17591     };
17592 }();/*
17593  * Based on:
17594  * Ext JS Library 1.1.1
17595  * Copyright(c) 2006-2007, Ext JS, LLC.
17596  *
17597  * Originally Released Under LGPL - original licence link has changed is not relivant.
17598  *
17599  * Fork - LGPL
17600  * <script type="text/javascript">
17601  */
17602  
17603
17604 /**
17605  * @class Roo.dd.StatusProxy
17606  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17607  * default drag proxy used by all Roo.dd components.
17608  * @constructor
17609  * @param {Object} config
17610  */
17611 Roo.dd.StatusProxy = function(config){
17612     Roo.apply(this, config);
17613     this.id = this.id || Roo.id();
17614     this.el = new Roo.Layer({
17615         dh: {
17616             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17617                 {tag: "div", cls: "x-dd-drop-icon"},
17618                 {tag: "div", cls: "x-dd-drag-ghost"}
17619             ]
17620         }, 
17621         shadow: !config || config.shadow !== false
17622     });
17623     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17624     this.dropStatus = this.dropNotAllowed;
17625 };
17626
17627 Roo.dd.StatusProxy.prototype = {
17628     /**
17629      * @cfg {String} dropAllowed
17630      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17631      */
17632     dropAllowed : "x-dd-drop-ok",
17633     /**
17634      * @cfg {String} dropNotAllowed
17635      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17636      */
17637     dropNotAllowed : "x-dd-drop-nodrop",
17638
17639     /**
17640      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17641      * over the current target element.
17642      * @param {String} cssClass The css class for the new drop status indicator image
17643      */
17644     setStatus : function(cssClass){
17645         cssClass = cssClass || this.dropNotAllowed;
17646         if(this.dropStatus != cssClass){
17647             this.el.replaceClass(this.dropStatus, cssClass);
17648             this.dropStatus = cssClass;
17649         }
17650     },
17651
17652     /**
17653      * Resets the status indicator to the default dropNotAllowed value
17654      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17655      */
17656     reset : function(clearGhost){
17657         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17658         this.dropStatus = this.dropNotAllowed;
17659         if(clearGhost){
17660             this.ghost.update("");
17661         }
17662     },
17663
17664     /**
17665      * Updates the contents of the ghost element
17666      * @param {String} html The html that will replace the current innerHTML of the ghost element
17667      */
17668     update : function(html){
17669         if(typeof html == "string"){
17670             this.ghost.update(html);
17671         }else{
17672             this.ghost.update("");
17673             html.style.margin = "0";
17674             this.ghost.dom.appendChild(html);
17675         }
17676         // ensure float = none set?? cant remember why though.
17677         var el = this.ghost.dom.firstChild;
17678                 if(el){
17679                         Roo.fly(el).setStyle('float', 'none');
17680                 }
17681     },
17682     
17683     /**
17684      * Returns the underlying proxy {@link Roo.Layer}
17685      * @return {Roo.Layer} el
17686     */
17687     getEl : function(){
17688         return this.el;
17689     },
17690
17691     /**
17692      * Returns the ghost element
17693      * @return {Roo.Element} el
17694      */
17695     getGhost : function(){
17696         return this.ghost;
17697     },
17698
17699     /**
17700      * Hides the proxy
17701      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17702      */
17703     hide : function(clear){
17704         this.el.hide();
17705         if(clear){
17706             this.reset(true);
17707         }
17708     },
17709
17710     /**
17711      * Stops the repair animation if it's currently running
17712      */
17713     stop : function(){
17714         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17715             this.anim.stop();
17716         }
17717     },
17718
17719     /**
17720      * Displays this proxy
17721      */
17722     show : function(){
17723         this.el.show();
17724     },
17725
17726     /**
17727      * Force the Layer to sync its shadow and shim positions to the element
17728      */
17729     sync : function(){
17730         this.el.sync();
17731     },
17732
17733     /**
17734      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17735      * invalid drop operation by the item being dragged.
17736      * @param {Array} xy The XY position of the element ([x, y])
17737      * @param {Function} callback The function to call after the repair is complete
17738      * @param {Object} scope The scope in which to execute the callback
17739      */
17740     repair : function(xy, callback, scope){
17741         this.callback = callback;
17742         this.scope = scope;
17743         if(xy && this.animRepair !== false){
17744             this.el.addClass("x-dd-drag-repair");
17745             this.el.hideUnders(true);
17746             this.anim = this.el.shift({
17747                 duration: this.repairDuration || .5,
17748                 easing: 'easeOut',
17749                 xy: xy,
17750                 stopFx: true,
17751                 callback: this.afterRepair,
17752                 scope: this
17753             });
17754         }else{
17755             this.afterRepair();
17756         }
17757     },
17758
17759     // private
17760     afterRepair : function(){
17761         this.hide(true);
17762         if(typeof this.callback == "function"){
17763             this.callback.call(this.scope || this);
17764         }
17765         this.callback = null;
17766         this.scope = null;
17767     }
17768 };/*
17769  * Based on:
17770  * Ext JS Library 1.1.1
17771  * Copyright(c) 2006-2007, Ext JS, LLC.
17772  *
17773  * Originally Released Under LGPL - original licence link has changed is not relivant.
17774  *
17775  * Fork - LGPL
17776  * <script type="text/javascript">
17777  */
17778
17779 /**
17780  * @class Roo.dd.DragSource
17781  * @extends Roo.dd.DDProxy
17782  * A simple class that provides the basic implementation needed to make any element draggable.
17783  * @constructor
17784  * @param {String/HTMLElement/Element} el The container element
17785  * @param {Object} config
17786  */
17787 Roo.dd.DragSource = function(el, config){
17788     this.el = Roo.get(el);
17789     this.dragData = {};
17790     
17791     Roo.apply(this, config);
17792     
17793     if(!this.proxy){
17794         this.proxy = new Roo.dd.StatusProxy();
17795     }
17796
17797     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17798           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17799     
17800     this.dragging = false;
17801 };
17802
17803 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17804     /**
17805      * @cfg {String} dropAllowed
17806      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17807      */
17808     dropAllowed : "x-dd-drop-ok",
17809     /**
17810      * @cfg {String} dropNotAllowed
17811      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17812      */
17813     dropNotAllowed : "x-dd-drop-nodrop",
17814
17815     /**
17816      * Returns the data object associated with this drag source
17817      * @return {Object} data An object containing arbitrary data
17818      */
17819     getDragData : function(e){
17820         return this.dragData;
17821     },
17822
17823     // private
17824     onDragEnter : function(e, id){
17825         var target = Roo.dd.DragDropMgr.getDDById(id);
17826         this.cachedTarget = target;
17827         if(this.beforeDragEnter(target, e, id) !== false){
17828             if(target.isNotifyTarget){
17829                 var status = target.notifyEnter(this, e, this.dragData);
17830                 this.proxy.setStatus(status);
17831             }else{
17832                 this.proxy.setStatus(this.dropAllowed);
17833             }
17834             
17835             if(this.afterDragEnter){
17836                 /**
17837                  * An empty function by default, but provided so that you can perform a custom action
17838                  * when the dragged item enters the drop target by providing an implementation.
17839                  * @param {Roo.dd.DragDrop} target The drop target
17840                  * @param {Event} e The event object
17841                  * @param {String} id The id of the dragged element
17842                  * @method afterDragEnter
17843                  */
17844                 this.afterDragEnter(target, e, id);
17845             }
17846         }
17847     },
17848
17849     /**
17850      * An empty function by default, but provided so that you can perform a custom action
17851      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17852      * @param {Roo.dd.DragDrop} target The drop target
17853      * @param {Event} e The event object
17854      * @param {String} id The id of the dragged element
17855      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17856      */
17857     beforeDragEnter : function(target, e, id){
17858         return true;
17859     },
17860
17861     // private
17862     alignElWithMouse: function() {
17863         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17864         this.proxy.sync();
17865     },
17866
17867     // private
17868     onDragOver : function(e, id){
17869         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17870         if(this.beforeDragOver(target, e, id) !== false){
17871             if(target.isNotifyTarget){
17872                 var status = target.notifyOver(this, e, this.dragData);
17873                 this.proxy.setStatus(status);
17874             }
17875
17876             if(this.afterDragOver){
17877                 /**
17878                  * An empty function by default, but provided so that you can perform a custom action
17879                  * while the dragged item is over the drop target by providing an implementation.
17880                  * @param {Roo.dd.DragDrop} target The drop target
17881                  * @param {Event} e The event object
17882                  * @param {String} id The id of the dragged element
17883                  * @method afterDragOver
17884                  */
17885                 this.afterDragOver(target, e, id);
17886             }
17887         }
17888     },
17889
17890     /**
17891      * An empty function by default, but provided so that you can perform a custom action
17892      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17893      * @param {Roo.dd.DragDrop} target The drop target
17894      * @param {Event} e The event object
17895      * @param {String} id The id of the dragged element
17896      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17897      */
17898     beforeDragOver : function(target, e, id){
17899         return true;
17900     },
17901
17902     // private
17903     onDragOut : function(e, id){
17904         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17905         if(this.beforeDragOut(target, e, id) !== false){
17906             if(target.isNotifyTarget){
17907                 target.notifyOut(this, e, this.dragData);
17908             }
17909             this.proxy.reset();
17910             if(this.afterDragOut){
17911                 /**
17912                  * An empty function by default, but provided so that you can perform a custom action
17913                  * after the dragged item is dragged out of the target without dropping.
17914                  * @param {Roo.dd.DragDrop} target The drop target
17915                  * @param {Event} e The event object
17916                  * @param {String} id The id of the dragged element
17917                  * @method afterDragOut
17918                  */
17919                 this.afterDragOut(target, e, id);
17920             }
17921         }
17922         this.cachedTarget = null;
17923     },
17924
17925     /**
17926      * An empty function by default, but provided so that you can perform a custom action before the dragged
17927      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17928      * @param {Roo.dd.DragDrop} target The drop target
17929      * @param {Event} e The event object
17930      * @param {String} id The id of the dragged element
17931      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17932      */
17933     beforeDragOut : function(target, e, id){
17934         return true;
17935     },
17936     
17937     // private
17938     onDragDrop : function(e, id){
17939         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17940         if(this.beforeDragDrop(target, e, id) !== false){
17941             if(target.isNotifyTarget){
17942                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17943                     this.onValidDrop(target, e, id);
17944                 }else{
17945                     this.onInvalidDrop(target, e, id);
17946                 }
17947             }else{
17948                 this.onValidDrop(target, e, id);
17949             }
17950             
17951             if(this.afterDragDrop){
17952                 /**
17953                  * An empty function by default, but provided so that you can perform a custom action
17954                  * after a valid drag drop has occurred by providing an implementation.
17955                  * @param {Roo.dd.DragDrop} target The drop target
17956                  * @param {Event} e The event object
17957                  * @param {String} id The id of the dropped element
17958                  * @method afterDragDrop
17959                  */
17960                 this.afterDragDrop(target, e, id);
17961             }
17962         }
17963         delete this.cachedTarget;
17964     },
17965
17966     /**
17967      * An empty function by default, but provided so that you can perform a custom action before the dragged
17968      * item is dropped onto the target and optionally cancel the onDragDrop.
17969      * @param {Roo.dd.DragDrop} target The drop target
17970      * @param {Event} e The event object
17971      * @param {String} id The id of the dragged element
17972      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17973      */
17974     beforeDragDrop : function(target, e, id){
17975         return true;
17976     },
17977
17978     // private
17979     onValidDrop : function(target, e, id){
17980         this.hideProxy();
17981         if(this.afterValidDrop){
17982             /**
17983              * An empty function by default, but provided so that you can perform a custom action
17984              * after a valid drop has occurred by providing an implementation.
17985              * @param {Object} target The target DD 
17986              * @param {Event} e The event object
17987              * @param {String} id The id of the dropped element
17988              * @method afterInvalidDrop
17989              */
17990             this.afterValidDrop(target, e, id);
17991         }
17992     },
17993
17994     // private
17995     getRepairXY : function(e, data){
17996         return this.el.getXY();  
17997     },
17998
17999     // private
18000     onInvalidDrop : function(target, e, id){
18001         this.beforeInvalidDrop(target, e, id);
18002         if(this.cachedTarget){
18003             if(this.cachedTarget.isNotifyTarget){
18004                 this.cachedTarget.notifyOut(this, e, this.dragData);
18005             }
18006             this.cacheTarget = null;
18007         }
18008         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18009
18010         if(this.afterInvalidDrop){
18011             /**
18012              * An empty function by default, but provided so that you can perform a custom action
18013              * after an invalid drop has occurred by providing an implementation.
18014              * @param {Event} e The event object
18015              * @param {String} id The id of the dropped element
18016              * @method afterInvalidDrop
18017              */
18018             this.afterInvalidDrop(e, id);
18019         }
18020     },
18021
18022     // private
18023     afterRepair : function(){
18024         if(Roo.enableFx){
18025             this.el.highlight(this.hlColor || "c3daf9");
18026         }
18027         this.dragging = false;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action after an invalid
18032      * drop has occurred.
18033      * @param {Roo.dd.DragDrop} target The drop target
18034      * @param {Event} e The event object
18035      * @param {String} id The id of the dragged element
18036      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18037      */
18038     beforeInvalidDrop : function(target, e, id){
18039         return true;
18040     },
18041
18042     // private
18043     handleMouseDown : function(e){
18044         if(this.dragging) {
18045             return;
18046         }
18047         var data = this.getDragData(e);
18048         if(data && this.onBeforeDrag(data, e) !== false){
18049             this.dragData = data;
18050             this.proxy.stop();
18051             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18052         } 
18053     },
18054
18055     /**
18056      * An empty function by default, but provided so that you can perform a custom action before the initial
18057      * drag event begins and optionally cancel it.
18058      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18059      * @param {Event} e The event object
18060      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18061      */
18062     onBeforeDrag : function(data, e){
18063         return true;
18064     },
18065
18066     /**
18067      * An empty function by default, but provided so that you can perform a custom action once the initial
18068      * drag event has begun.  The drag cannot be canceled from this function.
18069      * @param {Number} x The x position of the click on the dragged object
18070      * @param {Number} y The y position of the click on the dragged object
18071      */
18072     onStartDrag : Roo.emptyFn,
18073
18074     // private - YUI override
18075     startDrag : function(x, y){
18076         this.proxy.reset();
18077         this.dragging = true;
18078         this.proxy.update("");
18079         this.onInitDrag(x, y);
18080         this.proxy.show();
18081     },
18082
18083     // private
18084     onInitDrag : function(x, y){
18085         var clone = this.el.dom.cloneNode(true);
18086         clone.id = Roo.id(); // prevent duplicate ids
18087         this.proxy.update(clone);
18088         this.onStartDrag(x, y);
18089         return true;
18090     },
18091
18092     /**
18093      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18094      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18095      */
18096     getProxy : function(){
18097         return this.proxy;  
18098     },
18099
18100     /**
18101      * Hides the drag source's {@link Roo.dd.StatusProxy}
18102      */
18103     hideProxy : function(){
18104         this.proxy.hide();  
18105         this.proxy.reset(true);
18106         this.dragging = false;
18107     },
18108
18109     // private
18110     triggerCacheRefresh : function(){
18111         Roo.dd.DDM.refreshCache(this.groups);
18112     },
18113
18114     // private - override to prevent hiding
18115     b4EndDrag: function(e) {
18116     },
18117
18118     // private - override to prevent moving
18119     endDrag : function(e){
18120         this.onEndDrag(this.dragData, e);
18121     },
18122
18123     // private
18124     onEndDrag : function(data, e){
18125     },
18126     
18127     // private - pin to cursor
18128     autoOffset : function(x, y) {
18129         this.setDelta(-12, -20);
18130     }    
18131 });/*
18132  * Based on:
18133  * Ext JS Library 1.1.1
18134  * Copyright(c) 2006-2007, Ext JS, LLC.
18135  *
18136  * Originally Released Under LGPL - original licence link has changed is not relivant.
18137  *
18138  * Fork - LGPL
18139  * <script type="text/javascript">
18140  */
18141
18142
18143 /**
18144  * @class Roo.dd.DropTarget
18145  * @extends Roo.dd.DDTarget
18146  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18147  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18148  * @constructor
18149  * @param {String/HTMLElement/Element} el The container element
18150  * @param {Object} config
18151  */
18152 Roo.dd.DropTarget = function(el, config){
18153     this.el = Roo.get(el);
18154     
18155     Roo.apply(this, config);
18156     
18157     if(this.containerScroll){
18158         Roo.dd.ScrollManager.register(this.el);
18159     }
18160     
18161     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18162           {isTarget: true});
18163
18164 };
18165
18166 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18167     /**
18168      * @cfg {String} overClass
18169      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18170      */
18171     /**
18172      * @cfg {String} dropAllowed
18173      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18174      */
18175     dropAllowed : "x-dd-drop-ok",
18176     /**
18177      * @cfg {String} dropNotAllowed
18178      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18179      */
18180     dropNotAllowed : "x-dd-drop-nodrop",
18181
18182     // private
18183     isTarget : true,
18184
18185     // private
18186     isNotifyTarget : true,
18187
18188     /**
18189      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18190      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18191      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18192      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18193      * @param {Event} e The event
18194      * @param {Object} data An object containing arbitrary data supplied by the drag source
18195      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18196      * underlying {@link Roo.dd.StatusProxy} can be updated
18197      */
18198     notifyEnter : function(dd, e, data){
18199         if(this.overClass){
18200             this.el.addClass(this.overClass);
18201         }
18202         return this.dropAllowed;
18203     },
18204
18205     /**
18206      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18207      * This method will be called on every mouse movement while the drag source is over the drop target.
18208      * This default implementation simply returns the dropAllowed config value.
18209      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18210      * @param {Event} e The event
18211      * @param {Object} data An object containing arbitrary data supplied by the drag source
18212      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18213      * underlying {@link Roo.dd.StatusProxy} can be updated
18214      */
18215     notifyOver : function(dd, e, data){
18216         return this.dropAllowed;
18217     },
18218
18219     /**
18220      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18221      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18222      * overClass (if any) from the drop element.
18223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18224      * @param {Event} e The event
18225      * @param {Object} data An object containing arbitrary data supplied by the drag source
18226      */
18227     notifyOut : function(dd, e, data){
18228         if(this.overClass){
18229             this.el.removeClass(this.overClass);
18230         }
18231     },
18232
18233     /**
18234      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18235      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18236      * implementation that does something to process the drop event and returns true so that the drag source's
18237      * repair action does not run.
18238      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18239      * @param {Event} e The event
18240      * @param {Object} data An object containing arbitrary data supplied by the drag source
18241      * @return {Boolean} True if the drop was valid, else false
18242      */
18243     notifyDrop : function(dd, e, data){
18244         return false;
18245     }
18246 });/*
18247  * Based on:
18248  * Ext JS Library 1.1.1
18249  * Copyright(c) 2006-2007, Ext JS, LLC.
18250  *
18251  * Originally Released Under LGPL - original licence link has changed is not relivant.
18252  *
18253  * Fork - LGPL
18254  * <script type="text/javascript">
18255  */
18256
18257
18258 /**
18259  * @class Roo.dd.DragZone
18260  * @extends Roo.dd.DragSource
18261  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18262  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18263  * @constructor
18264  * @param {String/HTMLElement/Element} el The container element
18265  * @param {Object} config
18266  */
18267 Roo.dd.DragZone = function(el, config){
18268     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18269     if(this.containerScroll){
18270         Roo.dd.ScrollManager.register(this.el);
18271     }
18272 };
18273
18274 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18275     /**
18276      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18277      * for auto scrolling during drag operations.
18278      */
18279     /**
18280      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18281      * method after a failed drop (defaults to "c3daf9" - light blue)
18282      */
18283
18284     /**
18285      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18286      * for a valid target to drag based on the mouse down. Override this method
18287      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18288      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18289      * @param {EventObject} e The mouse down event
18290      * @return {Object} The dragData
18291      */
18292     getDragData : function(e){
18293         return Roo.dd.Registry.getHandleFromEvent(e);
18294     },
18295     
18296     /**
18297      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18298      * this.dragData.ddel
18299      * @param {Number} x The x position of the click on the dragged object
18300      * @param {Number} y The y position of the click on the dragged object
18301      * @return {Boolean} true to continue the drag, false to cancel
18302      */
18303     onInitDrag : function(x, y){
18304         this.proxy.update(this.dragData.ddel.cloneNode(true));
18305         this.onStartDrag(x, y);
18306         return true;
18307     },
18308     
18309     /**
18310      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18311      */
18312     afterRepair : function(){
18313         if(Roo.enableFx){
18314             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18315         }
18316         this.dragging = false;
18317     },
18318
18319     /**
18320      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18321      * the XY of this.dragData.ddel
18322      * @param {EventObject} e The mouse up event
18323      * @return {Array} The xy location (e.g. [100, 200])
18324      */
18325     getRepairXY : function(e){
18326         return Roo.Element.fly(this.dragData.ddel).getXY();  
18327     }
18328 });/*
18329  * Based on:
18330  * Ext JS Library 1.1.1
18331  * Copyright(c) 2006-2007, Ext JS, LLC.
18332  *
18333  * Originally Released Under LGPL - original licence link has changed is not relivant.
18334  *
18335  * Fork - LGPL
18336  * <script type="text/javascript">
18337  */
18338 /**
18339  * @class Roo.dd.DropZone
18340  * @extends Roo.dd.DropTarget
18341  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18342  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18343  * @constructor
18344  * @param {String/HTMLElement/Element} el The container element
18345  * @param {Object} config
18346  */
18347 Roo.dd.DropZone = function(el, config){
18348     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18349 };
18350
18351 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18352     /**
18353      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18354      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18355      * provide your own custom lookup.
18356      * @param {Event} e The event
18357      * @return {Object} data The custom data
18358      */
18359     getTargetFromEvent : function(e){
18360         return Roo.dd.Registry.getTargetFromEvent(e);
18361     },
18362
18363     /**
18364      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18365      * that it has registered.  This method has no default implementation and should be overridden to provide
18366      * node-specific processing if necessary.
18367      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18368      * {@link #getTargetFromEvent} for this node)
18369      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18370      * @param {Event} e The event
18371      * @param {Object} data An object containing arbitrary data supplied by the drag source
18372      */
18373     onNodeEnter : function(n, dd, e, data){
18374         
18375     },
18376
18377     /**
18378      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18379      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18380      * overridden to provide the proper feedback.
18381      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18382      * {@link #getTargetFromEvent} for this node)
18383      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18384      * @param {Event} e The event
18385      * @param {Object} data An object containing arbitrary data supplied by the drag source
18386      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18387      * underlying {@link Roo.dd.StatusProxy} can be updated
18388      */
18389     onNodeOver : function(n, dd, e, data){
18390         return this.dropAllowed;
18391     },
18392
18393     /**
18394      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18395      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18396      * node-specific processing if necessary.
18397      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18398      * {@link #getTargetFromEvent} for this node)
18399      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18400      * @param {Event} e The event
18401      * @param {Object} data An object containing arbitrary data supplied by the drag source
18402      */
18403     onNodeOut : function(n, dd, e, data){
18404         
18405     },
18406
18407     /**
18408      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18409      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18410      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18411      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18412      * {@link #getTargetFromEvent} for this node)
18413      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18414      * @param {Event} e The event
18415      * @param {Object} data An object containing arbitrary data supplied by the drag source
18416      * @return {Boolean} True if the drop was valid, else false
18417      */
18418     onNodeDrop : function(n, dd, e, data){
18419         return false;
18420     },
18421
18422     /**
18423      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18424      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18425      * it should be overridden to provide the proper feedback if necessary.
18426      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18427      * @param {Event} e The event
18428      * @param {Object} data An object containing arbitrary data supplied by the drag source
18429      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18430      * underlying {@link Roo.dd.StatusProxy} can be updated
18431      */
18432     onContainerOver : function(dd, e, data){
18433         return this.dropNotAllowed;
18434     },
18435
18436     /**
18437      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18438      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18439      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18440      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18441      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18442      * @param {Event} e The event
18443      * @param {Object} data An object containing arbitrary data supplied by the drag source
18444      * @return {Boolean} True if the drop was valid, else false
18445      */
18446     onContainerDrop : function(dd, e, data){
18447         return false;
18448     },
18449
18450     /**
18451      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18452      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18453      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18454      * you should override this method and provide a custom implementation.
18455      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18456      * @param {Event} e The event
18457      * @param {Object} data An object containing arbitrary data supplied by the drag source
18458      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18459      * underlying {@link Roo.dd.StatusProxy} can be updated
18460      */
18461     notifyEnter : function(dd, e, data){
18462         return this.dropNotAllowed;
18463     },
18464
18465     /**
18466      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18467      * This method will be called on every mouse movement while the drag source is over the drop zone.
18468      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18469      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18470      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18471      * registered node, it will call {@link #onContainerOver}.
18472      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18473      * @param {Event} e The event
18474      * @param {Object} data An object containing arbitrary data supplied by the drag source
18475      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18476      * underlying {@link Roo.dd.StatusProxy} can be updated
18477      */
18478     notifyOver : function(dd, e, data){
18479         var n = this.getTargetFromEvent(e);
18480         if(!n){ // not over valid drop target
18481             if(this.lastOverNode){
18482                 this.onNodeOut(this.lastOverNode, dd, e, data);
18483                 this.lastOverNode = null;
18484             }
18485             return this.onContainerOver(dd, e, data);
18486         }
18487         if(this.lastOverNode != n){
18488             if(this.lastOverNode){
18489                 this.onNodeOut(this.lastOverNode, dd, e, data);
18490             }
18491             this.onNodeEnter(n, dd, e, data);
18492             this.lastOverNode = n;
18493         }
18494         return this.onNodeOver(n, dd, e, data);
18495     },
18496
18497     /**
18498      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18499      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18500      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18501      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18502      * @param {Event} e The event
18503      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18504      */
18505     notifyOut : function(dd, e, data){
18506         if(this.lastOverNode){
18507             this.onNodeOut(this.lastOverNode, dd, e, data);
18508             this.lastOverNode = null;
18509         }
18510     },
18511
18512     /**
18513      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18514      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18515      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18516      * otherwise it will call {@link #onContainerDrop}.
18517      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18518      * @param {Event} e The event
18519      * @param {Object} data An object containing arbitrary data supplied by the drag source
18520      * @return {Boolean} True if the drop was valid, else false
18521      */
18522     notifyDrop : function(dd, e, data){
18523         if(this.lastOverNode){
18524             this.onNodeOut(this.lastOverNode, dd, e, data);
18525             this.lastOverNode = null;
18526         }
18527         var n = this.getTargetFromEvent(e);
18528         return n ?
18529             this.onNodeDrop(n, dd, e, data) :
18530             this.onContainerDrop(dd, e, data);
18531     },
18532
18533     // private
18534     triggerCacheRefresh : function(){
18535         Roo.dd.DDM.refreshCache(this.groups);
18536     }  
18537 });/*
18538  * Based on:
18539  * Ext JS Library 1.1.1
18540  * Copyright(c) 2006-2007, Ext JS, LLC.
18541  *
18542  * Originally Released Under LGPL - original licence link has changed is not relivant.
18543  *
18544  * Fork - LGPL
18545  * <script type="text/javascript">
18546  */
18547
18548
18549 /**
18550  * @class Roo.data.SortTypes
18551  * @singleton
18552  * Defines the default sorting (casting?) comparison functions used when sorting data.
18553  */
18554 Roo.data.SortTypes = {
18555     /**
18556      * Default sort that does nothing
18557      * @param {Mixed} s The value being converted
18558      * @return {Mixed} The comparison value
18559      */
18560     none : function(s){
18561         return s;
18562     },
18563     
18564     /**
18565      * The regular expression used to strip tags
18566      * @type {RegExp}
18567      * @property
18568      */
18569     stripTagsRE : /<\/?[^>]+>/gi,
18570     
18571     /**
18572      * Strips all HTML tags to sort on text only
18573      * @param {Mixed} s The value being converted
18574      * @return {String} The comparison value
18575      */
18576     asText : function(s){
18577         return String(s).replace(this.stripTagsRE, "");
18578     },
18579     
18580     /**
18581      * Strips all HTML tags to sort on text only - Case insensitive
18582      * @param {Mixed} s The value being converted
18583      * @return {String} The comparison value
18584      */
18585     asUCText : function(s){
18586         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18587     },
18588     
18589     /**
18590      * Case insensitive string
18591      * @param {Mixed} s The value being converted
18592      * @return {String} The comparison value
18593      */
18594     asUCString : function(s) {
18595         return String(s).toUpperCase();
18596     },
18597     
18598     /**
18599      * Date sorting
18600      * @param {Mixed} s The value being converted
18601      * @return {Number} The comparison value
18602      */
18603     asDate : function(s) {
18604         if(!s){
18605             return 0;
18606         }
18607         if(s instanceof Date){
18608             return s.getTime();
18609         }
18610         return Date.parse(String(s));
18611     },
18612     
18613     /**
18614      * Float sorting
18615      * @param {Mixed} s The value being converted
18616      * @return {Float} The comparison value
18617      */
18618     asFloat : function(s) {
18619         var val = parseFloat(String(s).replace(/,/g, ""));
18620         if(isNaN(val)) val = 0;
18621         return val;
18622     },
18623     
18624     /**
18625      * Integer sorting
18626      * @param {Mixed} s The value being converted
18627      * @return {Number} The comparison value
18628      */
18629     asInt : function(s) {
18630         var val = parseInt(String(s).replace(/,/g, ""));
18631         if(isNaN(val)) val = 0;
18632         return val;
18633     }
18634 };/*
18635  * Based on:
18636  * Ext JS Library 1.1.1
18637  * Copyright(c) 2006-2007, Ext JS, LLC.
18638  *
18639  * Originally Released Under LGPL - original licence link has changed is not relivant.
18640  *
18641  * Fork - LGPL
18642  * <script type="text/javascript">
18643  */
18644
18645 /**
18646 * @class Roo.data.Record
18647  * Instances of this class encapsulate both record <em>definition</em> information, and record
18648  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18649  * to access Records cached in an {@link Roo.data.Store} object.<br>
18650  * <p>
18651  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18652  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18653  * objects.<br>
18654  * <p>
18655  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18656  * @constructor
18657  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18658  * {@link #create}. The parameters are the same.
18659  * @param {Array} data An associative Array of data values keyed by the field name.
18660  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18661  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18662  * not specified an integer id is generated.
18663  */
18664 Roo.data.Record = function(data, id){
18665     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18666     this.data = data;
18667 };
18668
18669 /**
18670  * Generate a constructor for a specific record layout.
18671  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18672  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18673  * Each field definition object may contain the following properties: <ul>
18674  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18675  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18676  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18677  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18678  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18679  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18680  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18681  * this may be omitted.</p></li>
18682  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18683  * <ul><li>auto (Default, implies no conversion)</li>
18684  * <li>string</li>
18685  * <li>int</li>
18686  * <li>float</li>
18687  * <li>boolean</li>
18688  * <li>date</li></ul></p></li>
18689  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18690  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18691  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18692  * by the Reader into an object that will be stored in the Record. It is passed the
18693  * following parameters:<ul>
18694  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18695  * </ul></p></li>
18696  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18697  * </ul>
18698  * <br>usage:<br><pre><code>
18699 var TopicRecord = Roo.data.Record.create(
18700     {name: 'title', mapping: 'topic_title'},
18701     {name: 'author', mapping: 'username'},
18702     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18703     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18704     {name: 'lastPoster', mapping: 'user2'},
18705     {name: 'excerpt', mapping: 'post_text'}
18706 );
18707
18708 var myNewRecord = new TopicRecord({
18709     title: 'Do my job please',
18710     author: 'noobie',
18711     totalPosts: 1,
18712     lastPost: new Date(),
18713     lastPoster: 'Animal',
18714     excerpt: 'No way dude!'
18715 });
18716 myStore.add(myNewRecord);
18717 </code></pre>
18718  * @method create
18719  * @static
18720  */
18721 Roo.data.Record.create = function(o){
18722     var f = function(){
18723         f.superclass.constructor.apply(this, arguments);
18724     };
18725     Roo.extend(f, Roo.data.Record);
18726     var p = f.prototype;
18727     p.fields = new Roo.util.MixedCollection(false, function(field){
18728         return field.name;
18729     });
18730     for(var i = 0, len = o.length; i < len; i++){
18731         p.fields.add(new Roo.data.Field(o[i]));
18732     }
18733     f.getField = function(name){
18734         return p.fields.get(name);  
18735     };
18736     return f;
18737 };
18738
18739 Roo.data.Record.AUTO_ID = 1000;
18740 Roo.data.Record.EDIT = 'edit';
18741 Roo.data.Record.REJECT = 'reject';
18742 Roo.data.Record.COMMIT = 'commit';
18743
18744 Roo.data.Record.prototype = {
18745     /**
18746      * Readonly flag - true if this record has been modified.
18747      * @type Boolean
18748      */
18749     dirty : false,
18750     editing : false,
18751     error: null,
18752     modified: null,
18753
18754     // private
18755     join : function(store){
18756         this.store = store;
18757     },
18758
18759     /**
18760      * Set the named field to the specified value.
18761      * @param {String} name The name of the field to set.
18762      * @param {Object} value The value to set the field to.
18763      */
18764     set : function(name, value){
18765         if(this.data[name] == value){
18766             return;
18767         }
18768         this.dirty = true;
18769         if(!this.modified){
18770             this.modified = {};
18771         }
18772         if(typeof this.modified[name] == 'undefined'){
18773             this.modified[name] = this.data[name];
18774         }
18775         this.data[name] = value;
18776         if(!this.editing){
18777             this.store.afterEdit(this);
18778         }       
18779     },
18780
18781     /**
18782      * Get the value of the named field.
18783      * @param {String} name The name of the field to get the value of.
18784      * @return {Object} The value of the field.
18785      */
18786     get : function(name){
18787         return this.data[name]; 
18788     },
18789
18790     // private
18791     beginEdit : function(){
18792         this.editing = true;
18793         this.modified = {}; 
18794     },
18795
18796     // private
18797     cancelEdit : function(){
18798         this.editing = false;
18799         delete this.modified;
18800     },
18801
18802     // private
18803     endEdit : function(){
18804         this.editing = false;
18805         if(this.dirty && this.store){
18806             this.store.afterEdit(this);
18807         }
18808     },
18809
18810     /**
18811      * Usually called by the {@link Roo.data.Store} which owns the Record.
18812      * Rejects all changes made to the Record since either creation, or the last commit operation.
18813      * Modified fields are reverted to their original values.
18814      * <p>
18815      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18816      * of reject operations.
18817      */
18818     reject : function(){
18819         var m = this.modified;
18820         for(var n in m){
18821             if(typeof m[n] != "function"){
18822                 this.data[n] = m[n];
18823             }
18824         }
18825         this.dirty = false;
18826         delete this.modified;
18827         this.editing = false;
18828         if(this.store){
18829             this.store.afterReject(this);
18830         }
18831     },
18832
18833     /**
18834      * Usually called by the {@link Roo.data.Store} which owns the Record.
18835      * Commits all changes made to the Record since either creation, or the last commit operation.
18836      * <p>
18837      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18838      * of commit operations.
18839      */
18840     commit : function(){
18841         this.dirty = false;
18842         delete this.modified;
18843         this.editing = false;
18844         if(this.store){
18845             this.store.afterCommit(this);
18846         }
18847     },
18848
18849     // private
18850     hasError : function(){
18851         return this.error != null;
18852     },
18853
18854     // private
18855     clearError : function(){
18856         this.error = null;
18857     },
18858
18859     /**
18860      * Creates a copy of this record.
18861      * @param {String} id (optional) A new record id if you don't want to use this record's id
18862      * @return {Record}
18863      */
18864     copy : function(newId) {
18865         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18866     }
18867 };/*
18868  * Based on:
18869  * Ext JS Library 1.1.1
18870  * Copyright(c) 2006-2007, Ext JS, LLC.
18871  *
18872  * Originally Released Under LGPL - original licence link has changed is not relivant.
18873  *
18874  * Fork - LGPL
18875  * <script type="text/javascript">
18876  */
18877
18878
18879
18880 /**
18881  * @class Roo.data.Store
18882  * @extends Roo.util.Observable
18883  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18884  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18885  * <p>
18886  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
18887  * has no knowledge of the format of the data returned by the Proxy.<br>
18888  * <p>
18889  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18890  * instances from the data object. These records are cached and made available through accessor functions.
18891  * @constructor
18892  * Creates a new Store.
18893  * @param {Object} config A config object containing the objects needed for the Store to access data,
18894  * and read the data into Records.
18895  */
18896 Roo.data.Store = function(config){
18897     this.data = new Roo.util.MixedCollection(false);
18898     this.data.getKey = function(o){
18899         return o.id;
18900     };
18901     this.baseParams = {};
18902     // private
18903     this.paramNames = {
18904         "start" : "start",
18905         "limit" : "limit",
18906         "sort" : "sort",
18907         "dir" : "dir"
18908     };
18909
18910     if(config && config.data){
18911         this.inlineData = config.data;
18912         delete config.data;
18913     }
18914
18915     Roo.apply(this, config);
18916     
18917     if(this.reader){ // reader passed
18918         this.reader = Roo.factory(this.reader, Roo.data);
18919         this.reader.xmodule = this.xmodule || false;
18920         if(!this.recordType){
18921             this.recordType = this.reader.recordType;
18922         }
18923         if(this.reader.onMetaChange){
18924             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18925         }
18926     }
18927
18928     if(this.recordType){
18929         this.fields = this.recordType.prototype.fields;
18930     }
18931     this.modified = [];
18932
18933     this.addEvents({
18934         /**
18935          * @event datachanged
18936          * Fires when the data cache has changed, and a widget which is using this Store
18937          * as a Record cache should refresh its view.
18938          * @param {Store} this
18939          */
18940         datachanged : true,
18941         /**
18942          * @event metachange
18943          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18944          * @param {Store} this
18945          * @param {Object} meta The JSON metadata
18946          */
18947         metachange : true,
18948         /**
18949          * @event add
18950          * Fires when Records have been added to the Store
18951          * @param {Store} this
18952          * @param {Roo.data.Record[]} records The array of Records added
18953          * @param {Number} index The index at which the record(s) were added
18954          */
18955         add : true,
18956         /**
18957          * @event remove
18958          * Fires when a Record has been removed from the Store
18959          * @param {Store} this
18960          * @param {Roo.data.Record} record The Record that was removed
18961          * @param {Number} index The index at which the record was removed
18962          */
18963         remove : true,
18964         /**
18965          * @event update
18966          * Fires when a Record has been updated
18967          * @param {Store} this
18968          * @param {Roo.data.Record} record The Record that was updated
18969          * @param {String} operation The update operation being performed.  Value may be one of:
18970          * <pre><code>
18971  Roo.data.Record.EDIT
18972  Roo.data.Record.REJECT
18973  Roo.data.Record.COMMIT
18974          * </code></pre>
18975          */
18976         update : true,
18977         /**
18978          * @event clear
18979          * Fires when the data cache has been cleared.
18980          * @param {Store} this
18981          */
18982         clear : true,
18983         /**
18984          * @event beforeload
18985          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18986          * the load action will be canceled.
18987          * @param {Store} this
18988          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18989          */
18990         beforeload : true,
18991         /**
18992          * @event load
18993          * Fires after a new set of Records has been loaded.
18994          * @param {Store} this
18995          * @param {Roo.data.Record[]} records The Records that were loaded
18996          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18997          */
18998         load : true,
18999         /**
19000          * @event loadexception
19001          * Fires if an exception occurs in the Proxy during loading.
19002          * Called with the signature of the Proxy's "loadexception" event.
19003          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19004          * 
19005          * @param {Proxy} 
19006          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19007          * @param {Object} load options 
19008          * @param {Object} jsonData from your request (normally this contains the Exception)
19009          */
19010         loadexception : true
19011     });
19012     
19013     if(this.proxy){
19014         this.proxy = Roo.factory(this.proxy, Roo.data);
19015         this.proxy.xmodule = this.xmodule || false;
19016         this.relayEvents(this.proxy,  ["loadexception"]);
19017     }
19018     this.sortToggle = {};
19019
19020     Roo.data.Store.superclass.constructor.call(this);
19021
19022     if(this.inlineData){
19023         this.loadData(this.inlineData);
19024         delete this.inlineData;
19025     }
19026 };
19027 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19028      /**
19029     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19030     * without a remote query - used by combo/forms at present.
19031     */
19032     
19033     /**
19034     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19035     */
19036     /**
19037     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19038     */
19039     /**
19040     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19041     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19042     */
19043     /**
19044     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19045     * on any HTTP request
19046     */
19047     /**
19048     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19049     */
19050     /**
19051     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19052     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19053     */
19054     remoteSort : false,
19055
19056     /**
19057     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19058      * loaded or when a record is removed. (defaults to false).
19059     */
19060     pruneModifiedRecords : false,
19061
19062     // private
19063     lastOptions : null,
19064
19065     /**
19066      * Add Records to the Store and fires the add event.
19067      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19068      */
19069     add : function(records){
19070         records = [].concat(records);
19071         for(var i = 0, len = records.length; i < len; i++){
19072             records[i].join(this);
19073         }
19074         var index = this.data.length;
19075         this.data.addAll(records);
19076         this.fireEvent("add", this, records, index);
19077     },
19078
19079     /**
19080      * Remove a Record from the Store and fires the remove event.
19081      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19082      */
19083     remove : function(record){
19084         var index = this.data.indexOf(record);
19085         this.data.removeAt(index);
19086         if(this.pruneModifiedRecords){
19087             this.modified.remove(record);
19088         }
19089         this.fireEvent("remove", this, record, index);
19090     },
19091
19092     /**
19093      * Remove all Records from the Store and fires the clear event.
19094      */
19095     removeAll : function(){
19096         this.data.clear();
19097         if(this.pruneModifiedRecords){
19098             this.modified = [];
19099         }
19100         this.fireEvent("clear", this);
19101     },
19102
19103     /**
19104      * Inserts Records to the Store at the given index and fires the add event.
19105      * @param {Number} index The start index at which to insert the passed Records.
19106      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19107      */
19108     insert : function(index, records){
19109         records = [].concat(records);
19110         for(var i = 0, len = records.length; i < len; i++){
19111             this.data.insert(index, records[i]);
19112             records[i].join(this);
19113         }
19114         this.fireEvent("add", this, records, index);
19115     },
19116
19117     /**
19118      * Get the index within the cache of the passed Record.
19119      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19120      * @return {Number} The index of the passed Record. Returns -1 if not found.
19121      */
19122     indexOf : function(record){
19123         return this.data.indexOf(record);
19124     },
19125
19126     /**
19127      * Get the index within the cache of the Record with the passed id.
19128      * @param {String} id The id of the Record to find.
19129      * @return {Number} The index of the Record. Returns -1 if not found.
19130      */
19131     indexOfId : function(id){
19132         return this.data.indexOfKey(id);
19133     },
19134
19135     /**
19136      * Get the Record with the specified id.
19137      * @param {String} id The id of the Record to find.
19138      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19139      */
19140     getById : function(id){
19141         return this.data.key(id);
19142     },
19143
19144     /**
19145      * Get the Record at the specified index.
19146      * @param {Number} index The index of the Record to find.
19147      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19148      */
19149     getAt : function(index){
19150         return this.data.itemAt(index);
19151     },
19152
19153     /**
19154      * Returns a range of Records between specified indices.
19155      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19156      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19157      * @return {Roo.data.Record[]} An array of Records
19158      */
19159     getRange : function(start, end){
19160         return this.data.getRange(start, end);
19161     },
19162
19163     // private
19164     storeOptions : function(o){
19165         o = Roo.apply({}, o);
19166         delete o.callback;
19167         delete o.scope;
19168         this.lastOptions = o;
19169     },
19170
19171     /**
19172      * Loads the Record cache from the configured Proxy using the configured Reader.
19173      * <p>
19174      * If using remote paging, then the first load call must specify the <em>start</em>
19175      * and <em>limit</em> properties in the options.params property to establish the initial
19176      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19177      * <p>
19178      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19179      * and this call will return before the new data has been loaded. Perform any post-processing
19180      * in a callback function, or in a "load" event handler.</strong>
19181      * <p>
19182      * @param {Object} options An object containing properties which control loading options:<ul>
19183      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19184      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19185      * passed the following arguments:<ul>
19186      * <li>r : Roo.data.Record[]</li>
19187      * <li>options: Options object from the load call</li>
19188      * <li>success: Boolean success indicator</li></ul></li>
19189      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19190      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19191      * </ul>
19192      */
19193     load : function(options){
19194         options = options || {};
19195         if(this.fireEvent("beforeload", this, options) !== false){
19196             this.storeOptions(options);
19197             var p = Roo.apply(options.params || {}, this.baseParams);
19198             if(this.sortInfo && this.remoteSort){
19199                 var pn = this.paramNames;
19200                 p[pn["sort"]] = this.sortInfo.field;
19201                 p[pn["dir"]] = this.sortInfo.direction;
19202             }
19203             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19204         }
19205     },
19206
19207     /**
19208      * Reloads the Record cache from the configured Proxy using the configured Reader and
19209      * the options from the last load operation performed.
19210      * @param {Object} options (optional) An object containing properties which may override the options
19211      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19212      * the most recently used options are reused).
19213      */
19214     reload : function(options){
19215         this.load(Roo.applyIf(options||{}, this.lastOptions));
19216     },
19217
19218     // private
19219     // Called as a callback by the Reader during a load operation.
19220     loadRecords : function(o, options, success){
19221         if(!o || success === false){
19222             if(success !== false){
19223                 this.fireEvent("load", this, [], options);
19224             }
19225             if(options.callback){
19226                 options.callback.call(options.scope || this, [], options, false);
19227             }
19228             return;
19229         }
19230         // if data returned failure - throw an exception.
19231         if (o.success === false) {
19232             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19233             return;
19234         }
19235         var r = o.records, t = o.totalRecords || r.length;
19236         if(!options || options.add !== true){
19237             if(this.pruneModifiedRecords){
19238                 this.modified = [];
19239             }
19240             for(var i = 0, len = r.length; i < len; i++){
19241                 r[i].join(this);
19242             }
19243             if(this.snapshot){
19244                 this.data = this.snapshot;
19245                 delete this.snapshot;
19246             }
19247             this.data.clear();
19248             this.data.addAll(r);
19249             this.totalLength = t;
19250             this.applySort();
19251             this.fireEvent("datachanged", this);
19252         }else{
19253             this.totalLength = Math.max(t, this.data.length+r.length);
19254             this.add(r);
19255         }
19256         this.fireEvent("load", this, r, options);
19257         if(options.callback){
19258             options.callback.call(options.scope || this, r, options, true);
19259         }
19260     },
19261
19262     /**
19263      * Loads data from a passed data block. A Reader which understands the format of the data
19264      * must have been configured in the constructor.
19265      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19266      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19267      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19268      */
19269     loadData : function(o, append){
19270         var r = this.reader.readRecords(o);
19271         this.loadRecords(r, {add: append}, true);
19272     },
19273
19274     /**
19275      * Gets the number of cached records.
19276      * <p>
19277      * <em>If using paging, this may not be the total size of the dataset. If the data object
19278      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19279      * the data set size</em>
19280      */
19281     getCount : function(){
19282         return this.data.length || 0;
19283     },
19284
19285     /**
19286      * Gets the total number of records in the dataset as returned by the server.
19287      * <p>
19288      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19289      * the dataset size</em>
19290      */
19291     getTotalCount : function(){
19292         return this.totalLength || 0;
19293     },
19294
19295     /**
19296      * Returns the sort state of the Store as an object with two properties:
19297      * <pre><code>
19298  field {String} The name of the field by which the Records are sorted
19299  direction {String} The sort order, "ASC" or "DESC"
19300      * </code></pre>
19301      */
19302     getSortState : function(){
19303         return this.sortInfo;
19304     },
19305
19306     // private
19307     applySort : function(){
19308         if(this.sortInfo && !this.remoteSort){
19309             var s = this.sortInfo, f = s.field;
19310             var st = this.fields.get(f).sortType;
19311             var fn = function(r1, r2){
19312                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19313                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19314             };
19315             this.data.sort(s.direction, fn);
19316             if(this.snapshot && this.snapshot != this.data){
19317                 this.snapshot.sort(s.direction, fn);
19318             }
19319         }
19320     },
19321
19322     /**
19323      * Sets the default sort column and order to be used by the next load operation.
19324      * @param {String} fieldName The name of the field to sort by.
19325      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19326      */
19327     setDefaultSort : function(field, dir){
19328         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19329     },
19330
19331     /**
19332      * Sort the Records.
19333      * If remote sorting is used, the sort is performed on the server, and the cache is
19334      * reloaded. If local sorting is used, the cache is sorted internally.
19335      * @param {String} fieldName The name of the field to sort by.
19336      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19337      */
19338     sort : function(fieldName, dir){
19339         var f = this.fields.get(fieldName);
19340         if(!dir){
19341             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19342                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19343             }else{
19344                 dir = f.sortDir;
19345             }
19346         }
19347         this.sortToggle[f.name] = dir;
19348         this.sortInfo = {field: f.name, direction: dir};
19349         if(!this.remoteSort){
19350             this.applySort();
19351             this.fireEvent("datachanged", this);
19352         }else{
19353             this.load(this.lastOptions);
19354         }
19355     },
19356
19357     /**
19358      * Calls the specified function for each of the Records in the cache.
19359      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19360      * Returning <em>false</em> aborts and exits the iteration.
19361      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19362      */
19363     each : function(fn, scope){
19364         this.data.each(fn, scope);
19365     },
19366
19367     /**
19368      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19369      * (e.g., during paging).
19370      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19371      */
19372     getModifiedRecords : function(){
19373         return this.modified;
19374     },
19375
19376     // private
19377     createFilterFn : function(property, value, anyMatch){
19378         if(!value.exec){ // not a regex
19379             value = String(value);
19380             if(value.length == 0){
19381                 return false;
19382             }
19383             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19384         }
19385         return function(r){
19386             return value.test(r.data[property]);
19387         };
19388     },
19389
19390     /**
19391      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19392      * @param {String} property A field on your records
19393      * @param {Number} start The record index to start at (defaults to 0)
19394      * @param {Number} end The last record index to include (defaults to length - 1)
19395      * @return {Number} The sum
19396      */
19397     sum : function(property, start, end){
19398         var rs = this.data.items, v = 0;
19399         start = start || 0;
19400         end = (end || end === 0) ? end : rs.length-1;
19401
19402         for(var i = start; i <= end; i++){
19403             v += (rs[i].data[property] || 0);
19404         }
19405         return v;
19406     },
19407
19408     /**
19409      * Filter the records by a specified property.
19410      * @param {String} field A field on your records
19411      * @param {String/RegExp} value Either a string that the field
19412      * should start with or a RegExp to test against the field
19413      * @param {Boolean} anyMatch True to match any part not just the beginning
19414      */
19415     filter : function(property, value, anyMatch){
19416         var fn = this.createFilterFn(property, value, anyMatch);
19417         return fn ? this.filterBy(fn) : this.clearFilter();
19418     },
19419
19420     /**
19421      * Filter by a function. The specified function will be called with each
19422      * record in this data source. If the function returns true the record is included,
19423      * otherwise it is filtered.
19424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19425      * @param {Object} scope (optional) The scope of the function (defaults to this)
19426      */
19427     filterBy : function(fn, scope){
19428         this.snapshot = this.snapshot || this.data;
19429         this.data = this.queryBy(fn, scope||this);
19430         this.fireEvent("datachanged", this);
19431     },
19432
19433     /**
19434      * Query the records by a specified property.
19435      * @param {String} field A field on your records
19436      * @param {String/RegExp} value Either a string that the field
19437      * should start with or a RegExp to test against the field
19438      * @param {Boolean} anyMatch True to match any part not just the beginning
19439      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19440      */
19441     query : function(property, value, anyMatch){
19442         var fn = this.createFilterFn(property, value, anyMatch);
19443         return fn ? this.queryBy(fn) : this.data.clone();
19444     },
19445
19446     /**
19447      * Query by a function. The specified function will be called with each
19448      * record in this data source. If the function returns true the record is included
19449      * in the results.
19450      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19451      * @param {Object} scope (optional) The scope of the function (defaults to this)
19452       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19453      **/
19454     queryBy : function(fn, scope){
19455         var data = this.snapshot || this.data;
19456         return data.filterBy(fn, scope||this);
19457     },
19458
19459     /**
19460      * Collects unique values for a particular dataIndex from this store.
19461      * @param {String} dataIndex The property to collect
19462      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19463      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19464      * @return {Array} An array of the unique values
19465      **/
19466     collect : function(dataIndex, allowNull, bypassFilter){
19467         var d = (bypassFilter === true && this.snapshot) ?
19468                 this.snapshot.items : this.data.items;
19469         var v, sv, r = [], l = {};
19470         for(var i = 0, len = d.length; i < len; i++){
19471             v = d[i].data[dataIndex];
19472             sv = String(v);
19473             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19474                 l[sv] = true;
19475                 r[r.length] = v;
19476             }
19477         }
19478         return r;
19479     },
19480
19481     /**
19482      * Revert to a view of the Record cache with no filtering applied.
19483      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19484      */
19485     clearFilter : function(suppressEvent){
19486         if(this.snapshot && this.snapshot != this.data){
19487             this.data = this.snapshot;
19488             delete this.snapshot;
19489             if(suppressEvent !== true){
19490                 this.fireEvent("datachanged", this);
19491             }
19492         }
19493     },
19494
19495     // private
19496     afterEdit : function(record){
19497         if(this.modified.indexOf(record) == -1){
19498             this.modified.push(record);
19499         }
19500         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19501     },
19502
19503     // private
19504     afterReject : function(record){
19505         this.modified.remove(record);
19506         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19507     },
19508
19509     // private
19510     afterCommit : function(record){
19511         this.modified.remove(record);
19512         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19513     },
19514
19515     /**
19516      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19517      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19518      */
19519     commitChanges : function(){
19520         var m = this.modified.slice(0);
19521         this.modified = [];
19522         for(var i = 0, len = m.length; i < len; i++){
19523             m[i].commit();
19524         }
19525     },
19526
19527     /**
19528      * Cancel outstanding changes on all changed records.
19529      */
19530     rejectChanges : function(){
19531         var m = this.modified.slice(0);
19532         this.modified = [];
19533         for(var i = 0, len = m.length; i < len; i++){
19534             m[i].reject();
19535         }
19536     },
19537
19538     onMetaChange : function(meta, rtype, o){
19539         this.recordType = rtype;
19540         this.fields = rtype.prototype.fields;
19541         delete this.snapshot;
19542         this.sortInfo = meta.sortInfo;
19543         this.modified = [];
19544         this.fireEvent('metachange', this, this.reader.meta);
19545     }
19546 });/*
19547  * Based on:
19548  * Ext JS Library 1.1.1
19549  * Copyright(c) 2006-2007, Ext JS, LLC.
19550  *
19551  * Originally Released Under LGPL - original licence link has changed is not relivant.
19552  *
19553  * Fork - LGPL
19554  * <script type="text/javascript">
19555  */
19556
19557 /**
19558  * @class Roo.data.SimpleStore
19559  * @extends Roo.data.Store
19560  * Small helper class to make creating Stores from Array data easier.
19561  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19562  * @cfg {Array} fields An array of field definition objects, or field name strings.
19563  * @cfg {Array} data The multi-dimensional array of data
19564  * @constructor
19565  * @param {Object} config
19566  */
19567 Roo.data.SimpleStore = function(config){
19568     Roo.data.SimpleStore.superclass.constructor.call(this, {
19569         isLocal : true,
19570         reader: new Roo.data.ArrayReader({
19571                 id: config.id
19572             },
19573             Roo.data.Record.create(config.fields)
19574         ),
19575         proxy : new Roo.data.MemoryProxy(config.data)
19576     });
19577     this.load();
19578 };
19579 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19580  * Based on:
19581  * Ext JS Library 1.1.1
19582  * Copyright(c) 2006-2007, Ext JS, LLC.
19583  *
19584  * Originally Released Under LGPL - original licence link has changed is not relivant.
19585  *
19586  * Fork - LGPL
19587  * <script type="text/javascript">
19588  */
19589
19590 /**
19591 /**
19592  * @extends Roo.data.Store
19593  * @class Roo.data.JsonStore
19594  * Small helper class to make creating Stores for JSON data easier. <br/>
19595 <pre><code>
19596 var store = new Roo.data.JsonStore({
19597     url: 'get-images.php',
19598     root: 'images',
19599     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19600 });
19601 </code></pre>
19602  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19603  * JsonReader and HttpProxy (unless inline data is provided).</b>
19604  * @cfg {Array} fields An array of field definition objects, or field name strings.
19605  * @constructor
19606  * @param {Object} config
19607  */
19608 Roo.data.JsonStore = function(c){
19609     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19610         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19611         reader: new Roo.data.JsonReader(c, c.fields)
19612     }));
19613 };
19614 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19615  * Based on:
19616  * Ext JS Library 1.1.1
19617  * Copyright(c) 2006-2007, Ext JS, LLC.
19618  *
19619  * Originally Released Under LGPL - original licence link has changed is not relivant.
19620  *
19621  * Fork - LGPL
19622  * <script type="text/javascript">
19623  */
19624
19625  
19626 Roo.data.Field = function(config){
19627     if(typeof config == "string"){
19628         config = {name: config};
19629     }
19630     Roo.apply(this, config);
19631     
19632     if(!this.type){
19633         this.type = "auto";
19634     }
19635     
19636     var st = Roo.data.SortTypes;
19637     // named sortTypes are supported, here we look them up
19638     if(typeof this.sortType == "string"){
19639         this.sortType = st[this.sortType];
19640     }
19641     
19642     // set default sortType for strings and dates
19643     if(!this.sortType){
19644         switch(this.type){
19645             case "string":
19646                 this.sortType = st.asUCString;
19647                 break;
19648             case "date":
19649                 this.sortType = st.asDate;
19650                 break;
19651             default:
19652                 this.sortType = st.none;
19653         }
19654     }
19655
19656     // define once
19657     var stripRe = /[\$,%]/g;
19658
19659     // prebuilt conversion function for this field, instead of
19660     // switching every time we're reading a value
19661     if(!this.convert){
19662         var cv, dateFormat = this.dateFormat;
19663         switch(this.type){
19664             case "":
19665             case "auto":
19666             case undefined:
19667                 cv = function(v){ return v; };
19668                 break;
19669             case "string":
19670                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19671                 break;
19672             case "int":
19673                 cv = function(v){
19674                     return v !== undefined && v !== null && v !== '' ?
19675                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19676                     };
19677                 break;
19678             case "float":
19679                 cv = function(v){
19680                     return v !== undefined && v !== null && v !== '' ?
19681                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19682                     };
19683                 break;
19684             case "bool":
19685             case "boolean":
19686                 cv = function(v){ return v === true || v === "true" || v == 1; };
19687                 break;
19688             case "date":
19689                 cv = function(v){
19690                     if(!v){
19691                         return '';
19692                     }
19693                     if(v instanceof Date){
19694                         return v;
19695                     }
19696                     if(dateFormat){
19697                         if(dateFormat == "timestamp"){
19698                             return new Date(v*1000);
19699                         }
19700                         return Date.parseDate(v, dateFormat);
19701                     }
19702                     var parsed = Date.parse(v);
19703                     return parsed ? new Date(parsed) : null;
19704                 };
19705              break;
19706             
19707         }
19708         this.convert = cv;
19709     }
19710 };
19711
19712 Roo.data.Field.prototype = {
19713     dateFormat: null,
19714     defaultValue: "",
19715     mapping: null,
19716     sortType : null,
19717     sortDir : "ASC"
19718 };/*
19719  * Based on:
19720  * Ext JS Library 1.1.1
19721  * Copyright(c) 2006-2007, Ext JS, LLC.
19722  *
19723  * Originally Released Under LGPL - original licence link has changed is not relivant.
19724  *
19725  * Fork - LGPL
19726  * <script type="text/javascript">
19727  */
19728  
19729 // Base class for reading structured data from a data source.  This class is intended to be
19730 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19731
19732 /**
19733  * @class Roo.data.DataReader
19734  * Base class for reading structured data from a data source.  This class is intended to be
19735  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19736  */
19737
19738 Roo.data.DataReader = function(meta, recordType){
19739     
19740     this.meta = meta;
19741     
19742     this.recordType = recordType instanceof Array ? 
19743         Roo.data.Record.create(recordType) : recordType;
19744 };
19745
19746 Roo.data.DataReader.prototype = {
19747      /**
19748      * Create an empty record
19749      * @param {Object} data (optional) - overlay some values
19750      * @return {Roo.data.Record} record created.
19751      */
19752     newRow :  function(d) {
19753         var da =  {};
19754         this.recordType.prototype.fields.each(function(c) {
19755             switch( c.type) {
19756                 case 'int' : da[c.name] = 0; break;
19757                 case 'date' : da[c.name] = new Date(); break;
19758                 case 'float' : da[c.name] = 0.0; break;
19759                 case 'boolean' : da[c.name] = false; break;
19760                 default : da[c.name] = ""; break;
19761             }
19762             
19763         });
19764         return new this.recordType(Roo.apply(da, d));
19765     }
19766     
19767 };/*
19768  * Based on:
19769  * Ext JS Library 1.1.1
19770  * Copyright(c) 2006-2007, Ext JS, LLC.
19771  *
19772  * Originally Released Under LGPL - original licence link has changed is not relivant.
19773  *
19774  * Fork - LGPL
19775  * <script type="text/javascript">
19776  */
19777
19778 /**
19779  * @class Roo.data.DataProxy
19780  * @extends Roo.data.Observable
19781  * This class is an abstract base class for implementations which provide retrieval of
19782  * unformatted data objects.<br>
19783  * <p>
19784  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19785  * (of the appropriate type which knows how to parse the data object) to provide a block of
19786  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19787  * <p>
19788  * Custom implementations must implement the load method as described in
19789  * {@link Roo.data.HttpProxy#load}.
19790  */
19791 Roo.data.DataProxy = function(){
19792     this.addEvents({
19793         /**
19794          * @event beforeload
19795          * Fires before a network request is made to retrieve a data object.
19796          * @param {Object} This DataProxy object.
19797          * @param {Object} params The params parameter to the load function.
19798          */
19799         beforeload : true,
19800         /**
19801          * @event load
19802          * Fires before the load method's callback is called.
19803          * @param {Object} This DataProxy object.
19804          * @param {Object} o The data object.
19805          * @param {Object} arg The callback argument object passed to the load function.
19806          */
19807         load : true,
19808         /**
19809          * @event loadexception
19810          * Fires if an Exception occurs during data retrieval.
19811          * @param {Object} This DataProxy object.
19812          * @param {Object} o The data object.
19813          * @param {Object} arg The callback argument object passed to the load function.
19814          * @param {Object} e The Exception.
19815          */
19816         loadexception : true
19817     });
19818     Roo.data.DataProxy.superclass.constructor.call(this);
19819 };
19820
19821 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19822
19823     /**
19824      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19825      */
19826 /*
19827  * Based on:
19828  * Ext JS Library 1.1.1
19829  * Copyright(c) 2006-2007, Ext JS, LLC.
19830  *
19831  * Originally Released Under LGPL - original licence link has changed is not relivant.
19832  *
19833  * Fork - LGPL
19834  * <script type="text/javascript">
19835  */
19836 /**
19837  * @class Roo.data.MemoryProxy
19838  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19839  * to the Reader when its load method is called.
19840  * @constructor
19841  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19842  */
19843 Roo.data.MemoryProxy = function(data){
19844     if (data.data) {
19845         data = data.data;
19846     }
19847     Roo.data.MemoryProxy.superclass.constructor.call(this);
19848     this.data = data;
19849 };
19850
19851 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19852     /**
19853      * Load data from the requested source (in this case an in-memory
19854      * data object passed to the constructor), read the data object into
19855      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19856      * process that block using the passed callback.
19857      * @param {Object} params This parameter is not used by the MemoryProxy class.
19858      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19859      * object into a block of Roo.data.Records.
19860      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19861      * The function must be passed <ul>
19862      * <li>The Record block object</li>
19863      * <li>The "arg" argument from the load function</li>
19864      * <li>A boolean success indicator</li>
19865      * </ul>
19866      * @param {Object} scope The scope in which to call the callback
19867      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19868      */
19869     load : function(params, reader, callback, scope, arg){
19870         params = params || {};
19871         var result;
19872         try {
19873             result = reader.readRecords(this.data);
19874         }catch(e){
19875             this.fireEvent("loadexception", this, arg, null, e);
19876             callback.call(scope, null, arg, false);
19877             return;
19878         }
19879         callback.call(scope, result, arg, true);
19880     },
19881     
19882     // private
19883     update : function(params, records){
19884         
19885     }
19886 });/*
19887  * Based on:
19888  * Ext JS Library 1.1.1
19889  * Copyright(c) 2006-2007, Ext JS, LLC.
19890  *
19891  * Originally Released Under LGPL - original licence link has changed is not relivant.
19892  *
19893  * Fork - LGPL
19894  * <script type="text/javascript">
19895  */
19896 /**
19897  * @class Roo.data.HttpProxy
19898  * @extends Roo.data.DataProxy
19899  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19900  * configured to reference a certain URL.<br><br>
19901  * <p>
19902  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19903  * from which the running page was served.<br><br>
19904  * <p>
19905  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19906  * <p>
19907  * Be aware that to enable the browser to parse an XML document, the server must set
19908  * the Content-Type header in the HTTP response to "text/xml".
19909  * @constructor
19910  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19911  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19912  * will be used to make the request.
19913  */
19914 Roo.data.HttpProxy = function(conn){
19915     Roo.data.HttpProxy.superclass.constructor.call(this);
19916     // is conn a conn config or a real conn?
19917     this.conn = conn;
19918     this.useAjax = !conn || !conn.events;
19919   
19920 };
19921
19922 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19923     // thse are take from connection...
19924     
19925     /**
19926      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19927      */
19928     /**
19929      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19930      * extra parameters to each request made by this object. (defaults to undefined)
19931      */
19932     /**
19933      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19934      *  to each request made by this object. (defaults to undefined)
19935      */
19936     /**
19937      * @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)
19938      */
19939     /**
19940      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19941      */
19942      /**
19943      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19944      * @type Boolean
19945      */
19946   
19947
19948     /**
19949      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19950      * @type Boolean
19951      */
19952     /**
19953      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19954      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19955      * a finer-grained basis than the DataProxy events.
19956      */
19957     getConnection : function(){
19958         return this.useAjax ? Roo.Ajax : this.conn;
19959     },
19960
19961     /**
19962      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19963      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19964      * process that block using the passed callback.
19965      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19966      * for the request to the remote server.
19967      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19968      * object into a block of Roo.data.Records.
19969      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19970      * The function must be passed <ul>
19971      * <li>The Record block object</li>
19972      * <li>The "arg" argument from the load function</li>
19973      * <li>A boolean success indicator</li>
19974      * </ul>
19975      * @param {Object} scope The scope in which to call the callback
19976      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19977      */
19978     load : function(params, reader, callback, scope, arg){
19979         if(this.fireEvent("beforeload", this, params) !== false){
19980             var  o = {
19981                 params : params || {},
19982                 request: {
19983                     callback : callback,
19984                     scope : scope,
19985                     arg : arg
19986                 },
19987                 reader: reader,
19988                 callback : this.loadResponse,
19989                 scope: this
19990             };
19991             if(this.useAjax){
19992                 Roo.applyIf(o, this.conn);
19993                 if(this.activeRequest){
19994                     Roo.Ajax.abort(this.activeRequest);
19995                 }
19996                 this.activeRequest = Roo.Ajax.request(o);
19997             }else{
19998                 this.conn.request(o);
19999             }
20000         }else{
20001             callback.call(scope||this, null, arg, false);
20002         }
20003     },
20004
20005     // private
20006     loadResponse : function(o, success, response){
20007         delete this.activeRequest;
20008         if(!success){
20009             this.fireEvent("loadexception", this, o, response);
20010             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20011             return;
20012         }
20013         var result;
20014         try {
20015             result = o.reader.read(response);
20016         }catch(e){
20017             this.fireEvent("loadexception", this, o, response, e);
20018             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20019             return;
20020         }
20021         
20022         this.fireEvent("load", this, o, o.request.arg);
20023         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20024     },
20025
20026     // private
20027     update : function(dataSet){
20028
20029     },
20030
20031     // private
20032     updateResponse : function(dataSet){
20033
20034     }
20035 });/*
20036  * Based on:
20037  * Ext JS Library 1.1.1
20038  * Copyright(c) 2006-2007, Ext JS, LLC.
20039  *
20040  * Originally Released Under LGPL - original licence link has changed is not relivant.
20041  *
20042  * Fork - LGPL
20043  * <script type="text/javascript">
20044  */
20045
20046 /**
20047  * @class Roo.data.ScriptTagProxy
20048  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20049  * other than the originating domain of the running page.<br><br>
20050  * <p>
20051  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20052  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20053  * <p>
20054  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20055  * source code that is used as the source inside a &lt;script> tag.<br><br>
20056  * <p>
20057  * In order for the browser to process the returned data, the server must wrap the data object
20058  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20059  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20060  * depending on whether the callback name was passed:
20061  * <p>
20062  * <pre><code>
20063 boolean scriptTag = false;
20064 String cb = request.getParameter("callback");
20065 if (cb != null) {
20066     scriptTag = true;
20067     response.setContentType("text/javascript");
20068 } else {
20069     response.setContentType("application/x-json");
20070 }
20071 Writer out = response.getWriter();
20072 if (scriptTag) {
20073     out.write(cb + "(");
20074 }
20075 out.print(dataBlock.toJsonString());
20076 if (scriptTag) {
20077     out.write(");");
20078 }
20079 </pre></code>
20080  *
20081  * @constructor
20082  * @param {Object} config A configuration object.
20083  */
20084 Roo.data.ScriptTagProxy = function(config){
20085     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20086     Roo.apply(this, config);
20087     this.head = document.getElementsByTagName("head")[0];
20088 };
20089
20090 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20091
20092 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20093     /**
20094      * @cfg {String} url The URL from which to request the data object.
20095      */
20096     /**
20097      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20098      */
20099     timeout : 30000,
20100     /**
20101      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20102      * the server the name of the callback function set up by the load call to process the returned data object.
20103      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20104      * javascript output which calls this named function passing the data object as its only parameter.
20105      */
20106     callbackParam : "callback",
20107     /**
20108      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20109      * name to the request.
20110      */
20111     nocache : true,
20112
20113     /**
20114      * Load data from the configured URL, read the data object into
20115      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20116      * process that block using the passed callback.
20117      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20118      * for the request to the remote server.
20119      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20120      * object into a block of Roo.data.Records.
20121      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20122      * The function must be passed <ul>
20123      * <li>The Record block object</li>
20124      * <li>The "arg" argument from the load function</li>
20125      * <li>A boolean success indicator</li>
20126      * </ul>
20127      * @param {Object} scope The scope in which to call the callback
20128      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20129      */
20130     load : function(params, reader, callback, scope, arg){
20131         if(this.fireEvent("beforeload", this, params) !== false){
20132
20133             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20134
20135             var url = this.url;
20136             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20137             if(this.nocache){
20138                 url += "&_dc=" + (new Date().getTime());
20139             }
20140             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20141             var trans = {
20142                 id : transId,
20143                 cb : "stcCallback"+transId,
20144                 scriptId : "stcScript"+transId,
20145                 params : params,
20146                 arg : arg,
20147                 url : url,
20148                 callback : callback,
20149                 scope : scope,
20150                 reader : reader
20151             };
20152             var conn = this;
20153
20154             window[trans.cb] = function(o){
20155                 conn.handleResponse(o, trans);
20156             };
20157
20158             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20159
20160             if(this.autoAbort !== false){
20161                 this.abort();
20162             }
20163
20164             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20165
20166             var script = document.createElement("script");
20167             script.setAttribute("src", url);
20168             script.setAttribute("type", "text/javascript");
20169             script.setAttribute("id", trans.scriptId);
20170             this.head.appendChild(script);
20171
20172             this.trans = trans;
20173         }else{
20174             callback.call(scope||this, null, arg, false);
20175         }
20176     },
20177
20178     // private
20179     isLoading : function(){
20180         return this.trans ? true : false;
20181     },
20182
20183     /**
20184      * Abort the current server request.
20185      */
20186     abort : function(){
20187         if(this.isLoading()){
20188             this.destroyTrans(this.trans);
20189         }
20190     },
20191
20192     // private
20193     destroyTrans : function(trans, isLoaded){
20194         this.head.removeChild(document.getElementById(trans.scriptId));
20195         clearTimeout(trans.timeoutId);
20196         if(isLoaded){
20197             window[trans.cb] = undefined;
20198             try{
20199                 delete window[trans.cb];
20200             }catch(e){}
20201         }else{
20202             // if hasn't been loaded, wait for load to remove it to prevent script error
20203             window[trans.cb] = function(){
20204                 window[trans.cb] = undefined;
20205                 try{
20206                     delete window[trans.cb];
20207                 }catch(e){}
20208             };
20209         }
20210     },
20211
20212     // private
20213     handleResponse : function(o, trans){
20214         this.trans = false;
20215         this.destroyTrans(trans, true);
20216         var result;
20217         try {
20218             result = trans.reader.readRecords(o);
20219         }catch(e){
20220             this.fireEvent("loadexception", this, o, trans.arg, e);
20221             trans.callback.call(trans.scope||window, null, trans.arg, false);
20222             return;
20223         }
20224         this.fireEvent("load", this, o, trans.arg);
20225         trans.callback.call(trans.scope||window, result, trans.arg, true);
20226     },
20227
20228     // private
20229     handleFailure : function(trans){
20230         this.trans = false;
20231         this.destroyTrans(trans, false);
20232         this.fireEvent("loadexception", this, null, trans.arg);
20233         trans.callback.call(trans.scope||window, null, trans.arg, false);
20234     }
20235 });/*
20236  * Based on:
20237  * Ext JS Library 1.1.1
20238  * Copyright(c) 2006-2007, Ext JS, LLC.
20239  *
20240  * Originally Released Under LGPL - original licence link has changed is not relivant.
20241  *
20242  * Fork - LGPL
20243  * <script type="text/javascript">
20244  */
20245
20246 /**
20247  * @class Roo.data.JsonReader
20248  * @extends Roo.data.DataReader
20249  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20250  * based on mappings in a provided Roo.data.Record constructor.
20251  * <p>
20252  * Example code:
20253  * <pre><code>
20254 var RecordDef = Roo.data.Record.create([
20255     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20256     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20257 ]);
20258 var myReader = new Roo.data.JsonReader({
20259     totalProperty: "results",    // The property which contains the total dataset size (optional)
20260     root: "rows",                // The property which contains an Array of row objects
20261     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20262 }, RecordDef);
20263 </code></pre>
20264  * <p>
20265  * This would consume a JSON file like this:
20266  * <pre><code>
20267 { 'results': 2, 'rows': [
20268     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20269     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20270 }
20271 </code></pre>
20272  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20273  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20274  * paged from the remote server.
20275  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20276  * @cfg {String} root name of the property which contains the Array of row objects.
20277  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20278  * @constructor
20279  * Create a new JsonReader
20280  * @param {Object} meta Metadata configuration options
20281  * @param {Object} recordType Either an Array of field definition objects,
20282  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20283  */
20284 Roo.data.JsonReader = function(meta, recordType){
20285     
20286     meta = meta || {};
20287     // set some defaults:
20288     Roo.applyIf(meta, {
20289         totalProperty: 'total',
20290         successProperty : 'success',
20291         root : 'data',
20292         id : 'id'
20293     });
20294     
20295     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20296 };
20297 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20298     /**
20299      * This method is only used by a DataProxy which has retrieved data from a remote server.
20300      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20301      * @return {Object} data A data block which is used by an Roo.data.Store object as
20302      * a cache of Roo.data.Records.
20303      */
20304     read : function(response){
20305         var json = response.responseText;
20306         /* eval:var:o */
20307         var o = eval("("+json+")");
20308         if(!o) {
20309             throw {message: "JsonReader.read: Json object not found"};
20310         }
20311         
20312         if(o.metaData){
20313             delete this.ef;
20314             this.meta = o.metaData;
20315             this.recordType = Roo.data.Record.create(o.metaData.fields);
20316             this.onMetaChange(this.meta, this.recordType, o);
20317         }
20318         return this.readRecords(o);
20319     },
20320
20321     // private function a store will implement
20322     onMetaChange : function(meta, recordType, o){
20323
20324     },
20325
20326     /**
20327          * @ignore
20328          */
20329     simpleAccess: function(obj, subsc) {
20330         return obj[subsc];
20331     },
20332
20333         /**
20334          * @ignore
20335          */
20336     getJsonAccessor: function(){
20337         var re = /[\[\.]/;
20338         return function(expr) {
20339             try {
20340                 return(re.test(expr))
20341                     ? new Function("obj", "return obj." + expr)
20342                     : function(obj){
20343                         return obj[expr];
20344                     };
20345             } catch(e){}
20346             return Roo.emptyFn;
20347         };
20348     }(),
20349
20350     /**
20351      * Create a data block containing Roo.data.Records from an XML document.
20352      * @param {Object} o An object which contains an Array of row objects in the property specified
20353      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20354      * which contains the total size of the dataset.
20355      * @return {Object} data A data block which is used by an Roo.data.Store object as
20356      * a cache of Roo.data.Records.
20357      */
20358     readRecords : function(o){
20359         /**
20360          * After any data loads, the raw JSON data is available for further custom processing.
20361          * @type Object
20362          */
20363         this.jsonData = o;
20364         var s = this.meta, Record = this.recordType,
20365             f = Record.prototype.fields, fi = f.items, fl = f.length;
20366
20367 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20368         if (!this.ef) {
20369             if(s.totalProperty) {
20370                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20371                 }
20372                 if(s.successProperty) {
20373                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20374                 }
20375                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20376                 if (s.id) {
20377                         var g = this.getJsonAccessor(s.id);
20378                         this.getId = function(rec) {
20379                                 var r = g(rec);
20380                                 return (r === undefined || r === "") ? null : r;
20381                         };
20382                 } else {
20383                         this.getId = function(){return null;};
20384                 }
20385             this.ef = [];
20386             for(var i = 0; i < fl; i++){
20387                 f = fi[i];
20388                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20389                 this.ef[i] = this.getJsonAccessor(map);
20390             }
20391         }
20392
20393         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20394         if(s.totalProperty){
20395             var v = parseInt(this.getTotal(o), 10);
20396             if(!isNaN(v)){
20397                 totalRecords = v;
20398             }
20399         }
20400         if(s.successProperty){
20401             var v = this.getSuccess(o);
20402             if(v === false || v === 'false'){
20403                 success = false;
20404             }
20405         }
20406         var records = [];
20407             for(var i = 0; i < c; i++){
20408                     var n = root[i];
20409                 var values = {};
20410                 var id = this.getId(n);
20411                 for(var j = 0; j < fl; j++){
20412                     f = fi[j];
20413                 var v = this.ef[j](n);
20414                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20415                 }
20416                 var record = new Record(values, id);
20417                 record.json = n;
20418                 records[i] = record;
20419             }
20420             return {
20421                 success : success,
20422                 records : records,
20423                 totalRecords : totalRecords
20424             };
20425     }
20426 });/*
20427  * Based on:
20428  * Ext JS Library 1.1.1
20429  * Copyright(c) 2006-2007, Ext JS, LLC.
20430  *
20431  * Originally Released Under LGPL - original licence link has changed is not relivant.
20432  *
20433  * Fork - LGPL
20434  * <script type="text/javascript">
20435  */
20436
20437 /**
20438  * @class Roo.data.XmlReader
20439  * @extends Roo.data.DataReader
20440  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20441  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20442  * <p>
20443  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20444  * header in the HTTP response must be set to "text/xml".</em>
20445  * <p>
20446  * Example code:
20447  * <pre><code>
20448 var RecordDef = Roo.data.Record.create([
20449    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20450    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20451 ]);
20452 var myReader = new Roo.data.XmlReader({
20453    totalRecords: "results", // The element which contains the total dataset size (optional)
20454    record: "row",           // The repeated element which contains row information
20455    id: "id"                 // The element within the row that provides an ID for the record (optional)
20456 }, RecordDef);
20457 </code></pre>
20458  * <p>
20459  * This would consume an XML file like this:
20460  * <pre><code>
20461 &lt;?xml?>
20462 &lt;dataset>
20463  &lt;results>2&lt;/results>
20464  &lt;row>
20465    &lt;id>1&lt;/id>
20466    &lt;name>Bill&lt;/name>
20467    &lt;occupation>Gardener&lt;/occupation>
20468  &lt;/row>
20469  &lt;row>
20470    &lt;id>2&lt;/id>
20471    &lt;name>Ben&lt;/name>
20472    &lt;occupation>Horticulturalist&lt;/occupation>
20473  &lt;/row>
20474 &lt;/dataset>
20475 </code></pre>
20476  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20477  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20478  * paged from the remote server.
20479  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20480  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20481  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20482  * a record identifier value.
20483  * @constructor
20484  * Create a new XmlReader
20485  * @param {Object} meta Metadata configuration options
20486  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20487  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20488  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20489  */
20490 Roo.data.XmlReader = function(meta, recordType){
20491     meta = meta || {};
20492     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20493 };
20494 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20495     /**
20496      * This method is only used by a DataProxy which has retrieved data from a remote server.
20497          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20498          * to contain a method called 'responseXML' that returns an XML document object.
20499      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20500      * a cache of Roo.data.Records.
20501      */
20502     read : function(response){
20503         var doc = response.responseXML;
20504         if(!doc) {
20505             throw {message: "XmlReader.read: XML Document not available"};
20506         }
20507         return this.readRecords(doc);
20508     },
20509
20510     /**
20511      * Create a data block containing Roo.data.Records from an XML document.
20512          * @param {Object} doc A parsed XML document.
20513      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20514      * a cache of Roo.data.Records.
20515      */
20516     readRecords : function(doc){
20517         /**
20518          * After any data loads/reads, the raw XML Document is available for further custom processing.
20519          * @type XMLDocument
20520          */
20521         this.xmlData = doc;
20522         var root = doc.documentElement || doc;
20523         var q = Roo.DomQuery;
20524         var recordType = this.recordType, fields = recordType.prototype.fields;
20525         var sid = this.meta.id;
20526         var totalRecords = 0, success = true;
20527         if(this.meta.totalRecords){
20528             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20529         }
20530         
20531         if(this.meta.success){
20532             var sv = q.selectValue(this.meta.success, root, true);
20533             success = sv !== false && sv !== 'false';
20534         }
20535         var records = [];
20536         var ns = q.select(this.meta.record, root);
20537         for(var i = 0, len = ns.length; i < len; i++) {
20538                 var n = ns[i];
20539                 var values = {};
20540                 var id = sid ? q.selectValue(sid, n) : undefined;
20541                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20542                     var f = fields.items[j];
20543                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20544                     v = f.convert(v);
20545                     values[f.name] = v;
20546                 }
20547                 var record = new recordType(values, id);
20548                 record.node = n;
20549                 records[records.length] = record;
20550             }
20551
20552             return {
20553                 success : success,
20554                 records : records,
20555                 totalRecords : totalRecords || records.length
20556             };
20557     }
20558 });/*
20559  * Based on:
20560  * Ext JS Library 1.1.1
20561  * Copyright(c) 2006-2007, Ext JS, LLC.
20562  *
20563  * Originally Released Under LGPL - original licence link has changed is not relivant.
20564  *
20565  * Fork - LGPL
20566  * <script type="text/javascript">
20567  */
20568
20569 /**
20570  * @class Roo.data.ArrayReader
20571  * @extends Roo.data.DataReader
20572  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20573  * Each element of that Array represents a row of data fields. The
20574  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20575  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20576  * <p>
20577  * Example code:.
20578  * <pre><code>
20579 var RecordDef = Roo.data.Record.create([
20580     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20581     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20582 ]);
20583 var myReader = new Roo.data.ArrayReader({
20584     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20585 }, RecordDef);
20586 </code></pre>
20587  * <p>
20588  * This would consume an Array like this:
20589  * <pre><code>
20590 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20591   </code></pre>
20592  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20593  * @constructor
20594  * Create a new JsonReader
20595  * @param {Object} meta Metadata configuration options.
20596  * @param {Object} recordType Either an Array of field definition objects
20597  * as specified to {@link Roo.data.Record#create},
20598  * or an {@link Roo.data.Record} object
20599  * created using {@link Roo.data.Record#create}.
20600  */
20601 Roo.data.ArrayReader = function(meta, recordType){
20602     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20603 };
20604
20605 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20606     /**
20607      * Create a data block containing Roo.data.Records from an XML document.
20608      * @param {Object} o An Array of row objects which represents the dataset.
20609      * @return {Object} data A data block which is used by an Roo.data.Store object as
20610      * a cache of Roo.data.Records.
20611      */
20612     readRecords : function(o){
20613         var sid = this.meta ? this.meta.id : null;
20614         var recordType = this.recordType, fields = recordType.prototype.fields;
20615         var records = [];
20616         var root = o;
20617             for(var i = 0; i < root.length; i++){
20618                     var n = root[i];
20619                 var values = {};
20620                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20621                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20622                 var f = fields.items[j];
20623                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20624                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20625                 v = f.convert(v);
20626                 values[f.name] = v;
20627             }
20628                 var record = new recordType(values, id);
20629                 record.json = n;
20630                 records[records.length] = record;
20631             }
20632             return {
20633                 records : records,
20634                 totalRecords : records.length
20635             };
20636     }
20637 });/*
20638  * Based on:
20639  * Ext JS Library 1.1.1
20640  * Copyright(c) 2006-2007, Ext JS, LLC.
20641  *
20642  * Originally Released Under LGPL - original licence link has changed is not relivant.
20643  *
20644  * Fork - LGPL
20645  * <script type="text/javascript">
20646  */
20647
20648
20649 /**
20650  * @class Roo.data.Tree
20651  * @extends Roo.util.Observable
20652  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20653  * in the tree have most standard DOM functionality.
20654  * @constructor
20655  * @param {Node} root (optional) The root node
20656  */
20657 Roo.data.Tree = function(root){
20658    this.nodeHash = {};
20659    /**
20660     * The root node for this tree
20661     * @type Node
20662     */
20663    this.root = null;
20664    if(root){
20665        this.setRootNode(root);
20666    }
20667    this.addEvents({
20668        /**
20669         * @event append
20670         * Fires when a new child node is appended to a node in this tree.
20671         * @param {Tree} tree The owner tree
20672         * @param {Node} parent The parent node
20673         * @param {Node} node The newly appended node
20674         * @param {Number} index The index of the newly appended node
20675         */
20676        "append" : true,
20677        /**
20678         * @event remove
20679         * Fires when a child node is removed from a node in this tree.
20680         * @param {Tree} tree The owner tree
20681         * @param {Node} parent The parent node
20682         * @param {Node} node The child node removed
20683         */
20684        "remove" : true,
20685        /**
20686         * @event move
20687         * Fires when a node is moved to a new location in the tree
20688         * @param {Tree} tree The owner tree
20689         * @param {Node} node The node moved
20690         * @param {Node} oldParent The old parent of this node
20691         * @param {Node} newParent The new parent of this node
20692         * @param {Number} index The index it was moved to
20693         */
20694        "move" : true,
20695        /**
20696         * @event insert
20697         * Fires when a new child node is inserted in a node in this tree.
20698         * @param {Tree} tree The owner tree
20699         * @param {Node} parent The parent node
20700         * @param {Node} node The child node inserted
20701         * @param {Node} refNode The child node the node was inserted before
20702         */
20703        "insert" : true,
20704        /**
20705         * @event beforeappend
20706         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20707         * @param {Tree} tree The owner tree
20708         * @param {Node} parent The parent node
20709         * @param {Node} node The child node to be appended
20710         */
20711        "beforeappend" : true,
20712        /**
20713         * @event beforeremove
20714         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20715         * @param {Tree} tree The owner tree
20716         * @param {Node} parent The parent node
20717         * @param {Node} node The child node to be removed
20718         */
20719        "beforeremove" : true,
20720        /**
20721         * @event beforemove
20722         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20723         * @param {Tree} tree The owner tree
20724         * @param {Node} node The node being moved
20725         * @param {Node} oldParent The parent of the node
20726         * @param {Node} newParent The new parent the node is moving to
20727         * @param {Number} index The index it is being moved to
20728         */
20729        "beforemove" : true,
20730        /**
20731         * @event beforeinsert
20732         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20733         * @param {Tree} tree The owner tree
20734         * @param {Node} parent The parent node
20735         * @param {Node} node The child node to be inserted
20736         * @param {Node} refNode The child node the node is being inserted before
20737         */
20738        "beforeinsert" : true
20739    });
20740
20741     Roo.data.Tree.superclass.constructor.call(this);
20742 };
20743
20744 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20745     pathSeparator: "/",
20746
20747     proxyNodeEvent : function(){
20748         return this.fireEvent.apply(this, arguments);
20749     },
20750
20751     /**
20752      * Returns the root node for this tree.
20753      * @return {Node}
20754      */
20755     getRootNode : function(){
20756         return this.root;
20757     },
20758
20759     /**
20760      * Sets the root node for this tree.
20761      * @param {Node} node
20762      * @return {Node}
20763      */
20764     setRootNode : function(node){
20765         this.root = node;
20766         node.ownerTree = this;
20767         node.isRoot = true;
20768         this.registerNode(node);
20769         return node;
20770     },
20771
20772     /**
20773      * Gets a node in this tree by its id.
20774      * @param {String} id
20775      * @return {Node}
20776      */
20777     getNodeById : function(id){
20778         return this.nodeHash[id];
20779     },
20780
20781     registerNode : function(node){
20782         this.nodeHash[node.id] = node;
20783     },
20784
20785     unregisterNode : function(node){
20786         delete this.nodeHash[node.id];
20787     },
20788
20789     toString : function(){
20790         return "[Tree"+(this.id?" "+this.id:"")+"]";
20791     }
20792 });
20793
20794 /**
20795  * @class Roo.data.Node
20796  * @extends Roo.util.Observable
20797  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20798  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20799  * @constructor
20800  * @param {Object} attributes The attributes/config for the node
20801  */
20802 Roo.data.Node = function(attributes){
20803     /**
20804      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20805      * @type {Object}
20806      */
20807     this.attributes = attributes || {};
20808     this.leaf = this.attributes.leaf;
20809     /**
20810      * The node id. @type String
20811      */
20812     this.id = this.attributes.id;
20813     if(!this.id){
20814         this.id = Roo.id(null, "ynode-");
20815         this.attributes.id = this.id;
20816     }
20817     /**
20818      * All child nodes of this node. @type Array
20819      */
20820     this.childNodes = [];
20821     if(!this.childNodes.indexOf){ // indexOf is a must
20822         this.childNodes.indexOf = function(o){
20823             for(var i = 0, len = this.length; i < len; i++){
20824                 if(this[i] == o) return i;
20825             }
20826             return -1;
20827         };
20828     }
20829     /**
20830      * The parent node for this node. @type Node
20831      */
20832     this.parentNode = null;
20833     /**
20834      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20835      */
20836     this.firstChild = null;
20837     /**
20838      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20839      */
20840     this.lastChild = null;
20841     /**
20842      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20843      */
20844     this.previousSibling = null;
20845     /**
20846      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20847      */
20848     this.nextSibling = null;
20849
20850     this.addEvents({
20851        /**
20852         * @event append
20853         * Fires when a new child node is appended
20854         * @param {Tree} tree The owner tree
20855         * @param {Node} this This node
20856         * @param {Node} node The newly appended node
20857         * @param {Number} index The index of the newly appended node
20858         */
20859        "append" : true,
20860        /**
20861         * @event remove
20862         * Fires when a child node is removed
20863         * @param {Tree} tree The owner tree
20864         * @param {Node} this This node
20865         * @param {Node} node The removed node
20866         */
20867        "remove" : true,
20868        /**
20869         * @event move
20870         * Fires when this node is moved to a new location in the tree
20871         * @param {Tree} tree The owner tree
20872         * @param {Node} this This node
20873         * @param {Node} oldParent The old parent of this node
20874         * @param {Node} newParent The new parent of this node
20875         * @param {Number} index The index it was moved to
20876         */
20877        "move" : true,
20878        /**
20879         * @event insert
20880         * Fires when a new child node is inserted.
20881         * @param {Tree} tree The owner tree
20882         * @param {Node} this This node
20883         * @param {Node} node The child node inserted
20884         * @param {Node} refNode The child node the node was inserted before
20885         */
20886        "insert" : true,
20887        /**
20888         * @event beforeappend
20889         * Fires before a new child is appended, return false to cancel the append.
20890         * @param {Tree} tree The owner tree
20891         * @param {Node} this This node
20892         * @param {Node} node The child node to be appended
20893         */
20894        "beforeappend" : true,
20895        /**
20896         * @event beforeremove
20897         * Fires before a child is removed, return false to cancel the remove.
20898         * @param {Tree} tree The owner tree
20899         * @param {Node} this This node
20900         * @param {Node} node The child node to be removed
20901         */
20902        "beforeremove" : true,
20903        /**
20904         * @event beforemove
20905         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20906         * @param {Tree} tree The owner tree
20907         * @param {Node} this This node
20908         * @param {Node} oldParent The parent of this node
20909         * @param {Node} newParent The new parent this node is moving to
20910         * @param {Number} index The index it is being moved to
20911         */
20912        "beforemove" : true,
20913        /**
20914         * @event beforeinsert
20915         * Fires before a new child is inserted, return false to cancel the insert.
20916         * @param {Tree} tree The owner tree
20917         * @param {Node} this This node
20918         * @param {Node} node The child node to be inserted
20919         * @param {Node} refNode The child node the node is being inserted before
20920         */
20921        "beforeinsert" : true
20922    });
20923     this.listeners = this.attributes.listeners;
20924     Roo.data.Node.superclass.constructor.call(this);
20925 };
20926
20927 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20928     fireEvent : function(evtName){
20929         // first do standard event for this node
20930         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20931             return false;
20932         }
20933         // then bubble it up to the tree if the event wasn't cancelled
20934         var ot = this.getOwnerTree();
20935         if(ot){
20936             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20937                 return false;
20938             }
20939         }
20940         return true;
20941     },
20942
20943     /**
20944      * Returns true if this node is a leaf
20945      * @return {Boolean}
20946      */
20947     isLeaf : function(){
20948         return this.leaf === true;
20949     },
20950
20951     // private
20952     setFirstChild : function(node){
20953         this.firstChild = node;
20954     },
20955
20956     //private
20957     setLastChild : function(node){
20958         this.lastChild = node;
20959     },
20960
20961
20962     /**
20963      * Returns true if this node is the last child of its parent
20964      * @return {Boolean}
20965      */
20966     isLast : function(){
20967        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20968     },
20969
20970     /**
20971      * Returns true if this node is the first child of its parent
20972      * @return {Boolean}
20973      */
20974     isFirst : function(){
20975        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20976     },
20977
20978     hasChildNodes : function(){
20979         return !this.isLeaf() && this.childNodes.length > 0;
20980     },
20981
20982     /**
20983      * Insert node(s) as the last child node of this node.
20984      * @param {Node/Array} node The node or Array of nodes to append
20985      * @return {Node} The appended node if single append, or null if an array was passed
20986      */
20987     appendChild : function(node){
20988         var multi = false;
20989         if(node instanceof Array){
20990             multi = node;
20991         }else if(arguments.length > 1){
20992             multi = arguments;
20993         }
20994         // if passed an array or multiple args do them one by one
20995         if(multi){
20996             for(var i = 0, len = multi.length; i < len; i++) {
20997                 this.appendChild(multi[i]);
20998             }
20999         }else{
21000             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21001                 return false;
21002             }
21003             var index = this.childNodes.length;
21004             var oldParent = node.parentNode;
21005             // it's a move, make sure we move it cleanly
21006             if(oldParent){
21007                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21008                     return false;
21009                 }
21010                 oldParent.removeChild(node);
21011             }
21012             index = this.childNodes.length;
21013             if(index == 0){
21014                 this.setFirstChild(node);
21015             }
21016             this.childNodes.push(node);
21017             node.parentNode = this;
21018             var ps = this.childNodes[index-1];
21019             if(ps){
21020                 node.previousSibling = ps;
21021                 ps.nextSibling = node;
21022             }else{
21023                 node.previousSibling = null;
21024             }
21025             node.nextSibling = null;
21026             this.setLastChild(node);
21027             node.setOwnerTree(this.getOwnerTree());
21028             this.fireEvent("append", this.ownerTree, this, node, index);
21029             if(oldParent){
21030                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21031             }
21032             return node;
21033         }
21034     },
21035
21036     /**
21037      * Removes a child node from this node.
21038      * @param {Node} node The node to remove
21039      * @return {Node} The removed node
21040      */
21041     removeChild : function(node){
21042         var index = this.childNodes.indexOf(node);
21043         if(index == -1){
21044             return false;
21045         }
21046         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21047             return false;
21048         }
21049
21050         // remove it from childNodes collection
21051         this.childNodes.splice(index, 1);
21052
21053         // update siblings
21054         if(node.previousSibling){
21055             node.previousSibling.nextSibling = node.nextSibling;
21056         }
21057         if(node.nextSibling){
21058             node.nextSibling.previousSibling = node.previousSibling;
21059         }
21060
21061         // update child refs
21062         if(this.firstChild == node){
21063             this.setFirstChild(node.nextSibling);
21064         }
21065         if(this.lastChild == node){
21066             this.setLastChild(node.previousSibling);
21067         }
21068
21069         node.setOwnerTree(null);
21070         // clear any references from the node
21071         node.parentNode = null;
21072         node.previousSibling = null;
21073         node.nextSibling = null;
21074         this.fireEvent("remove", this.ownerTree, this, node);
21075         return node;
21076     },
21077
21078     /**
21079      * Inserts the first node before the second node in this nodes childNodes collection.
21080      * @param {Node} node The node to insert
21081      * @param {Node} refNode The node to insert before (if null the node is appended)
21082      * @return {Node} The inserted node
21083      */
21084     insertBefore : function(node, refNode){
21085         if(!refNode){ // like standard Dom, refNode can be null for append
21086             return this.appendChild(node);
21087         }
21088         // nothing to do
21089         if(node == refNode){
21090             return false;
21091         }
21092
21093         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21094             return false;
21095         }
21096         var index = this.childNodes.indexOf(refNode);
21097         var oldParent = node.parentNode;
21098         var refIndex = index;
21099
21100         // when moving internally, indexes will change after remove
21101         if(oldParent == this && this.childNodes.indexOf(node) < index){
21102             refIndex--;
21103         }
21104
21105         // it's a move, make sure we move it cleanly
21106         if(oldParent){
21107             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21108                 return false;
21109             }
21110             oldParent.removeChild(node);
21111         }
21112         if(refIndex == 0){
21113             this.setFirstChild(node);
21114         }
21115         this.childNodes.splice(refIndex, 0, node);
21116         node.parentNode = this;
21117         var ps = this.childNodes[refIndex-1];
21118         if(ps){
21119             node.previousSibling = ps;
21120             ps.nextSibling = node;
21121         }else{
21122             node.previousSibling = null;
21123         }
21124         node.nextSibling = refNode;
21125         refNode.previousSibling = node;
21126         node.setOwnerTree(this.getOwnerTree());
21127         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21128         if(oldParent){
21129             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21130         }
21131         return node;
21132     },
21133
21134     /**
21135      * Returns the child node at the specified index.
21136      * @param {Number} index
21137      * @return {Node}
21138      */
21139     item : function(index){
21140         return this.childNodes[index];
21141     },
21142
21143     /**
21144      * Replaces one child node in this node with another.
21145      * @param {Node} newChild The replacement node
21146      * @param {Node} oldChild The node to replace
21147      * @return {Node} The replaced node
21148      */
21149     replaceChild : function(newChild, oldChild){
21150         this.insertBefore(newChild, oldChild);
21151         this.removeChild(oldChild);
21152         return oldChild;
21153     },
21154
21155     /**
21156      * Returns the index of a child node
21157      * @param {Node} node
21158      * @return {Number} The index of the node or -1 if it was not found
21159      */
21160     indexOf : function(child){
21161         return this.childNodes.indexOf(child);
21162     },
21163
21164     /**
21165      * Returns the tree this node is in.
21166      * @return {Tree}
21167      */
21168     getOwnerTree : function(){
21169         // if it doesn't have one, look for one
21170         if(!this.ownerTree){
21171             var p = this;
21172             while(p){
21173                 if(p.ownerTree){
21174                     this.ownerTree = p.ownerTree;
21175                     break;
21176                 }
21177                 p = p.parentNode;
21178             }
21179         }
21180         return this.ownerTree;
21181     },
21182
21183     /**
21184      * Returns depth of this node (the root node has a depth of 0)
21185      * @return {Number}
21186      */
21187     getDepth : function(){
21188         var depth = 0;
21189         var p = this;
21190         while(p.parentNode){
21191             ++depth;
21192             p = p.parentNode;
21193         }
21194         return depth;
21195     },
21196
21197     // private
21198     setOwnerTree : function(tree){
21199         // if it's move, we need to update everyone
21200         if(tree != this.ownerTree){
21201             if(this.ownerTree){
21202                 this.ownerTree.unregisterNode(this);
21203             }
21204             this.ownerTree = tree;
21205             var cs = this.childNodes;
21206             for(var i = 0, len = cs.length; i < len; i++) {
21207                 cs[i].setOwnerTree(tree);
21208             }
21209             if(tree){
21210                 tree.registerNode(this);
21211             }
21212         }
21213     },
21214
21215     /**
21216      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21217      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21218      * @return {String} The path
21219      */
21220     getPath : function(attr){
21221         attr = attr || "id";
21222         var p = this.parentNode;
21223         var b = [this.attributes[attr]];
21224         while(p){
21225             b.unshift(p.attributes[attr]);
21226             p = p.parentNode;
21227         }
21228         var sep = this.getOwnerTree().pathSeparator;
21229         return sep + b.join(sep);
21230     },
21231
21232     /**
21233      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21234      * function call will be the scope provided or the current node. The arguments to the function
21235      * will be the args provided or the current node. If the function returns false at any point,
21236      * the bubble is stopped.
21237      * @param {Function} fn The function to call
21238      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21239      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21240      */
21241     bubble : function(fn, scope, args){
21242         var p = this;
21243         while(p){
21244             if(fn.call(scope || p, args || p) === false){
21245                 break;
21246             }
21247             p = p.parentNode;
21248         }
21249     },
21250
21251     /**
21252      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21253      * function call will be the scope provided or the current node. The arguments to the function
21254      * will be the args provided or the current node. If the function returns false at any point,
21255      * the cascade is stopped on that branch.
21256      * @param {Function} fn The function to call
21257      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21258      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21259      */
21260     cascade : function(fn, scope, args){
21261         if(fn.call(scope || this, args || this) !== false){
21262             var cs = this.childNodes;
21263             for(var i = 0, len = cs.length; i < len; i++) {
21264                 cs[i].cascade(fn, scope, args);
21265             }
21266         }
21267     },
21268
21269     /**
21270      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21271      * function call will be the scope provided or the current node. The arguments to the function
21272      * will be the args provided or the current node. If the function returns false at any point,
21273      * the iteration stops.
21274      * @param {Function} fn The function to call
21275      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21276      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21277      */
21278     eachChild : function(fn, scope, args){
21279         var cs = this.childNodes;
21280         for(var i = 0, len = cs.length; i < len; i++) {
21281                 if(fn.call(scope || this, args || cs[i]) === false){
21282                     break;
21283                 }
21284         }
21285     },
21286
21287     /**
21288      * Finds the first child that has the attribute with the specified value.
21289      * @param {String} attribute The attribute name
21290      * @param {Mixed} value The value to search for
21291      * @return {Node} The found child or null if none was found
21292      */
21293     findChild : function(attribute, value){
21294         var cs = this.childNodes;
21295         for(var i = 0, len = cs.length; i < len; i++) {
21296                 if(cs[i].attributes[attribute] == value){
21297                     return cs[i];
21298                 }
21299         }
21300         return null;
21301     },
21302
21303     /**
21304      * Finds the first child by a custom function. The child matches if the function passed
21305      * returns true.
21306      * @param {Function} fn
21307      * @param {Object} scope (optional)
21308      * @return {Node} The found child or null if none was found
21309      */
21310     findChildBy : function(fn, scope){
21311         var cs = this.childNodes;
21312         for(var i = 0, len = cs.length; i < len; i++) {
21313                 if(fn.call(scope||cs[i], cs[i]) === true){
21314                     return cs[i];
21315                 }
21316         }
21317         return null;
21318     },
21319
21320     /**
21321      * Sorts this nodes children using the supplied sort function
21322      * @param {Function} fn
21323      * @param {Object} scope (optional)
21324      */
21325     sort : function(fn, scope){
21326         var cs = this.childNodes;
21327         var len = cs.length;
21328         if(len > 0){
21329             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21330             cs.sort(sortFn);
21331             for(var i = 0; i < len; i++){
21332                 var n = cs[i];
21333                 n.previousSibling = cs[i-1];
21334                 n.nextSibling = cs[i+1];
21335                 if(i == 0){
21336                     this.setFirstChild(n);
21337                 }
21338                 if(i == len-1){
21339                     this.setLastChild(n);
21340                 }
21341             }
21342         }
21343     },
21344
21345     /**
21346      * Returns true if this node is an ancestor (at any point) of the passed node.
21347      * @param {Node} node
21348      * @return {Boolean}
21349      */
21350     contains : function(node){
21351         return node.isAncestor(this);
21352     },
21353
21354     /**
21355      * Returns true if the passed node is an ancestor (at any point) of this node.
21356      * @param {Node} node
21357      * @return {Boolean}
21358      */
21359     isAncestor : function(node){
21360         var p = this.parentNode;
21361         while(p){
21362             if(p == node){
21363                 return true;
21364             }
21365             p = p.parentNode;
21366         }
21367         return false;
21368     },
21369
21370     toString : function(){
21371         return "[Node"+(this.id?" "+this.id:"")+"]";
21372     }
21373 });