roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88
89         /**
90          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
91          * @type Boolean
92          */
93         enableGarbageCollector : true,
94
95         /**
96          * True to automatically purge event listeners after uncaching an element (defaults to false).
97          * Note: this only happens if enableGarbageCollector is true.
98          * @type Boolean
99          */
100         enableListenerCollection:false,
101
102         /**
103          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
104          * the IE insecure content warning (defaults to javascript:false).
105          * @type String
106          */
107         SSL_SECURE_URL : "javascript:false",
108
109         /**
110          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
111          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
112          * @type String
113          */
114         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
115
116         emptyFn : function(){},
117
118         /**
119          * Copies all the properties of config to obj if they don't already exist.
120          * @param {Object} obj The receiver of the properties
121          * @param {Object} config The source of the properties
122          * @return {Object} returns obj
123          */
124         applyIf : function(o, c){
125             if(o && c){
126                 for(var p in c){
127                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
128                 }
129             }
130             return o;
131         },
132
133         /**
134          * Applies event listeners to elements by selectors when the document is ready.
135          * The event name is specified with an @ suffix.
136 <pre><code>
137 Roo.addBehaviors({
138    // add a listener for click on all anchors in element with id foo
139    '#foo a@click' : function(e, t){
140        // do something
141    },
142
143    // add the same listener to multiple selectors (separated by comma BEFORE the @)
144    '#foo a, #bar span.some-class@mouseover' : function(){
145        // do something
146    }
147 });
148 </code></pre>
149          * @param {Object} obj The list of behaviors to apply
150          */
151         addBehaviors : function(o){
152             if(!Roo.isReady){
153                 Roo.onReady(function(){
154                     Roo.addBehaviors(o);
155                 });
156                 return;
157             }
158             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
159             for(var b in o){
160                 var parts = b.split('@');
161                 if(parts[1]){ // for Object prototype breakers
162                     var s = parts[0];
163                     if(!cache[s]){
164                         cache[s] = Roo.select(s);
165                     }
166                     cache[s].on(parts[1], o[b]);
167                 }
168             }
169             cache = null;
170         },
171
172         /**
173          * Generates unique ids. If the element already has an id, it is unchanged
174          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
175          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
176          * @return {String} The generated Id.
177          */
178         id : function(el, prefix){
179             prefix = prefix || "roo-gen";
180             el = Roo.getDom(el);
181             var id = prefix + (++idSeed);
182             return el ? (el.id ? el.id : (el.id = id)) : id;
183         },
184          
185        
186         /**
187          * Extends one class with another class and optionally overrides members with the passed literal. This class
188          * also adds the function "override()" to the class that can be used to override
189          * members on an instance.
190          * @param {Object} subclass The class inheriting the functionality
191          * @param {Object} superclass The class being extended
192          * @param {Object} overrides (optional) A literal with members
193          * @method extend
194          */
195         extend : function(){
196             // inline overrides
197             var io = function(o){
198                 for(var m in o){
199                     this[m] = o[m];
200                 }
201             };
202             return function(sb, sp, overrides){
203                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
204                     overrides = sp;
205                     sp = sb;
206                     sb = function(){sp.apply(this, arguments);};
207                 }
208                 var F = function(){}, sbp, spp = sp.prototype;
209                 F.prototype = spp;
210                 sbp = sb.prototype = new F();
211                 sbp.constructor=sb;
212                 sb.superclass=spp;
213                 
214                 if(spp.constructor == Object.prototype.constructor){
215                     spp.constructor=sp;
216                    
217                 }
218                 
219                 sb.override = function(o){
220                     Roo.override(sb, o);
221                 };
222                 sbp.override = io;
223                 Roo.override(sb, overrides);
224                 return sb;
225             };
226         }(),
227
228         /**
229          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
230          * Usage:<pre><code>
231 Roo.override(MyClass, {
232     newMethod1: function(){
233         // etc.
234     },
235     newMethod2: function(foo){
236         // etc.
237     }
238 });
239  </code></pre>
240          * @param {Object} origclass The class to override
241          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
242          * containing one or more methods.
243          * @method override
244          */
245         override : function(origclass, overrides){
246             if(overrides){
247                 var p = origclass.prototype;
248                 for(var method in overrides){
249                     p[method] = overrides[method];
250                 }
251             }
252         },
253         /**
254          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
255          * <pre><code>
256 Roo.namespace('Company', 'Company.data');
257 Company.Widget = function() { ... }
258 Company.data.CustomStore = function(config) { ... }
259 </code></pre>
260          * @param {String} namespace1
261          * @param {String} namespace2
262          * @param {String} etc
263          * @method namespace
264          */
265         namespace : function(){
266             var a=arguments, o=null, i, j, d, rt;
267             for (i=0; i<a.length; ++i) {
268                 d=a[i].split(".");
269                 rt = d[0];
270                 /** eval:var:o */
271                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
272                 for (j=1; j<d.length; ++j) {
273                     o[d[j]]=o[d[j]] || {};
274                     o=o[d[j]];
275                 }
276             }
277         },
278         /**
279          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
280          * <pre><code>
281 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
282 Roo.factory(conf, Roo.data);
283 </code></pre>
284          * @param {String} classname
285          * @param {String} namespace (optional)
286          * @method factory
287          */
288          
289         factory : function(c, ns)
290         {
291             // no xtype, no ns or c.xns - or forced off by c.xns
292             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
293                 return c;
294             }
295             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
296             if (c.constructor == ns[c.xtype]) {// already created...
297                 return c;
298             }
299             if (ns[c.xtype]) {
300                 console.log("Roo.Factory(" + c.xtype + ")");
301                 var ret = new ns[c.xtype](c);
302                 ret.xns = false;
303                 return ret;
304             }
305             c.xns = false; // prevent recursion..
306             return c;
307         },
308          
309         /**
310          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
311          * @param {Object} o
312          * @return {String}
313          */
314         urlEncode : function(o){
315             if(!o){
316                 return "";
317             }
318             var buf = [];
319             for(var key in o){
320                 var ov = o[key], k = encodeURIComponent(key);
321                 var type = typeof ov;
322                 if(type == 'undefined'){
323                     buf.push(k, "=&");
324                 }else if(type != "function" && type != "object"){
325                     buf.push(k, "=", encodeURIComponent(ov), "&");
326                 }else if(ov instanceof Array){
327                     if (ov.length) {
328                             for(var i = 0, len = ov.length; i < len; i++) {
329                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
330                             }
331                         } else {
332                             buf.push(k, "=&");
333                         }
334                 }
335             }
336             buf.pop();
337             return buf.join("");
338         },
339
340         /**
341          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
342          * @param {String} string
343          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
344          * @return {Object} A literal with members
345          */
346         urlDecode : function(string, overwrite){
347             if(!string || !string.length){
348                 return {};
349             }
350             var obj = {};
351             var pairs = string.split('&');
352             var pair, name, value;
353             for(var i = 0, len = pairs.length; i < len; i++){
354                 pair = pairs[i].split('=');
355                 name = decodeURIComponent(pair[0]);
356                 value = decodeURIComponent(pair[1]);
357                 if(overwrite !== true){
358                     if(typeof obj[name] == "undefined"){
359                         obj[name] = value;
360                     }else if(typeof obj[name] == "string"){
361                         obj[name] = [obj[name]];
362                         obj[name].push(value);
363                     }else{
364                         obj[name].push(value);
365                     }
366                 }else{
367                     obj[name] = value;
368                 }
369             }
370             return obj;
371         },
372
373         /**
374          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
375          * passed array is not really an array, your function is called once with it.
376          * The supplied function is called with (Object item, Number index, Array allItems).
377          * @param {Array/NodeList/Mixed} array
378          * @param {Function} fn
379          * @param {Object} scope
380          */
381         each : function(array, fn, scope){
382             if(typeof array.length == "undefined" || typeof array == "string"){
383                 array = [array];
384             }
385             for(var i = 0, len = array.length; i < len; i++){
386                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
387             }
388         },
389
390         // deprecated
391         combine : function(){
392             var as = arguments, l = as.length, r = [];
393             for(var i = 0; i < l; i++){
394                 var a = as[i];
395                 if(a instanceof Array){
396                     r = r.concat(a);
397                 }else if(a.length !== undefined && !a.substr){
398                     r = r.concat(Array.prototype.slice.call(a, 0));
399                 }else{
400                     r.push(a);
401                 }
402             }
403             return r;
404         },
405
406         /**
407          * Escapes the passed string for use in a regular expression
408          * @param {String} str
409          * @return {String}
410          */
411         escapeRe : function(s) {
412             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
413         },
414
415         // internal
416         callback : function(cb, scope, args, delay){
417             if(typeof cb == "function"){
418                 if(delay){
419                     cb.defer(delay, scope, args || []);
420                 }else{
421                     cb.apply(scope, args || []);
422                 }
423             }
424         },
425
426         /**
427          * Return the dom node for the passed string (id), dom node, or Roo.Element
428          * @param {String/HTMLElement/Roo.Element} el
429          * @return HTMLElement
430          */
431         getDom : function(el){
432             if(!el){
433                 return null;
434             }
435             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
436         },
437
438         /**
439         * Shorthand for {@link Roo.ComponentMgr#get}
440         * @param {String} id
441         * @return Roo.Component
442         */
443         getCmp : function(id){
444             return Roo.ComponentMgr.get(id);
445         },
446          
447         num : function(v, defaultValue){
448             if(typeof v != 'number'){
449                 return defaultValue;
450             }
451             return v;
452         },
453
454         destroy : function(){
455             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
456                 var as = a[i];
457                 if(as){
458                     if(as.dom){
459                         as.removeAllListeners();
460                         as.remove();
461                         continue;
462                     }
463                     if(typeof as.purgeListeners == 'function'){
464                         as.purgeListeners();
465                     }
466                     if(typeof as.destroy == 'function'){
467                         as.destroy();
468                     }
469                 }
470             }
471         },
472
473         // inpired by a similar function in mootools library
474         /**
475          * Returns the type of object that is passed in. If the object passed in is null or undefined it
476          * return false otherwise it returns one of the following values:<ul>
477          * <li><b>string</b>: If the object passed is a string</li>
478          * <li><b>number</b>: If the object passed is a number</li>
479          * <li><b>boolean</b>: If the object passed is a boolean value</li>
480          * <li><b>function</b>: If the object passed is a function reference</li>
481          * <li><b>object</b>: If the object passed is an object</li>
482          * <li><b>array</b>: If the object passed is an array</li>
483          * <li><b>regexp</b>: If the object passed is a regular expression</li>
484          * <li><b>element</b>: If the object passed is a DOM Element</li>
485          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
486          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
487          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
488          * @param {Mixed} object
489          * @return {String}
490          */
491         type : function(o){
492             if(o === undefined || o === null){
493                 return false;
494             }
495             if(o.htmlElement){
496                 return 'element';
497             }
498             var t = typeof o;
499             if(t == 'object' && o.nodeName) {
500                 switch(o.nodeType) {
501                     case 1: return 'element';
502                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
503                 }
504             }
505             if(t == 'object' || t == 'function') {
506                 switch(o.constructor) {
507                     case Array: return 'array';
508                     case RegExp: return 'regexp';
509                 }
510                 if(typeof o.length == 'number' && typeof o.item == 'function') {
511                     return 'nodelist';
512                 }
513             }
514             return t;
515         },
516
517         /**
518          * Returns true if the passed value is null, undefined or an empty string (optional).
519          * @param {Mixed} value The value to test
520          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
521          * @return {Boolean}
522          */
523         isEmpty : function(v, allowBlank){
524             return v === null || v === undefined || (!allowBlank ? v === '' : false);
525         },
526         
527         /** @type Boolean */
528         isOpera : isOpera,
529         /** @type Boolean */
530         isSafari : isSafari,
531         /** @type Boolean */
532         isIE : isIE,
533         /** @type Boolean */
534         isIE7 : isIE7,
535         /** @type Boolean */
536         isGecko : isGecko,
537         /** @type Boolean */
538         isBorderBox : isBorderBox,
539         /** @type Boolean */
540         isWindows : isWindows,
541         /** @type Boolean */
542         isLinux : isLinux,
543         /** @type Boolean */
544         isMac : isMac,
545
546         /**
547          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
548          * you may want to set this to true.
549          * @type Boolean
550          */
551         useShims : ((isIE && !isIE7) || (isGecko && isMac))
552     });
553
554
555 })();
556
557 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
558                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
559 /*
560  * Based on:
561  * Ext JS Library 1.1.1
562  * Copyright(c) 2006-2007, Ext JS, LLC.
563  *
564  * Originally Released Under LGPL - original licence link has changed is not relivant.
565  *
566  * Fork - LGPL
567  * <script type="text/javascript">
568  */
569
570 (function() {    
571     // wrappedn so fnCleanup is not in global scope...
572     if(Roo.isIE) {
573         function fnCleanUp() {
574             var p = Function.prototype;
575             delete p.createSequence;
576             delete p.defer;
577             delete p.createDelegate;
578             delete p.createCallback;
579             delete p.createInterceptor;
580
581             window.detachEvent("onunload", fnCleanUp);
582         }
583         window.attachEvent("onunload", fnCleanUp);
584     }
585 })();
586
587
588 /**
589  * @class Function
590  * These functions are available on every Function object (any JavaScript function).
591  */
592 Roo.apply(Function.prototype, {
593      /**
594      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
595      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
596      * Will create a function that is bound to those 2 args.
597      * @return {Function} The new function
598     */
599     createCallback : function(/*args...*/){
600         // make args available, in function below
601         var args = arguments;
602         var method = this;
603         return function() {
604             return method.apply(window, args);
605         };
606     },
607
608     /**
609      * Creates a delegate (callback) that sets the scope to obj.
610      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
611      * Will create a function that is automatically scoped to this.
612      * @param {Object} obj (optional) The object for which the scope is set
613      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
614      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
615      *                                             if a number the args are inserted at the specified position
616      * @return {Function} The new function
617      */
618     createDelegate : function(obj, args, appendArgs){
619         var method = this;
620         return function() {
621             var callArgs = args || arguments;
622             if(appendArgs === true){
623                 callArgs = Array.prototype.slice.call(arguments, 0);
624                 callArgs = callArgs.concat(args);
625             }else if(typeof appendArgs == "number"){
626                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
627                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
628                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
629             }
630             return method.apply(obj || window, callArgs);
631         };
632     },
633
634     /**
635      * Calls this function after the number of millseconds specified.
636      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
637      * @param {Object} obj (optional) The object for which the scope is set
638      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
639      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
640      *                                             if a number the args are inserted at the specified position
641      * @return {Number} The timeout id that can be used with clearTimeout
642      */
643     defer : function(millis, obj, args, appendArgs){
644         var fn = this.createDelegate(obj, args, appendArgs);
645         if(millis){
646             return setTimeout(fn, millis);
647         }
648         fn();
649         return 0;
650     },
651     /**
652      * Create a combined function call sequence of the original function + the passed function.
653      * The resulting function returns the results of the original function.
654      * The passed fcn is called with the parameters of the original function
655      * @param {Function} fcn The function to sequence
656      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
657      * @return {Function} The new function
658      */
659     createSequence : function(fcn, scope){
660         if(typeof fcn != "function"){
661             return this;
662         }
663         var method = this;
664         return function() {
665             var retval = method.apply(this || window, arguments);
666             fcn.apply(scope || this || window, arguments);
667             return retval;
668         };
669     },
670
671     /**
672      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
673      * The resulting function returns the results of the original function.
674      * The passed fcn is called with the parameters of the original function.
675      * @addon
676      * @param {Function} fcn The function to call before the original
677      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
678      * @return {Function} The new function
679      */
680     createInterceptor : function(fcn, scope){
681         if(typeof fcn != "function"){
682             return this;
683         }
684         var method = this;
685         return function() {
686             fcn.target = this;
687             fcn.method = method;
688             if(fcn.apply(scope || this || window, arguments) === false){
689                 return;
690             }
691             return method.apply(this || window, arguments);
692         };
693     }
694 });
695 /*
696  * Based on:
697  * Ext JS Library 1.1.1
698  * Copyright(c) 2006-2007, Ext JS, LLC.
699  *
700  * Originally Released Under LGPL - original licence link has changed is not relivant.
701  *
702  * Fork - LGPL
703  * <script type="text/javascript">
704  */
705
706 Roo.applyIf(String, {
707     
708     /** @scope String */
709     
710     /**
711      * Escapes the passed string for ' and \
712      * @param {String} string The string to escape
713      * @return {String} The escaped string
714      * @static
715      */
716     escape : function(string) {
717         return string.replace(/('|\\)/g, "\\$1");
718     },
719
720     /**
721      * Pads the left side of a string with a specified character.  This is especially useful
722      * for normalizing number and date strings.  Example usage:
723      * <pre><code>
724 var s = String.leftPad('123', 5, '0');
725 // s now contains the string: '00123'
726 </code></pre>
727      * @param {String} string The original string
728      * @param {Number} size The total length of the output string
729      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
730      * @return {String} The padded string
731      * @static
732      */
733     leftPad : function (val, size, ch) {
734         var result = new String(val);
735         if(ch === null || ch === undefined || ch === '') {
736             ch = " ";
737         }
738         while (result.length < size) {
739             result = ch + result;
740         }
741         return result;
742     },
743
744     /**
745      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
746      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
747      * <pre><code>
748 var cls = 'my-class', text = 'Some text';
749 var s = String.format('<div class="{0}">{1}</div>', cls, text);
750 // s now contains the string: '<div class="my-class">Some text</div>'
751 </code></pre>
752      * @param {String} string The tokenized string to be formatted
753      * @param {String} value1 The value to replace token {0}
754      * @param {String} value2 Etc...
755      * @return {String} The formatted string
756      * @static
757      */
758     format : function(format){
759         var args = Array.prototype.slice.call(arguments, 1);
760         return format.replace(/\{(\d+)\}/g, function(m, i){
761             return Roo.util.Format.htmlEncode(args[i]);
762         });
763     }
764 });
765
766 /**
767  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
768  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
769  * they are already different, the first value passed in is returned.  Note that this method returns the new value
770  * but does not change the current string.
771  * <pre><code>
772 // alternate sort directions
773 sort = sort.toggle('ASC', 'DESC');
774
775 // instead of conditional logic:
776 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
777 </code></pre>
778  * @param {String} value The value to compare to the current string
779  * @param {String} other The new value to use if the string already equals the first value passed in
780  * @return {String} The new value
781  */
782  
783 String.prototype.toggle = function(value, other){
784     return this == value ? other : value;
785 };/*
786  * Based on:
787  * Ext JS Library 1.1.1
788  * Copyright(c) 2006-2007, Ext JS, LLC.
789  *
790  * Originally Released Under LGPL - original licence link has changed is not relivant.
791  *
792  * Fork - LGPL
793  * <script type="text/javascript">
794  */
795
796  /**
797  * @class Number
798  */
799 Roo.applyIf(Number.prototype, {
800     /**
801      * Checks whether or not the current number is within a desired range.  If the number is already within the
802      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
803      * exceeded.  Note that this method returns the constrained value but does not change the current number.
804      * @param {Number} min The minimum number in the range
805      * @param {Number} max The maximum number in the range
806      * @return {Number} The constrained value if outside the range, otherwise the current value
807      */
808     constrain : function(min, max){
809         return Math.min(Math.max(this, min), max);
810     }
811 });/*
812  * Based on:
813  * Ext JS Library 1.1.1
814  * Copyright(c) 2006-2007, Ext JS, LLC.
815  *
816  * Originally Released Under LGPL - original licence link has changed is not relivant.
817  *
818  * Fork - LGPL
819  * <script type="text/javascript">
820  */
821  /**
822  * @class Array
823  */
824 Roo.applyIf(Array.prototype, {
825     /**
826      * Checks whether or not the specified object exists in the array.
827      * @param {Object} o The object to check for
828      * @return {Number} The index of o in the array (or -1 if it is not found)
829      */
830     indexOf : function(o){
831        for (var i = 0, len = this.length; i < len; i++){
832               if(this[i] == o) return i;
833        }
834            return -1;
835     },
836
837     /**
838      * Removes the specified object from the array.  If the object is not found nothing happens.
839      * @param {Object} o The object to remove
840      */
841     remove : function(o){
842        var index = this.indexOf(o);
843        if(index != -1){
844            this.splice(index, 1);
845        }
846     }
847 });/*
848  * Based on:
849  * Ext JS Library 1.1.1
850  * Copyright(c) 2006-2007, Ext JS, LLC.
851  *
852  * Originally Released Under LGPL - original licence link has changed is not relivant.
853  *
854  * Fork - LGPL
855  * <script type="text/javascript">
856  */
857
858 /**
859  * @class Date
860  *
861  * The date parsing and format syntax is a subset of
862  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
863  * supported will provide results equivalent to their PHP versions.
864  *
865  * Following is the list of all currently supported formats:
866  *<pre>
867 Sample date:
868 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
869
870 Format  Output      Description
871 ------  ----------  --------------------------------------------------------------
872   d      10         Day of the month, 2 digits with leading zeros
873   D      Wed        A textual representation of a day, three letters
874   j      10         Day of the month without leading zeros
875   l      Wednesday  A full textual representation of the day of the week
876   S      th         English ordinal day of month suffix, 2 chars (use with j)
877   w      3          Numeric representation of the day of the week
878   z      9          The julian date, or day of the year (0-365)
879   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
880   F      January    A full textual representation of the month
881   m      01         Numeric representation of a month, with leading zeros
882   M      Jan        Month name abbreviation, three letters
883   n      1          Numeric representation of a month, without leading zeros
884   t      31         Number of days in the given month
885   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
886   Y      2007       A full numeric representation of a year, 4 digits
887   y      07         A two digit representation of a year
888   a      pm         Lowercase Ante meridiem and Post meridiem
889   A      PM         Uppercase Ante meridiem and Post meridiem
890   g      3          12-hour format of an hour without leading zeros
891   G      15         24-hour format of an hour without leading zeros
892   h      03         12-hour format of an hour with leading zeros
893   H      15         24-hour format of an hour with leading zeros
894   i      05         Minutes with leading zeros
895   s      01         Seconds, with leading zeros
896   O      -0600      Difference to Greenwich time (GMT) in hours
897   T      CST        Timezone setting of the machine running the code
898   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
899 </pre>
900  *
901  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
902  * <pre><code>
903 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
904 document.write(dt.format('Y-m-d'));                         //2007-01-10
905 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
906 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
907  </code></pre>
908  *
909  * Here are some standard date/time patterns that you might find helpful.  They
910  * are not part of the source of Date.js, but to use them you can simply copy this
911  * block of code into any script that is included after Date.js and they will also become
912  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
913  * <pre><code>
914 Date.patterns = {
915     ISO8601Long:"Y-m-d H:i:s",
916     ISO8601Short:"Y-m-d",
917     ShortDate: "n/j/Y",
918     LongDate: "l, F d, Y",
919     FullDateTime: "l, F d, Y g:i:s A",
920     MonthDay: "F d",
921     ShortTime: "g:i A",
922     LongTime: "g:i:s A",
923     SortableDateTime: "Y-m-d\\TH:i:s",
924     UniversalSortableDateTime: "Y-m-d H:i:sO",
925     YearMonth: "F, Y"
926 };
927 </code></pre>
928  *
929  * Example usage:
930  * <pre><code>
931 var dt = new Date();
932 document.write(dt.format(Date.patterns.ShortDate));
933  </code></pre>
934  */
935
936 /*
937  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
938  * They generate precompiled functions from date formats instead of parsing and
939  * processing the pattern every time you format a date.  These functions are available
940  * on every Date object (any javascript function).
941  *
942  * The original article and download are here:
943  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
944  *
945  */
946  
947  
948  // was in core
949 /**
950  Returns the number of milliseconds between this date and date
951  @param {Date} date (optional) Defaults to now
952  @return {Number} The diff in milliseconds
953  @member Date getElapsed
954  */
955 Date.prototype.getElapsed = function(date) {
956         return Math.abs((date || new Date()).getTime()-this.getTime());
957 };
958 // was in date file..
959
960
961 // private
962 Date.parseFunctions = {count:0};
963 // private
964 Date.parseRegexes = [];
965 // private
966 Date.formatFunctions = {count:0};
967
968 // private
969 Date.prototype.dateFormat = function(format) {
970     if (Date.formatFunctions[format] == null) {
971         Date.createNewFormat(format);
972     }
973     var func = Date.formatFunctions[format];
974     return this[func]();
975 };
976
977
978 /**
979  * Formats a date given the supplied format string
980  * @param {String} format The format string
981  * @return {String} The formatted date
982  * @method
983  */
984 Date.prototype.format = Date.prototype.dateFormat;
985
986 // private
987 Date.createNewFormat = function(format) {
988     var funcName = "format" + Date.formatFunctions.count++;
989     Date.formatFunctions[format] = funcName;
990     var code = "Date.prototype." + funcName + " = function(){return ";
991     var special = false;
992     var ch = '';
993     for (var i = 0; i < format.length; ++i) {
994         ch = format.charAt(i);
995         if (!special && ch == "\\") {
996             special = true;
997         }
998         else if (special) {
999             special = false;
1000             code += "'" + String.escape(ch) + "' + ";
1001         }
1002         else {
1003             code += Date.getFormatCode(ch);
1004         }
1005     }
1006     /** eval:var:zzzzzzzzzzzzz */
1007     eval(code.substring(0, code.length - 3) + ";}");
1008 };
1009
1010 // private
1011 Date.getFormatCode = function(character) {
1012     switch (character) {
1013     case "d":
1014         return "String.leftPad(this.getDate(), 2, '0') + ";
1015     case "D":
1016         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1017     case "j":
1018         return "this.getDate() + ";
1019     case "l":
1020         return "Date.dayNames[this.getDay()] + ";
1021     case "S":
1022         return "this.getSuffix() + ";
1023     case "w":
1024         return "this.getDay() + ";
1025     case "z":
1026         return "this.getDayOfYear() + ";
1027     case "W":
1028         return "this.getWeekOfYear() + ";
1029     case "F":
1030         return "Date.monthNames[this.getMonth()] + ";
1031     case "m":
1032         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1033     case "M":
1034         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1035     case "n":
1036         return "(this.getMonth() + 1) + ";
1037     case "t":
1038         return "this.getDaysInMonth() + ";
1039     case "L":
1040         return "(this.isLeapYear() ? 1 : 0) + ";
1041     case "Y":
1042         return "this.getFullYear() + ";
1043     case "y":
1044         return "('' + this.getFullYear()).substring(2, 4) + ";
1045     case "a":
1046         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1047     case "A":
1048         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1049     case "g":
1050         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1051     case "G":
1052         return "this.getHours() + ";
1053     case "h":
1054         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1055     case "H":
1056         return "String.leftPad(this.getHours(), 2, '0') + ";
1057     case "i":
1058         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1059     case "s":
1060         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1061     case "O":
1062         return "this.getGMTOffset() + ";
1063     case "T":
1064         return "this.getTimezone() + ";
1065     case "Z":
1066         return "(this.getTimezoneOffset() * -60) + ";
1067     default:
1068         return "'" + String.escape(character) + "' + ";
1069     }
1070 };
1071
1072 /**
1073  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1074  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1075  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1076  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1077  * string or the parse operation will fail.
1078  * Example Usage:
1079 <pre><code>
1080 //dt = Fri May 25 2007 (current date)
1081 var dt = new Date();
1082
1083 //dt = Thu May 25 2006 (today's month/day in 2006)
1084 dt = Date.parseDate("2006", "Y");
1085
1086 //dt = Sun Jan 15 2006 (all date parts specified)
1087 dt = Date.parseDate("2006-1-15", "Y-m-d");
1088
1089 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1090 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1091 </code></pre>
1092  * @param {String} input The unparsed date as a string
1093  * @param {String} format The format the date is in
1094  * @return {Date} The parsed date
1095  * @static
1096  */
1097 Date.parseDate = function(input, format) {
1098     if (Date.parseFunctions[format] == null) {
1099         Date.createParser(format);
1100     }
1101     var func = Date.parseFunctions[format];
1102     return Date[func](input);
1103 };
1104
1105 // private
1106 Date.createParser = function(format) {
1107     var funcName = "parse" + Date.parseFunctions.count++;
1108     var regexNum = Date.parseRegexes.length;
1109     var currentGroup = 1;
1110     Date.parseFunctions[format] = funcName;
1111
1112     var code = "Date." + funcName + " = function(input){\n"
1113         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1114         + "var d = new Date();\n"
1115         + "y = d.getFullYear();\n"
1116         + "m = d.getMonth();\n"
1117         + "d = d.getDate();\n"
1118         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1119         + "if (results && results.length > 0) {";
1120     var regex = "";
1121
1122     var special = false;
1123     var ch = '';
1124     for (var i = 0; i < format.length; ++i) {
1125         ch = format.charAt(i);
1126         if (!special && ch == "\\") {
1127             special = true;
1128         }
1129         else if (special) {
1130             special = false;
1131             regex += String.escape(ch);
1132         }
1133         else {
1134             var obj = Date.formatCodeToRegex(ch, currentGroup);
1135             currentGroup += obj.g;
1136             regex += obj.s;
1137             if (obj.g && obj.c) {
1138                 code += obj.c;
1139             }
1140         }
1141     }
1142
1143     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1144         + "{v = new Date(y, m, d, h, i, s);}\n"
1145         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1146         + "{v = new Date(y, m, d, h, i);}\n"
1147         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1148         + "{v = new Date(y, m, d, h);}\n"
1149         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1150         + "{v = new Date(y, m, d);}\n"
1151         + "else if (y >= 0 && m >= 0)\n"
1152         + "{v = new Date(y, m);}\n"
1153         + "else if (y >= 0)\n"
1154         + "{v = new Date(y);}\n"
1155         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1156         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1157         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1158         + ";}";
1159
1160     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1161      /** eval:var:zzzzzzzzzzzzz */
1162     eval(code);
1163 };
1164
1165 // private
1166 Date.formatCodeToRegex = function(character, currentGroup) {
1167     switch (character) {
1168     case "D":
1169         return {g:0,
1170         c:null,
1171         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1172     case "j":
1173         return {g:1,
1174             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1175             s:"(\\d{1,2})"}; // day of month without leading zeroes
1176     case "d":
1177         return {g:1,
1178             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1179             s:"(\\d{2})"}; // day of month with leading zeroes
1180     case "l":
1181         return {g:0,
1182             c:null,
1183             s:"(?:" + Date.dayNames.join("|") + ")"};
1184     case "S":
1185         return {g:0,
1186             c:null,
1187             s:"(?:st|nd|rd|th)"};
1188     case "w":
1189         return {g:0,
1190             c:null,
1191             s:"\\d"};
1192     case "z":
1193         return {g:0,
1194             c:null,
1195             s:"(?:\\d{1,3})"};
1196     case "W":
1197         return {g:0,
1198             c:null,
1199             s:"(?:\\d{2})"};
1200     case "F":
1201         return {g:1,
1202             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1203             s:"(" + Date.monthNames.join("|") + ")"};
1204     case "M":
1205         return {g:1,
1206             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1207             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1208     case "n":
1209         return {g:1,
1210             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1211             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1212     case "m":
1213         return {g:1,
1214             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1215             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1216     case "t":
1217         return {g:0,
1218             c:null,
1219             s:"\\d{1,2}"};
1220     case "L":
1221         return {g:0,
1222             c:null,
1223             s:"(?:1|0)"};
1224     case "Y":
1225         return {g:1,
1226             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1227             s:"(\\d{4})"};
1228     case "y":
1229         return {g:1,
1230             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1231                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1232             s:"(\\d{1,2})"};
1233     case "a":
1234         return {g:1,
1235             c:"if (results[" + currentGroup + "] == 'am') {\n"
1236                 + "if (h == 12) { h = 0; }\n"
1237                 + "} else { if (h < 12) { h += 12; }}",
1238             s:"(am|pm)"};
1239     case "A":
1240         return {g:1,
1241             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1242                 + "if (h == 12) { h = 0; }\n"
1243                 + "} else { if (h < 12) { h += 12; }}",
1244             s:"(AM|PM)"};
1245     case "g":
1246     case "G":
1247         return {g:1,
1248             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1249             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1250     case "h":
1251     case "H":
1252         return {g:1,
1253             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1254             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1255     case "i":
1256         return {g:1,
1257             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1258             s:"(\\d{2})"};
1259     case "s":
1260         return {g:1,
1261             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1262             s:"(\\d{2})"};
1263     case "O":
1264         return {g:1,
1265             c:[
1266                 "o = results[", currentGroup, "];\n",
1267                 "var sn = o.substring(0,1);\n", // get + / - sign
1268                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1269                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1270                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1271                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1272             ].join(""),
1273             s:"([+\-]\\d{4})"};
1274     case "T":
1275         return {g:0,
1276             c:null,
1277             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1278     case "Z":
1279         return {g:1,
1280             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1281                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1282             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1283     default:
1284         return {g:0,
1285             c:null,
1286             s:String.escape(character)};
1287     }
1288 };
1289
1290 /**
1291  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1292  * @return {String} The abbreviated timezone name (e.g. 'CST')
1293  */
1294 Date.prototype.getTimezone = function() {
1295     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1296 };
1297
1298 /**
1299  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1300  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1301  */
1302 Date.prototype.getGMTOffset = function() {
1303     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1304         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1305         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1306 };
1307
1308 /**
1309  * Get the numeric day number of the year, adjusted for leap year.
1310  * @return {Number} 0 through 364 (365 in leap years)
1311  */
1312 Date.prototype.getDayOfYear = function() {
1313     var num = 0;
1314     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1315     for (var i = 0; i < this.getMonth(); ++i) {
1316         num += Date.daysInMonth[i];
1317     }
1318     return num + this.getDate() - 1;
1319 };
1320
1321 /**
1322  * Get the string representation of the numeric week number of the year
1323  * (equivalent to the format specifier 'W').
1324  * @return {String} '00' through '52'
1325  */
1326 Date.prototype.getWeekOfYear = function() {
1327     // Skip to Thursday of this week
1328     var now = this.getDayOfYear() + (4 - this.getDay());
1329     // Find the first Thursday of the year
1330     var jan1 = new Date(this.getFullYear(), 0, 1);
1331     var then = (7 - jan1.getDay() + 4);
1332     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1333 };
1334
1335 /**
1336  * Whether or not the current date is in a leap year.
1337  * @return {Boolean} True if the current date is in a leap year, else false
1338  */
1339 Date.prototype.isLeapYear = function() {
1340     var year = this.getFullYear();
1341     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1342 };
1343
1344 /**
1345  * Get the first day of the current month, adjusted for leap year.  The returned value
1346  * is the numeric day index within the week (0-6) which can be used in conjunction with
1347  * the {@link #monthNames} array to retrieve the textual day name.
1348  * Example:
1349  *<pre><code>
1350 var dt = new Date('1/10/2007');
1351 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1352 </code></pre>
1353  * @return {Number} The day number (0-6)
1354  */
1355 Date.prototype.getFirstDayOfMonth = function() {
1356     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1357     return (day < 0) ? (day + 7) : day;
1358 };
1359
1360 /**
1361  * Get the last day of the current month, adjusted for leap year.  The returned value
1362  * is the numeric day index within the week (0-6) which can be used in conjunction with
1363  * the {@link #monthNames} array to retrieve the textual day name.
1364  * Example:
1365  *<pre><code>
1366 var dt = new Date('1/10/2007');
1367 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1368 </code></pre>
1369  * @return {Number} The day number (0-6)
1370  */
1371 Date.prototype.getLastDayOfMonth = function() {
1372     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1373     return (day < 0) ? (day + 7) : day;
1374 };
1375
1376
1377 /**
1378  * Get the first date of this date's month
1379  * @return {Date}
1380  */
1381 Date.prototype.getFirstDateOfMonth = function() {
1382     return new Date(this.getFullYear(), this.getMonth(), 1);
1383 };
1384
1385 /**
1386  * Get the last date of this date's month
1387  * @return {Date}
1388  */
1389 Date.prototype.getLastDateOfMonth = function() {
1390     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1391 };
1392 /**
1393  * Get the number of days in the current month, adjusted for leap year.
1394  * @return {Number} The number of days in the month
1395  */
1396 Date.prototype.getDaysInMonth = function() {
1397     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1398     return Date.daysInMonth[this.getMonth()];
1399 };
1400
1401 /**
1402  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1403  * @return {String} 'st, 'nd', 'rd' or 'th'
1404  */
1405 Date.prototype.getSuffix = function() {
1406     switch (this.getDate()) {
1407         case 1:
1408         case 21:
1409         case 31:
1410             return "st";
1411         case 2:
1412         case 22:
1413             return "nd";
1414         case 3:
1415         case 23:
1416             return "rd";
1417         default:
1418             return "th";
1419     }
1420 };
1421
1422 // private
1423 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1424
1425 /**
1426  * An array of textual month names.
1427  * Override these values for international dates, for example...
1428  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1429  * @type Array
1430  * @static
1431  */
1432 Date.monthNames =
1433    ["January",
1434     "February",
1435     "March",
1436     "April",
1437     "May",
1438     "June",
1439     "July",
1440     "August",
1441     "September",
1442     "October",
1443     "November",
1444     "December"];
1445
1446 /**
1447  * An array of textual day names.
1448  * Override these values for international dates, for example...
1449  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1450  * @type Array
1451  * @static
1452  */
1453 Date.dayNames =
1454    ["Sunday",
1455     "Monday",
1456     "Tuesday",
1457     "Wednesday",
1458     "Thursday",
1459     "Friday",
1460     "Saturday"];
1461
1462 // private
1463 Date.y2kYear = 50;
1464 // private
1465 Date.monthNumbers = {
1466     Jan:0,
1467     Feb:1,
1468     Mar:2,
1469     Apr:3,
1470     May:4,
1471     Jun:5,
1472     Jul:6,
1473     Aug:7,
1474     Sep:8,
1475     Oct:9,
1476     Nov:10,
1477     Dec:11};
1478
1479 /**
1480  * Creates and returns a new Date instance with the exact same date value as the called instance.
1481  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1482  * variable will also be changed.  When the intention is to create a new variable that will not
1483  * modify the original instance, you should create a clone.
1484  *
1485  * Example of correctly cloning a date:
1486  * <pre><code>
1487 //wrong way:
1488 var orig = new Date('10/1/2006');
1489 var copy = orig;
1490 copy.setDate(5);
1491 document.write(orig);  //returns 'Thu Oct 05 2006'!
1492
1493 //correct way:
1494 var orig = new Date('10/1/2006');
1495 var copy = orig.clone();
1496 copy.setDate(5);
1497 document.write(orig);  //returns 'Thu Oct 01 2006'
1498 </code></pre>
1499  * @return {Date} The new Date instance
1500  */
1501 Date.prototype.clone = function() {
1502         return new Date(this.getTime());
1503 };
1504
1505 /**
1506  * Clears any time information from this date
1507  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1508  @return {Date} this or the clone
1509  */
1510 Date.prototype.clearTime = function(clone){
1511     if(clone){
1512         return this.clone().clearTime();
1513     }
1514     this.setHours(0);
1515     this.setMinutes(0);
1516     this.setSeconds(0);
1517     this.setMilliseconds(0);
1518     return this;
1519 };
1520
1521 // private
1522 // safari setMonth is broken
1523 if(Roo.isSafari){
1524     Date.brokenSetMonth = Date.prototype.setMonth;
1525         Date.prototype.setMonth = function(num){
1526                 if(num <= -1){
1527                         var n = Math.ceil(-num);
1528                         var back_year = Math.ceil(n/12);
1529                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1530                         this.setFullYear(this.getFullYear() - back_year);
1531                         return Date.brokenSetMonth.call(this, month);
1532                 } else {
1533                         return Date.brokenSetMonth.apply(this, arguments);
1534                 }
1535         };
1536 }
1537
1538 /** Date interval constant 
1539 * @static 
1540 * @type String */
1541 Date.MILLI = "ms";
1542 /** Date interval constant 
1543 * @static 
1544 * @type String */
1545 Date.SECOND = "s";
1546 /** Date interval constant 
1547 * @static 
1548 * @type String */
1549 Date.MINUTE = "mi";
1550 /** Date interval constant 
1551 * @static 
1552 * @type String */
1553 Date.HOUR = "h";
1554 /** Date interval constant 
1555 * @static 
1556 * @type String */
1557 Date.DAY = "d";
1558 /** Date interval constant 
1559 * @static 
1560 * @type String */
1561 Date.MONTH = "mo";
1562 /** Date interval constant 
1563 * @static 
1564 * @type String */
1565 Date.YEAR = "y";
1566
1567 /**
1568  * Provides a convenient method of performing basic date arithmetic.  This method
1569  * does not modify the Date instance being called - it creates and returns
1570  * a new Date instance containing the resulting date value.
1571  *
1572  * Examples:
1573  * <pre><code>
1574 //Basic usage:
1575 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1576 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1577
1578 //Negative values will subtract correctly:
1579 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1580 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1581
1582 //You can even chain several calls together in one line!
1583 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1584 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1585  </code></pre>
1586  *
1587  * @param {String} interval   A valid date interval enum value
1588  * @param {Number} value      The amount to add to the current date
1589  * @return {Date} The new Date instance
1590  */
1591 Date.prototype.add = function(interval, value){
1592   var d = this.clone();
1593   if (!interval || value === 0) return d;
1594   switch(interval.toLowerCase()){
1595     case Date.MILLI:
1596       d.setMilliseconds(this.getMilliseconds() + value);
1597       break;
1598     case Date.SECOND:
1599       d.setSeconds(this.getSeconds() + value);
1600       break;
1601     case Date.MINUTE:
1602       d.setMinutes(this.getMinutes() + value);
1603       break;
1604     case Date.HOUR:
1605       d.setHours(this.getHours() + value);
1606       break;
1607     case Date.DAY:
1608       d.setDate(this.getDate() + value);
1609       break;
1610     case Date.MONTH:
1611       var day = this.getDate();
1612       if(day > 28){
1613           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1614       }
1615       d.setDate(day);
1616       d.setMonth(this.getMonth() + value);
1617       break;
1618     case Date.YEAR:
1619       d.setFullYear(this.getFullYear() + value);
1620       break;
1621   }
1622   return d;
1623 };/*
1624  * Based on:
1625  * Ext JS Library 1.1.1
1626  * Copyright(c) 2006-2007, Ext JS, LLC.
1627  *
1628  * Originally Released Under LGPL - original licence link has changed is not relivant.
1629  *
1630  * Fork - LGPL
1631  * <script type="text/javascript">
1632  */
1633
1634 Roo.lib.Dom = {
1635     getViewWidth : function(full) {
1636         return full ? this.getDocumentWidth() : this.getViewportWidth();
1637     },
1638
1639     getViewHeight : function(full) {
1640         return full ? this.getDocumentHeight() : this.getViewportHeight();
1641     },
1642
1643     getDocumentHeight: function() {
1644         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1645         return Math.max(scrollHeight, this.getViewportHeight());
1646     },
1647
1648     getDocumentWidth: function() {
1649         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1650         return Math.max(scrollWidth, this.getViewportWidth());
1651     },
1652
1653     getViewportHeight: function() {
1654         var height = self.innerHeight;
1655         var mode = document.compatMode;
1656
1657         if ((mode || Roo.isIE) && !Roo.isOpera) {
1658             height = (mode == "CSS1Compat") ?
1659                      document.documentElement.clientHeight :
1660                      document.body.clientHeight;
1661         }
1662
1663         return height;
1664     },
1665
1666     getViewportWidth: function() {
1667         var width = self.innerWidth;
1668         var mode = document.compatMode;
1669
1670         if (mode || Roo.isIE) {
1671             width = (mode == "CSS1Compat") ?
1672                     document.documentElement.clientWidth :
1673                     document.body.clientWidth;
1674         }
1675         return width;
1676     },
1677
1678     isAncestor : function(p, c) {
1679         p = Roo.getDom(p);
1680         c = Roo.getDom(c);
1681         if (!p || !c) {
1682             return false;
1683         }
1684
1685         if (p.contains && !Roo.isSafari) {
1686             return p.contains(c);
1687         } else if (p.compareDocumentPosition) {
1688             return !!(p.compareDocumentPosition(c) & 16);
1689         } else {
1690             var parent = c.parentNode;
1691             while (parent) {
1692                 if (parent == p) {
1693                     return true;
1694                 }
1695                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1696                     return false;
1697                 }
1698                 parent = parent.parentNode;
1699             }
1700             return false;
1701         }
1702     },
1703
1704     getRegion : function(el) {
1705         return Roo.lib.Region.getRegion(el);
1706     },
1707
1708     getY : function(el) {
1709         return this.getXY(el)[1];
1710     },
1711
1712     getX : function(el) {
1713         return this.getXY(el)[0];
1714     },
1715
1716     getXY : function(el) {
1717         var p, pe, b, scroll, bd = document.body;
1718         el = Roo.getDom(el);
1719         var fly = Roo.lib.AnimBase.fly;
1720         if (el.getBoundingClientRect) {
1721             b = el.getBoundingClientRect();
1722             scroll = fly(document).getScroll();
1723             return [b.left + scroll.left, b.top + scroll.top];
1724         }
1725         var x = 0, y = 0;
1726
1727         p = el;
1728
1729         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1730
1731         while (p) {
1732
1733             x += p.offsetLeft;
1734             y += p.offsetTop;
1735
1736             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1737                 hasAbsolute = true;
1738             }
1739
1740             if (Roo.isGecko) {
1741                 pe = fly(p);
1742
1743                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1744                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1745
1746
1747                 x += bl;
1748                 y += bt;
1749
1750
1751                 if (p != el && pe.getStyle('overflow') != 'visible') {
1752                     x += bl;
1753                     y += bt;
1754                 }
1755             }
1756             p = p.offsetParent;
1757         }
1758
1759         if (Roo.isSafari && hasAbsolute) {
1760             x -= bd.offsetLeft;
1761             y -= bd.offsetTop;
1762         }
1763
1764         if (Roo.isGecko && !hasAbsolute) {
1765             var dbd = fly(bd);
1766             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1767             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1768         }
1769
1770         p = el.parentNode;
1771         while (p && p != bd) {
1772             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1773                 x -= p.scrollLeft;
1774                 y -= p.scrollTop;
1775             }
1776             p = p.parentNode;
1777         }
1778         return [x, y];
1779     },
1780  
1781   
1782
1783
1784     setXY : function(el, xy) {
1785         el = Roo.fly(el, '_setXY');
1786         el.position();
1787         var pts = el.translatePoints(xy);
1788         if (xy[0] !== false) {
1789             el.dom.style.left = pts.left + "px";
1790         }
1791         if (xy[1] !== false) {
1792             el.dom.style.top = pts.top + "px";
1793         }
1794     },
1795
1796     setX : function(el, x) {
1797         this.setXY(el, [x, false]);
1798     },
1799
1800     setY : function(el, y) {
1801         this.setXY(el, [false, y]);
1802     }
1803 };
1804 /*
1805  * Portions of this file are based on pieces of Yahoo User Interface Library
1806  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1807  * YUI licensed under the BSD License:
1808  * http://developer.yahoo.net/yui/license.txt
1809  * <script type="text/javascript">
1810  *
1811  */
1812
1813 Roo.lib.Event = function() {
1814     var loadComplete = false;
1815     var listeners = [];
1816     var unloadListeners = [];
1817     var retryCount = 0;
1818     var onAvailStack = [];
1819     var counter = 0;
1820     var lastError = null;
1821
1822     return {
1823         POLL_RETRYS: 200,
1824         POLL_INTERVAL: 20,
1825         EL: 0,
1826         TYPE: 1,
1827         FN: 2,
1828         WFN: 3,
1829         OBJ: 3,
1830         ADJ_SCOPE: 4,
1831         _interval: null,
1832
1833         startInterval: function() {
1834             if (!this._interval) {
1835                 var self = this;
1836                 var callback = function() {
1837                     self._tryPreloadAttach();
1838                 };
1839                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1840
1841             }
1842         },
1843
1844         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1845             onAvailStack.push({ id:         p_id,
1846                 fn:         p_fn,
1847                 obj:        p_obj,
1848                 override:   p_override,
1849                 checkReady: false    });
1850
1851             retryCount = this.POLL_RETRYS;
1852             this.startInterval();
1853         },
1854
1855
1856         addListener: function(el, eventName, fn) {
1857             el = Roo.getDom(el);
1858             if (!el || !fn) {
1859                 return false;
1860             }
1861
1862             if ("unload" == eventName) {
1863                 unloadListeners[unloadListeners.length] =
1864                 [el, eventName, fn];
1865                 return true;
1866             }
1867
1868             var wrappedFn = function(e) {
1869                 return fn(Roo.lib.Event.getEvent(e));
1870             };
1871
1872             var li = [el, eventName, fn, wrappedFn];
1873
1874             var index = listeners.length;
1875             listeners[index] = li;
1876
1877             this.doAdd(el, eventName, wrappedFn, false);
1878             return true;
1879
1880         },
1881
1882
1883         removeListener: function(el, eventName, fn) {
1884             var i, len;
1885
1886             el = Roo.getDom(el);
1887
1888             if(!fn) {
1889                 return this.purgeElement(el, false, eventName);
1890             }
1891
1892
1893             if ("unload" == eventName) {
1894
1895                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1896                     var li = unloadListeners[i];
1897                     if (li &&
1898                         li[0] == el &&
1899                         li[1] == eventName &&
1900                         li[2] == fn) {
1901                         unloadListeners.splice(i, 1);
1902                         return true;
1903                     }
1904                 }
1905
1906                 return false;
1907             }
1908
1909             var cacheItem = null;
1910
1911
1912             var index = arguments[3];
1913
1914             if ("undefined" == typeof index) {
1915                 index = this._getCacheIndex(el, eventName, fn);
1916             }
1917
1918             if (index >= 0) {
1919                 cacheItem = listeners[index];
1920             }
1921
1922             if (!el || !cacheItem) {
1923                 return false;
1924             }
1925
1926             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1927
1928             delete listeners[index][this.WFN];
1929             delete listeners[index][this.FN];
1930             listeners.splice(index, 1);
1931
1932             return true;
1933
1934         },
1935
1936
1937         getTarget: function(ev, resolveTextNode) {
1938             ev = ev.browserEvent || ev;
1939             var t = ev.target || ev.srcElement;
1940             return this.resolveTextNode(t);
1941         },
1942
1943
1944         resolveTextNode: function(node) {
1945             if (Roo.isSafari && node && 3 == node.nodeType) {
1946                 return node.parentNode;
1947             } else {
1948                 return node;
1949             }
1950         },
1951
1952
1953         getPageX: function(ev) {
1954             ev = ev.browserEvent || ev;
1955             var x = ev.pageX;
1956             if (!x && 0 !== x) {
1957                 x = ev.clientX || 0;
1958
1959                 if (Roo.isIE) {
1960                     x += this.getScroll()[1];
1961                 }
1962             }
1963
1964             return x;
1965         },
1966
1967
1968         getPageY: function(ev) {
1969             ev = ev.browserEvent || ev;
1970             var y = ev.pageY;
1971             if (!y && 0 !== y) {
1972                 y = ev.clientY || 0;
1973
1974                 if (Roo.isIE) {
1975                     y += this.getScroll()[0];
1976                 }
1977             }
1978
1979
1980             return y;
1981         },
1982
1983
1984         getXY: function(ev) {
1985             ev = ev.browserEvent || ev;
1986             return [this.getPageX(ev), this.getPageY(ev)];
1987         },
1988
1989
1990         getRelatedTarget: function(ev) {
1991             ev = ev.browserEvent || ev;
1992             var t = ev.relatedTarget;
1993             if (!t) {
1994                 if (ev.type == "mouseout") {
1995                     t = ev.toElement;
1996                 } else if (ev.type == "mouseover") {
1997                     t = ev.fromElement;
1998                 }
1999             }
2000
2001             return this.resolveTextNode(t);
2002         },
2003
2004
2005         getTime: function(ev) {
2006             ev = ev.browserEvent || ev;
2007             if (!ev.time) {
2008                 var t = new Date().getTime();
2009                 try {
2010                     ev.time = t;
2011                 } catch(ex) {
2012                     this.lastError = ex;
2013                     return t;
2014                 }
2015             }
2016
2017             return ev.time;
2018         },
2019
2020
2021         stopEvent: function(ev) {
2022             this.stopPropagation(ev);
2023             this.preventDefault(ev);
2024         },
2025
2026
2027         stopPropagation: function(ev) {
2028             ev = ev.browserEvent || ev;
2029             if (ev.stopPropagation) {
2030                 ev.stopPropagation();
2031             } else {
2032                 ev.cancelBubble = true;
2033             }
2034         },
2035
2036
2037         preventDefault: function(ev) {
2038             ev = ev.browserEvent || ev;
2039             if(ev.preventDefault) {
2040                 ev.preventDefault();
2041             } else {
2042                 ev.returnValue = false;
2043             }
2044         },
2045
2046
2047         getEvent: function(e) {
2048             var ev = e || window.event;
2049             if (!ev) {
2050                 var c = this.getEvent.caller;
2051                 while (c) {
2052                     ev = c.arguments[0];
2053                     if (ev && Event == ev.constructor) {
2054                         break;
2055                     }
2056                     c = c.caller;
2057                 }
2058             }
2059             return ev;
2060         },
2061
2062
2063         getCharCode: function(ev) {
2064             ev = ev.browserEvent || ev;
2065             return ev.charCode || ev.keyCode || 0;
2066         },
2067
2068
2069         _getCacheIndex: function(el, eventName, fn) {
2070             for (var i = 0,len = listeners.length; i < len; ++i) {
2071                 var li = listeners[i];
2072                 if (li &&
2073                     li[this.FN] == fn &&
2074                     li[this.EL] == el &&
2075                     li[this.TYPE] == eventName) {
2076                     return i;
2077                 }
2078             }
2079
2080             return -1;
2081         },
2082
2083
2084         elCache: {},
2085
2086
2087         getEl: function(id) {
2088             return document.getElementById(id);
2089         },
2090
2091
2092         clearCache: function() {
2093         },
2094
2095
2096         _load: function(e) {
2097             loadComplete = true;
2098             var EU = Roo.lib.Event;
2099
2100
2101             if (Roo.isIE) {
2102                 EU.doRemove(window, "load", EU._load);
2103             }
2104         },
2105
2106
2107         _tryPreloadAttach: function() {
2108
2109             if (this.locked) {
2110                 return false;
2111             }
2112
2113             this.locked = true;
2114
2115
2116             var tryAgain = !loadComplete;
2117             if (!tryAgain) {
2118                 tryAgain = (retryCount > 0);
2119             }
2120
2121
2122             var notAvail = [];
2123             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2124                 var item = onAvailStack[i];
2125                 if (item) {
2126                     var el = this.getEl(item.id);
2127
2128                     if (el) {
2129                         if (!item.checkReady ||
2130                             loadComplete ||
2131                             el.nextSibling ||
2132                             (document && document.body)) {
2133
2134                             var scope = el;
2135                             if (item.override) {
2136                                 if (item.override === true) {
2137                                     scope = item.obj;
2138                                 } else {
2139                                     scope = item.override;
2140                                 }
2141                             }
2142                             item.fn.call(scope, item.obj);
2143                             onAvailStack[i] = null;
2144                         }
2145                     } else {
2146                         notAvail.push(item);
2147                     }
2148                 }
2149             }
2150
2151             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2152
2153             if (tryAgain) {
2154
2155                 this.startInterval();
2156             } else {
2157                 clearInterval(this._interval);
2158                 this._interval = null;
2159             }
2160
2161             this.locked = false;
2162
2163             return true;
2164
2165         },
2166
2167
2168         purgeElement: function(el, recurse, eventName) {
2169             var elListeners = this.getListeners(el, eventName);
2170             if (elListeners) {
2171                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2172                     var l = elListeners[i];
2173                     this.removeListener(el, l.type, l.fn);
2174                 }
2175             }
2176
2177             if (recurse && el && el.childNodes) {
2178                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2179                     this.purgeElement(el.childNodes[i], recurse, eventName);
2180                 }
2181             }
2182         },
2183
2184
2185         getListeners: function(el, eventName) {
2186             var results = [], searchLists;
2187             if (!eventName) {
2188                 searchLists = [listeners, unloadListeners];
2189             } else if (eventName == "unload") {
2190                 searchLists = [unloadListeners];
2191             } else {
2192                 searchLists = [listeners];
2193             }
2194
2195             for (var j = 0; j < searchLists.length; ++j) {
2196                 var searchList = searchLists[j];
2197                 if (searchList && searchList.length > 0) {
2198                     for (var i = 0,len = searchList.length; i < len; ++i) {
2199                         var l = searchList[i];
2200                         if (l && l[this.EL] === el &&
2201                             (!eventName || eventName === l[this.TYPE])) {
2202                             results.push({
2203                                 type:   l[this.TYPE],
2204                                 fn:     l[this.FN],
2205                                 obj:    l[this.OBJ],
2206                                 adjust: l[this.ADJ_SCOPE],
2207                                 index:  i
2208                             });
2209                         }
2210                     }
2211                 }
2212             }
2213
2214             return (results.length) ? results : null;
2215         },
2216
2217
2218         _unload: function(e) {
2219
2220             var EU = Roo.lib.Event, i, j, l, len, index;
2221
2222             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2223                 l = unloadListeners[i];
2224                 if (l) {
2225                     var scope = window;
2226                     if (l[EU.ADJ_SCOPE]) {
2227                         if (l[EU.ADJ_SCOPE] === true) {
2228                             scope = l[EU.OBJ];
2229                         } else {
2230                             scope = l[EU.ADJ_SCOPE];
2231                         }
2232                     }
2233                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2234                     unloadListeners[i] = null;
2235                     l = null;
2236                     scope = null;
2237                 }
2238             }
2239
2240             unloadListeners = null;
2241
2242             if (listeners && listeners.length > 0) {
2243                 j = listeners.length;
2244                 while (j) {
2245                     index = j - 1;
2246                     l = listeners[index];
2247                     if (l) {
2248                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2249                                 l[EU.FN], index);
2250                     }
2251                     j = j - 1;
2252                 }
2253                 l = null;
2254
2255                 EU.clearCache();
2256             }
2257
2258             EU.doRemove(window, "unload", EU._unload);
2259
2260         },
2261
2262
2263         getScroll: function() {
2264             var dd = document.documentElement, db = document.body;
2265             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2266                 return [dd.scrollTop, dd.scrollLeft];
2267             } else if (db) {
2268                 return [db.scrollTop, db.scrollLeft];
2269             } else {
2270                 return [0, 0];
2271             }
2272         },
2273
2274
2275         doAdd: function () {
2276             if (window.addEventListener) {
2277                 return function(el, eventName, fn, capture) {
2278                     el.addEventListener(eventName, fn, (capture));
2279                 };
2280             } else if (window.attachEvent) {
2281                 return function(el, eventName, fn, capture) {
2282                     el.attachEvent("on" + eventName, fn);
2283                 };
2284             } else {
2285                 return function() {
2286                 };
2287             }
2288         }(),
2289
2290
2291         doRemove: function() {
2292             if (window.removeEventListener) {
2293                 return function (el, eventName, fn, capture) {
2294                     el.removeEventListener(eventName, fn, (capture));
2295                 };
2296             } else if (window.detachEvent) {
2297                 return function (el, eventName, fn) {
2298                     el.detachEvent("on" + eventName, fn);
2299                 };
2300             } else {
2301                 return function() {
2302                 };
2303             }
2304         }()
2305     };
2306     
2307 }();
2308 (function() {     
2309    
2310     var E = Roo.lib.Event;
2311     E.on = E.addListener;
2312     E.un = E.removeListener;
2313
2314     if (document && document.body) {
2315         E._load();
2316     } else {
2317         E.doAdd(window, "load", E._load);
2318     }
2319     E.doAdd(window, "unload", E._unload);
2320     E._tryPreloadAttach();
2321 })();
2322
2323 /*
2324  * Portions of this file are based on pieces of Yahoo User Interface Library
2325  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2326  * YUI licensed under the BSD License:
2327  * http://developer.yahoo.net/yui/license.txt
2328  * <script type="text/javascript">
2329  *
2330  */
2331
2332 (function() {
2333     
2334     Roo.lib.Ajax = {
2335         request : function(method, uri, cb, data, options) {
2336             if(options){
2337                 var hs = options.headers;
2338                 if(hs){
2339                     for(var h in hs){
2340                         if(hs.hasOwnProperty(h)){
2341                             this.initHeader(h, hs[h], false);
2342                         }
2343                     }
2344                 }
2345                 if(options.xmlData){
2346                     this.initHeader('Content-Type', 'text/xml', false);
2347                     method = 'POST';
2348                     data = options.xmlData;
2349                 }
2350             }
2351
2352             return this.asyncRequest(method, uri, cb, data);
2353         },
2354
2355         serializeForm : function(form) {
2356             if(typeof form == 'string') {
2357                 form = (document.getElementById(form) || document.forms[form]);
2358             }
2359
2360             var el, name, val, disabled, data = '', hasSubmit = false;
2361             for (var i = 0; i < form.elements.length; i++) {
2362                 el = form.elements[i];
2363                 disabled = form.elements[i].disabled;
2364                 name = form.elements[i].name;
2365                 val = form.elements[i].value;
2366
2367                 if (!disabled && name){
2368                     switch (el.type)
2369                             {
2370                         case 'select-one':
2371                         case 'select-multiple':
2372                             for (var j = 0; j < el.options.length; j++) {
2373                                 if (el.options[j].selected) {
2374                                     if (Roo.isIE) {
2375                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2376                                     }
2377                                     else {
2378                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2379                                     }
2380                                 }
2381                             }
2382                             break;
2383                         case 'radio':
2384                         case 'checkbox':
2385                             if (el.checked) {
2386                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2387                             }
2388                             break;
2389                         case 'file':
2390
2391                         case undefined:
2392
2393                         case 'reset':
2394
2395                         case 'button':
2396
2397                             break;
2398                         case 'submit':
2399                             if(hasSubmit == false) {
2400                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2401                                 hasSubmit = true;
2402                             }
2403                             break;
2404                         default:
2405                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2406                             break;
2407                     }
2408                 }
2409             }
2410             data = data.substr(0, data.length - 1);
2411             return data;
2412         },
2413
2414         headers:{},
2415
2416         hasHeaders:false,
2417
2418         useDefaultHeader:true,
2419
2420         defaultPostHeader:'application/x-www-form-urlencoded',
2421
2422         useDefaultXhrHeader:true,
2423
2424         defaultXhrHeader:'XMLHttpRequest',
2425
2426         hasDefaultHeaders:true,
2427
2428         defaultHeaders:{},
2429
2430         poll:{},
2431
2432         timeout:{},
2433
2434         pollInterval:50,
2435
2436         transactionId:0,
2437
2438         setProgId:function(id)
2439         {
2440             this.activeX.unshift(id);
2441         },
2442
2443         setDefaultPostHeader:function(b)
2444         {
2445             this.useDefaultHeader = b;
2446         },
2447
2448         setDefaultXhrHeader:function(b)
2449         {
2450             this.useDefaultXhrHeader = b;
2451         },
2452
2453         setPollingInterval:function(i)
2454         {
2455             if (typeof i == 'number' && isFinite(i)) {
2456                 this.pollInterval = i;
2457             }
2458         },
2459
2460         createXhrObject:function(transactionId)
2461         {
2462             var obj,http;
2463             try
2464             {
2465
2466                 http = new XMLHttpRequest();
2467
2468                 obj = { conn:http, tId:transactionId };
2469             }
2470             catch(e)
2471             {
2472                 for (var i = 0; i < this.activeX.length; ++i) {
2473                     try
2474                     {
2475
2476                         http = new ActiveXObject(this.activeX[i]);
2477
2478                         obj = { conn:http, tId:transactionId };
2479                         break;
2480                     }
2481                     catch(e) {
2482                     }
2483                 }
2484             }
2485             finally
2486             {
2487                 return obj;
2488             }
2489         },
2490
2491         getConnectionObject:function()
2492         {
2493             var o;
2494             var tId = this.transactionId;
2495
2496             try
2497             {
2498                 o = this.createXhrObject(tId);
2499                 if (o) {
2500                     this.transactionId++;
2501                 }
2502             }
2503             catch(e) {
2504             }
2505             finally
2506             {
2507                 return o;
2508             }
2509         },
2510
2511         asyncRequest:function(method, uri, callback, postData)
2512         {
2513             var o = this.getConnectionObject();
2514
2515             if (!o) {
2516                 return null;
2517             }
2518             else {
2519                 o.conn.open(method, uri, true);
2520
2521                 if (this.useDefaultXhrHeader) {
2522                     if (!this.defaultHeaders['X-Requested-With']) {
2523                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2524                     }
2525                 }
2526
2527                 if(postData && this.useDefaultHeader){
2528                     this.initHeader('Content-Type', this.defaultPostHeader);
2529                 }
2530
2531                  if (this.hasDefaultHeaders || this.hasHeaders) {
2532                     this.setHeader(o);
2533                 }
2534
2535                 this.handleReadyState(o, callback);
2536                 o.conn.send(postData || null);
2537
2538                 return o;
2539             }
2540         },
2541
2542         handleReadyState:function(o, callback)
2543         {
2544             var oConn = this;
2545
2546             if (callback && callback.timeout) {
2547                 this.timeout[o.tId] = window.setTimeout(function() {
2548                     oConn.abort(o, callback, true);
2549                 }, callback.timeout);
2550             }
2551
2552             this.poll[o.tId] = window.setInterval(
2553                     function() {
2554                         if (o.conn && o.conn.readyState == 4) {
2555                             window.clearInterval(oConn.poll[o.tId]);
2556                             delete oConn.poll[o.tId];
2557
2558                             if(callback && callback.timeout) {
2559                                 window.clearTimeout(oConn.timeout[o.tId]);
2560                                 delete oConn.timeout[o.tId];
2561                             }
2562
2563                             oConn.handleTransactionResponse(o, callback);
2564                         }
2565                     }
2566                     , this.pollInterval);
2567         },
2568
2569         handleTransactionResponse:function(o, callback, isAbort)
2570         {
2571
2572             if (!callback) {
2573                 this.releaseObject(o);
2574                 return;
2575             }
2576
2577             var httpStatus, responseObject;
2578
2579             try
2580             {
2581                 if (o.conn.status !== undefined && o.conn.status != 0) {
2582                     httpStatus = o.conn.status;
2583                 }
2584                 else {
2585                     httpStatus = 13030;
2586                 }
2587             }
2588             catch(e) {
2589
2590
2591                 httpStatus = 13030;
2592             }
2593
2594             if (httpStatus >= 200 && httpStatus < 300) {
2595                 responseObject = this.createResponseObject(o, callback.argument);
2596                 if (callback.success) {
2597                     if (!callback.scope) {
2598                         callback.success(responseObject);
2599                     }
2600                     else {
2601
2602
2603                         callback.success.apply(callback.scope, [responseObject]);
2604                     }
2605                 }
2606             }
2607             else {
2608                 switch (httpStatus) {
2609
2610                     case 12002:
2611                     case 12029:
2612                     case 12030:
2613                     case 12031:
2614                     case 12152:
2615                     case 13030:
2616                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2617                         if (callback.failure) {
2618                             if (!callback.scope) {
2619                                 callback.failure(responseObject);
2620                             }
2621                             else {
2622                                 callback.failure.apply(callback.scope, [responseObject]);
2623                             }
2624                         }
2625                         break;
2626                     default:
2627                         responseObject = this.createResponseObject(o, callback.argument);
2628                         if (callback.failure) {
2629                             if (!callback.scope) {
2630                                 callback.failure(responseObject);
2631                             }
2632                             else {
2633                                 callback.failure.apply(callback.scope, [responseObject]);
2634                             }
2635                         }
2636                 }
2637             }
2638
2639             this.releaseObject(o);
2640             responseObject = null;
2641         },
2642
2643         createResponseObject:function(o, callbackArg)
2644         {
2645             var obj = {};
2646             var headerObj = {};
2647
2648             try
2649             {
2650                 var headerStr = o.conn.getAllResponseHeaders();
2651                 var header = headerStr.split('\n');
2652                 for (var i = 0; i < header.length; i++) {
2653                     var delimitPos = header[i].indexOf(':');
2654                     if (delimitPos != -1) {
2655                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2656                     }
2657                 }
2658             }
2659             catch(e) {
2660             }
2661
2662             obj.tId = o.tId;
2663             obj.status = o.conn.status;
2664             obj.statusText = o.conn.statusText;
2665             obj.getResponseHeader = headerObj;
2666             obj.getAllResponseHeaders = headerStr;
2667             obj.responseText = o.conn.responseText;
2668             obj.responseXML = o.conn.responseXML;
2669
2670             if (typeof callbackArg !== undefined) {
2671                 obj.argument = callbackArg;
2672             }
2673
2674             return obj;
2675         },
2676
2677         createExceptionObject:function(tId, callbackArg, isAbort)
2678         {
2679             var COMM_CODE = 0;
2680             var COMM_ERROR = 'communication failure';
2681             var ABORT_CODE = -1;
2682             var ABORT_ERROR = 'transaction aborted';
2683
2684             var obj = {};
2685
2686             obj.tId = tId;
2687             if (isAbort) {
2688                 obj.status = ABORT_CODE;
2689                 obj.statusText = ABORT_ERROR;
2690             }
2691             else {
2692                 obj.status = COMM_CODE;
2693                 obj.statusText = COMM_ERROR;
2694             }
2695
2696             if (callbackArg) {
2697                 obj.argument = callbackArg;
2698             }
2699
2700             return obj;
2701         },
2702
2703         initHeader:function(label, value, isDefault)
2704         {
2705             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2706
2707             if (headerObj[label] === undefined) {
2708                 headerObj[label] = value;
2709             }
2710             else {
2711
2712
2713                 headerObj[label] = value + "," + headerObj[label];
2714             }
2715
2716             if (isDefault) {
2717                 this.hasDefaultHeaders = true;
2718             }
2719             else {
2720                 this.hasHeaders = true;
2721             }
2722         },
2723
2724
2725         setHeader:function(o)
2726         {
2727             if (this.hasDefaultHeaders) {
2728                 for (var prop in this.defaultHeaders) {
2729                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2730                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2731                     }
2732                 }
2733             }
2734
2735             if (this.hasHeaders) {
2736                 for (var prop in this.headers) {
2737                     if (this.headers.hasOwnProperty(prop)) {
2738                         o.conn.setRequestHeader(prop, this.headers[prop]);
2739                     }
2740                 }
2741                 this.headers = {};
2742                 this.hasHeaders = false;
2743             }
2744         },
2745
2746         resetDefaultHeaders:function() {
2747             delete this.defaultHeaders;
2748             this.defaultHeaders = {};
2749             this.hasDefaultHeaders = false;
2750         },
2751
2752         abort:function(o, callback, isTimeout)
2753         {
2754             if(this.isCallInProgress(o)) {
2755                 o.conn.abort();
2756                 window.clearInterval(this.poll[o.tId]);
2757                 delete this.poll[o.tId];
2758                 if (isTimeout) {
2759                     delete this.timeout[o.tId];
2760                 }
2761
2762                 this.handleTransactionResponse(o, callback, true);
2763
2764                 return true;
2765             }
2766             else {
2767                 return false;
2768             }
2769         },
2770
2771
2772         isCallInProgress:function(o)
2773         {
2774             if (o && o.conn) {
2775                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2776             }
2777             else {
2778
2779                 return false;
2780             }
2781         },
2782
2783
2784         releaseObject:function(o)
2785         {
2786
2787             o.conn = null;
2788
2789             o = null;
2790         },
2791
2792         activeX:[
2793         'MSXML2.XMLHTTP.3.0',
2794         'MSXML2.XMLHTTP',
2795         'Microsoft.XMLHTTP'
2796         ]
2797
2798
2799     };
2800 })();/*
2801  * Portions of this file are based on pieces of Yahoo User Interface Library
2802  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2803  * YUI licensed under the BSD License:
2804  * http://developer.yahoo.net/yui/license.txt
2805  * <script type="text/javascript">
2806  *
2807  */
2808
2809 Roo.lib.Region = function(t, r, b, l) {
2810     this.top = t;
2811     this[1] = t;
2812     this.right = r;
2813     this.bottom = b;
2814     this.left = l;
2815     this[0] = l;
2816 };
2817
2818
2819 Roo.lib.Region.prototype = {
2820     contains : function(region) {
2821         return ( region.left >= this.left &&
2822                  region.right <= this.right &&
2823                  region.top >= this.top &&
2824                  region.bottom <= this.bottom    );
2825
2826     },
2827
2828     getArea : function() {
2829         return ( (this.bottom - this.top) * (this.right - this.left) );
2830     },
2831
2832     intersect : function(region) {
2833         var t = Math.max(this.top, region.top);
2834         var r = Math.min(this.right, region.right);
2835         var b = Math.min(this.bottom, region.bottom);
2836         var l = Math.max(this.left, region.left);
2837
2838         if (b >= t && r >= l) {
2839             return new Roo.lib.Region(t, r, b, l);
2840         } else {
2841             return null;
2842         }
2843     },
2844     union : function(region) {
2845         var t = Math.min(this.top, region.top);
2846         var r = Math.max(this.right, region.right);
2847         var b = Math.max(this.bottom, region.bottom);
2848         var l = Math.min(this.left, region.left);
2849
2850         return new Roo.lib.Region(t, r, b, l);
2851     },
2852
2853     adjust : function(t, l, b, r) {
2854         this.top += t;
2855         this.left += l;
2856         this.right += r;
2857         this.bottom += b;
2858         return this;
2859     }
2860 };
2861
2862 Roo.lib.Region.getRegion = function(el) {
2863     var p = Roo.lib.Dom.getXY(el);
2864
2865     var t = p[1];
2866     var r = p[0] + el.offsetWidth;
2867     var b = p[1] + el.offsetHeight;
2868     var l = p[0];
2869
2870     return new Roo.lib.Region(t, r, b, l);
2871 };
2872 /*
2873  * Portions of this file are based on pieces of Yahoo User Interface Library
2874  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2875  * YUI licensed under the BSD License:
2876  * http://developer.yahoo.net/yui/license.txt
2877  * <script type="text/javascript">
2878  *
2879  */
2880 //@@dep Roo.lib.Region
2881
2882
2883 Roo.lib.Point = function(x, y) {
2884     if (x instanceof Array) {
2885         y = x[1];
2886         x = x[0];
2887     }
2888     this.x = this.right = this.left = this[0] = x;
2889     this.y = this.top = this.bottom = this[1] = y;
2890 };
2891
2892 Roo.lib.Point.prototype = new Roo.lib.Region();
2893 /*
2894  * Portions of this file are based on pieces of Yahoo User Interface Library
2895  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2896  * YUI licensed under the BSD License:
2897  * http://developer.yahoo.net/yui/license.txt
2898  * <script type="text/javascript">
2899  *
2900  */
2901  
2902 (function() {   
2903
2904     Roo.lib.Anim = {
2905         scroll : function(el, args, duration, easing, cb, scope) {
2906             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2907         },
2908
2909         motion : function(el, args, duration, easing, cb, scope) {
2910             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2911         },
2912
2913         color : function(el, args, duration, easing, cb, scope) {
2914             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2915         },
2916
2917         run : function(el, args, duration, easing, cb, scope, type) {
2918             type = type || Roo.lib.AnimBase;
2919             if (typeof easing == "string") {
2920                 easing = Roo.lib.Easing[easing];
2921             }
2922             var anim = new type(el, args, duration, easing);
2923             anim.animateX(function() {
2924                 Roo.callback(cb, scope);
2925             });
2926             return anim;
2927         }
2928     };
2929 })();/*
2930  * Portions of this file are based on pieces of Yahoo User Interface Library
2931  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2932  * YUI licensed under the BSD License:
2933  * http://developer.yahoo.net/yui/license.txt
2934  * <script type="text/javascript">
2935  *
2936  */
2937
2938 (function() {    
2939     var libFlyweight;
2940     
2941     function fly(el) {
2942         if (!libFlyweight) {
2943             libFlyweight = new Roo.Element.Flyweight();
2944         }
2945         libFlyweight.dom = el;
2946         return libFlyweight;
2947     }
2948
2949     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2950     
2951    
2952     
2953     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2954         if (el) {
2955             this.init(el, attributes, duration, method);
2956         }
2957     };
2958
2959     Roo.lib.AnimBase.fly = fly;
2960     
2961     
2962     
2963     Roo.lib.AnimBase.prototype = {
2964
2965         toString: function() {
2966             var el = this.getEl();
2967             var id = el.id || el.tagName;
2968             return ("Anim " + id);
2969         },
2970
2971         patterns: {
2972             noNegatives:        /width|height|opacity|padding/i,
2973             offsetAttribute:  /^((width|height)|(top|left))$/,
2974             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2975             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2976         },
2977
2978
2979         doMethod: function(attr, start, end) {
2980             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2981         },
2982
2983
2984         setAttribute: function(attr, val, unit) {
2985             if (this.patterns.noNegatives.test(attr)) {
2986                 val = (val > 0) ? val : 0;
2987             }
2988
2989             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2990         },
2991
2992
2993         getAttribute: function(attr) {
2994             var el = this.getEl();
2995             var val = fly(el).getStyle(attr);
2996
2997             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2998                 return parseFloat(val);
2999             }
3000
3001             var a = this.patterns.offsetAttribute.exec(attr) || [];
3002             var pos = !!( a[3] );
3003             var box = !!( a[2] );
3004
3005
3006             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3007                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3008             } else {
3009                 val = 0;
3010             }
3011
3012             return val;
3013         },
3014
3015
3016         getDefaultUnit: function(attr) {
3017             if (this.patterns.defaultUnit.test(attr)) {
3018                 return 'px';
3019             }
3020
3021             return '';
3022         },
3023
3024         animateX : function(callback, scope) {
3025             var f = function() {
3026                 this.onComplete.removeListener(f);
3027                 if (typeof callback == "function") {
3028                     callback.call(scope || this, this);
3029                 }
3030             };
3031             this.onComplete.addListener(f, this);
3032             this.animate();
3033         },
3034
3035
3036         setRuntimeAttribute: function(attr) {
3037             var start;
3038             var end;
3039             var attributes = this.attributes;
3040
3041             this.runtimeAttributes[attr] = {};
3042
3043             var isset = function(prop) {
3044                 return (typeof prop !== 'undefined');
3045             };
3046
3047             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3048                 return false;
3049             }
3050
3051             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3052
3053
3054             if (isset(attributes[attr]['to'])) {
3055                 end = attributes[attr]['to'];
3056             } else if (isset(attributes[attr]['by'])) {
3057                 if (start.constructor == Array) {
3058                     end = [];
3059                     for (var i = 0, len = start.length; i < len; ++i) {
3060                         end[i] = start[i] + attributes[attr]['by'][i];
3061                     }
3062                 } else {
3063                     end = start + attributes[attr]['by'];
3064                 }
3065             }
3066
3067             this.runtimeAttributes[attr].start = start;
3068             this.runtimeAttributes[attr].end = end;
3069
3070
3071             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3072         },
3073
3074
3075         init: function(el, attributes, duration, method) {
3076
3077             var isAnimated = false;
3078
3079
3080             var startTime = null;
3081
3082
3083             var actualFrames = 0;
3084
3085
3086             el = Roo.getDom(el);
3087
3088
3089             this.attributes = attributes || {};
3090
3091
3092             this.duration = duration || 1;
3093
3094
3095             this.method = method || Roo.lib.Easing.easeNone;
3096
3097
3098             this.useSeconds = true;
3099
3100
3101             this.currentFrame = 0;
3102
3103
3104             this.totalFrames = Roo.lib.AnimMgr.fps;
3105
3106
3107             this.getEl = function() {
3108                 return el;
3109             };
3110
3111
3112             this.isAnimated = function() {
3113                 return isAnimated;
3114             };
3115
3116
3117             this.getStartTime = function() {
3118                 return startTime;
3119             };
3120
3121             this.runtimeAttributes = {};
3122
3123
3124             this.animate = function() {
3125                 if (this.isAnimated()) {
3126                     return false;
3127                 }
3128
3129                 this.currentFrame = 0;
3130
3131                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3132
3133                 Roo.lib.AnimMgr.registerElement(this);
3134             };
3135
3136
3137             this.stop = function(finish) {
3138                 if (finish) {
3139                     this.currentFrame = this.totalFrames;
3140                     this._onTween.fire();
3141                 }
3142                 Roo.lib.AnimMgr.stop(this);
3143             };
3144
3145             var onStart = function() {
3146                 this.onStart.fire();
3147
3148                 this.runtimeAttributes = {};
3149                 for (var attr in this.attributes) {
3150                     this.setRuntimeAttribute(attr);
3151                 }
3152
3153                 isAnimated = true;
3154                 actualFrames = 0;
3155                 startTime = new Date();
3156             };
3157
3158
3159             var onTween = function() {
3160                 var data = {
3161                     duration: new Date() - this.getStartTime(),
3162                     currentFrame: this.currentFrame
3163                 };
3164
3165                 data.toString = function() {
3166                     return (
3167                             'duration: ' + data.duration +
3168                             ', currentFrame: ' + data.currentFrame
3169                             );
3170                 };
3171
3172                 this.onTween.fire(data);
3173
3174                 var runtimeAttributes = this.runtimeAttributes;
3175
3176                 for (var attr in runtimeAttributes) {
3177                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3178                 }
3179
3180                 actualFrames += 1;
3181             };
3182
3183             var onComplete = function() {
3184                 var actual_duration = (new Date() - startTime) / 1000 ;
3185
3186                 var data = {
3187                     duration: actual_duration,
3188                     frames: actualFrames,
3189                     fps: actualFrames / actual_duration
3190                 };
3191
3192                 data.toString = function() {
3193                     return (
3194                             'duration: ' + data.duration +
3195                             ', frames: ' + data.frames +
3196                             ', fps: ' + data.fps
3197                             );
3198                 };
3199
3200                 isAnimated = false;
3201                 actualFrames = 0;
3202                 this.onComplete.fire(data);
3203             };
3204
3205
3206             this._onStart = new Roo.util.Event(this);
3207             this.onStart = new Roo.util.Event(this);
3208             this.onTween = new Roo.util.Event(this);
3209             this._onTween = new Roo.util.Event(this);
3210             this.onComplete = new Roo.util.Event(this);
3211             this._onComplete = new Roo.util.Event(this);
3212             this._onStart.addListener(onStart);
3213             this._onTween.addListener(onTween);
3214             this._onComplete.addListener(onComplete);
3215         }
3216     };
3217 })();
3218 /*
3219  * Portions of this file are based on pieces of Yahoo User Interface Library
3220  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3221  * YUI licensed under the BSD License:
3222  * http://developer.yahoo.net/yui/license.txt
3223  * <script type="text/javascript">
3224  *
3225  */
3226
3227 Roo.lib.AnimMgr = new function() {
3228
3229         var thread = null;
3230
3231
3232         var queue = [];
3233
3234
3235         var tweenCount = 0;
3236
3237
3238         this.fps = 1000;
3239
3240
3241         this.delay = 1;
3242
3243
3244         this.registerElement = function(tween) {
3245             queue[queue.length] = tween;
3246             tweenCount += 1;
3247             tween._onStart.fire();
3248             this.start();
3249         };
3250
3251
3252         this.unRegister = function(tween, index) {
3253             tween._onComplete.fire();
3254             index = index || getIndex(tween);
3255             if (index != -1) {
3256                 queue.splice(index, 1);
3257             }
3258
3259             tweenCount -= 1;
3260             if (tweenCount <= 0) {
3261                 this.stop();
3262             }
3263         };
3264
3265
3266         this.start = function() {
3267             if (thread === null) {
3268                 thread = setInterval(this.run, this.delay);
3269             }
3270         };
3271
3272
3273         this.stop = function(tween) {
3274             if (!tween) {
3275                 clearInterval(thread);
3276
3277                 for (var i = 0, len = queue.length; i < len; ++i) {
3278                     if (queue[0].isAnimated()) {
3279                         this.unRegister(queue[0], 0);
3280                     }
3281                 }
3282
3283                 queue = [];
3284                 thread = null;
3285                 tweenCount = 0;
3286             }
3287             else {
3288                 this.unRegister(tween);
3289             }
3290         };
3291
3292
3293         this.run = function() {
3294             for (var i = 0, len = queue.length; i < len; ++i) {
3295                 var tween = queue[i];
3296                 if (!tween || !tween.isAnimated()) {
3297                     continue;
3298                 }
3299
3300                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3301                 {
3302                     tween.currentFrame += 1;
3303
3304                     if (tween.useSeconds) {
3305                         correctFrame(tween);
3306                     }
3307                     tween._onTween.fire();
3308                 }
3309                 else {
3310                     Roo.lib.AnimMgr.stop(tween, i);
3311                 }
3312             }
3313         };
3314
3315         var getIndex = function(anim) {
3316             for (var i = 0, len = queue.length; i < len; ++i) {
3317                 if (queue[i] == anim) {
3318                     return i;
3319                 }
3320             }
3321             return -1;
3322         };
3323
3324
3325         var correctFrame = function(tween) {
3326             var frames = tween.totalFrames;
3327             var frame = tween.currentFrame;
3328             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3329             var elapsed = (new Date() - tween.getStartTime());
3330             var tweak = 0;
3331
3332             if (elapsed < tween.duration * 1000) {
3333                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3334             } else {
3335                 tweak = frames - (frame + 1);
3336             }
3337             if (tweak > 0 && isFinite(tweak)) {
3338                 if (tween.currentFrame + tweak >= frames) {
3339                     tweak = frames - (frame + 1);
3340                 }
3341
3342                 tween.currentFrame += tweak;
3343             }
3344         };
3345     };/*
3346  * Portions of this file are based on pieces of Yahoo User Interface Library
3347  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3348  * YUI licensed under the BSD License:
3349  * http://developer.yahoo.net/yui/license.txt
3350  * <script type="text/javascript">
3351  *
3352  */
3353 Roo.lib.Bezier = new function() {
3354
3355         this.getPosition = function(points, t) {
3356             var n = points.length;
3357             var tmp = [];
3358
3359             for (var i = 0; i < n; ++i) {
3360                 tmp[i] = [points[i][0], points[i][1]];
3361             }
3362
3363             for (var j = 1; j < n; ++j) {
3364                 for (i = 0; i < n - j; ++i) {
3365                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3366                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3367                 }
3368             }
3369
3370             return [ tmp[0][0], tmp[0][1] ];
3371
3372         };
3373     };/*
3374  * Portions of this file are based on pieces of Yahoo User Interface Library
3375  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3376  * YUI licensed under the BSD License:
3377  * http://developer.yahoo.net/yui/license.txt
3378  * <script type="text/javascript">
3379  *
3380  */
3381 (function() {
3382
3383     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3384         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3385     };
3386
3387     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3388
3389     var fly = Roo.lib.AnimBase.fly;
3390     var Y = Roo.lib;
3391     var superclass = Y.ColorAnim.superclass;
3392     var proto = Y.ColorAnim.prototype;
3393
3394     proto.toString = function() {
3395         var el = this.getEl();
3396         var id = el.id || el.tagName;
3397         return ("ColorAnim " + id);
3398     };
3399
3400     proto.patterns.color = /color$/i;
3401     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3402     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3403     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3404     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3405
3406
3407     proto.parseColor = function(s) {
3408         if (s.length == 3) {
3409             return s;
3410         }
3411
3412         var c = this.patterns.hex.exec(s);
3413         if (c && c.length == 4) {
3414             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3415         }
3416
3417         c = this.patterns.rgb.exec(s);
3418         if (c && c.length == 4) {
3419             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3420         }
3421
3422         c = this.patterns.hex3.exec(s);
3423         if (c && c.length == 4) {
3424             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3425         }
3426
3427         return null;
3428     };
3429     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3430     proto.getAttribute = function(attr) {
3431         var el = this.getEl();
3432         if (this.patterns.color.test(attr)) {
3433             var val = fly(el).getStyle(attr);
3434
3435             if (this.patterns.transparent.test(val)) {
3436                 var parent = el.parentNode;
3437                 val = fly(parent).getStyle(attr);
3438
3439                 while (parent && this.patterns.transparent.test(val)) {
3440                     parent = parent.parentNode;
3441                     val = fly(parent).getStyle(attr);
3442                     if (parent.tagName.toUpperCase() == 'HTML') {
3443                         val = '#fff';
3444                     }
3445                 }
3446             }
3447         } else {
3448             val = superclass.getAttribute.call(this, attr);
3449         }
3450
3451         return val;
3452     };
3453     proto.getAttribute = function(attr) {
3454         var el = this.getEl();
3455         if (this.patterns.color.test(attr)) {
3456             var val = fly(el).getStyle(attr);
3457
3458             if (this.patterns.transparent.test(val)) {
3459                 var parent = el.parentNode;
3460                 val = fly(parent).getStyle(attr);
3461
3462                 while (parent && this.patterns.transparent.test(val)) {
3463                     parent = parent.parentNode;
3464                     val = fly(parent).getStyle(attr);
3465                     if (parent.tagName.toUpperCase() == 'HTML') {
3466                         val = '#fff';
3467                     }
3468                 }
3469             }
3470         } else {
3471             val = superclass.getAttribute.call(this, attr);
3472         }
3473
3474         return val;
3475     };
3476
3477     proto.doMethod = function(attr, start, end) {
3478         var val;
3479
3480         if (this.patterns.color.test(attr)) {
3481             val = [];
3482             for (var i = 0, len = start.length; i < len; ++i) {
3483                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3484             }
3485
3486             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3487         }
3488         else {
3489             val = superclass.doMethod.call(this, attr, start, end);
3490         }
3491
3492         return val;
3493     };
3494
3495     proto.setRuntimeAttribute = function(attr) {
3496         superclass.setRuntimeAttribute.call(this, attr);
3497
3498         if (this.patterns.color.test(attr)) {
3499             var attributes = this.attributes;
3500             var start = this.parseColor(this.runtimeAttributes[attr].start);
3501             var end = this.parseColor(this.runtimeAttributes[attr].end);
3502
3503             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3504                 end = this.parseColor(attributes[attr].by);
3505
3506                 for (var i = 0, len = start.length; i < len; ++i) {
3507                     end[i] = start[i] + end[i];
3508                 }
3509             }
3510
3511             this.runtimeAttributes[attr].start = start;
3512             this.runtimeAttributes[attr].end = end;
3513         }
3514     };
3515 })();
3516
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525 Roo.lib.Easing = {
3526
3527
3528     easeNone: function (t, b, c, d) {
3529         return c * t / d + b;
3530     },
3531
3532
3533     easeIn: function (t, b, c, d) {
3534         return c * (t /= d) * t + b;
3535     },
3536
3537
3538     easeOut: function (t, b, c, d) {
3539         return -c * (t /= d) * (t - 2) + b;
3540     },
3541
3542
3543     easeBoth: function (t, b, c, d) {
3544         if ((t /= d / 2) < 1) {
3545             return c / 2 * t * t + b;
3546         }
3547
3548         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3549     },
3550
3551
3552     easeInStrong: function (t, b, c, d) {
3553         return c * (t /= d) * t * t * t + b;
3554     },
3555
3556
3557     easeOutStrong: function (t, b, c, d) {
3558         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3559     },
3560
3561
3562     easeBothStrong: function (t, b, c, d) {
3563         if ((t /= d / 2) < 1) {
3564             return c / 2 * t * t * t * t + b;
3565         }
3566
3567         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3568     },
3569
3570
3571
3572     elasticIn: function (t, b, c, d, a, p) {
3573         if (t == 0) {
3574             return b;
3575         }
3576         if ((t /= d) == 1) {
3577             return b + c;
3578         }
3579         if (!p) {
3580             p = d * .3;
3581         }
3582
3583         if (!a || a < Math.abs(c)) {
3584             a = c;
3585             var s = p / 4;
3586         }
3587         else {
3588             var s = p / (2 * Math.PI) * Math.asin(c / a);
3589         }
3590
3591         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3592     },
3593
3594
3595     elasticOut: function (t, b, c, d, a, p) {
3596         if (t == 0) {
3597             return b;
3598         }
3599         if ((t /= d) == 1) {
3600             return b + c;
3601         }
3602         if (!p) {
3603             p = d * .3;
3604         }
3605
3606         if (!a || a < Math.abs(c)) {
3607             a = c;
3608             var s = p / 4;
3609         }
3610         else {
3611             var s = p / (2 * Math.PI) * Math.asin(c / a);
3612         }
3613
3614         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3615     },
3616
3617
3618     elasticBoth: function (t, b, c, d, a, p) {
3619         if (t == 0) {
3620             return b;
3621         }
3622
3623         if ((t /= d / 2) == 2) {
3624             return b + c;
3625         }
3626
3627         if (!p) {
3628             p = d * (.3 * 1.5);
3629         }
3630
3631         if (!a || a < Math.abs(c)) {
3632             a = c;
3633             var s = p / 4;
3634         }
3635         else {
3636             var s = p / (2 * Math.PI) * Math.asin(c / a);
3637         }
3638
3639         if (t < 1) {
3640             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3641                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3642         }
3643         return a * Math.pow(2, -10 * (t -= 1)) *
3644                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3645     },
3646
3647
3648
3649     backIn: function (t, b, c, d, s) {
3650         if (typeof s == 'undefined') {
3651             s = 1.70158;
3652         }
3653         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3654     },
3655
3656
3657     backOut: function (t, b, c, d, s) {
3658         if (typeof s == 'undefined') {
3659             s = 1.70158;
3660         }
3661         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3662     },
3663
3664
3665     backBoth: function (t, b, c, d, s) {
3666         if (typeof s == 'undefined') {
3667             s = 1.70158;
3668         }
3669
3670         if ((t /= d / 2 ) < 1) {
3671             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3672         }
3673         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3674     },
3675
3676
3677     bounceIn: function (t, b, c, d) {
3678         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3679     },
3680
3681
3682     bounceOut: function (t, b, c, d) {
3683         if ((t /= d) < (1 / 2.75)) {
3684             return c * (7.5625 * t * t) + b;
3685         } else if (t < (2 / 2.75)) {
3686             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3687         } else if (t < (2.5 / 2.75)) {
3688             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3689         }
3690         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3691     },
3692
3693
3694     bounceBoth: function (t, b, c, d) {
3695         if (t < d / 2) {
3696             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3697         }
3698         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3699     }
3700 };/*
3701  * Portions of this file are based on pieces of Yahoo User Interface Library
3702  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3703  * YUI licensed under the BSD License:
3704  * http://developer.yahoo.net/yui/license.txt
3705  * <script type="text/javascript">
3706  *
3707  */
3708     (function() {
3709         Roo.lib.Motion = function(el, attributes, duration, method) {
3710             if (el) {
3711                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3712             }
3713         };
3714
3715         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3716
3717
3718         var Y = Roo.lib;
3719         var superclass = Y.Motion.superclass;
3720         var proto = Y.Motion.prototype;
3721
3722         proto.toString = function() {
3723             var el = this.getEl();
3724             var id = el.id || el.tagName;
3725             return ("Motion " + id);
3726         };
3727
3728         proto.patterns.points = /^points$/i;
3729
3730         proto.setAttribute = function(attr, val, unit) {
3731             if (this.patterns.points.test(attr)) {
3732                 unit = unit || 'px';
3733                 superclass.setAttribute.call(this, 'left', val[0], unit);
3734                 superclass.setAttribute.call(this, 'top', val[1], unit);
3735             } else {
3736                 superclass.setAttribute.call(this, attr, val, unit);
3737             }
3738         };
3739
3740         proto.getAttribute = function(attr) {
3741             if (this.patterns.points.test(attr)) {
3742                 var val = [
3743                         superclass.getAttribute.call(this, 'left'),
3744                         superclass.getAttribute.call(this, 'top')
3745                         ];
3746             } else {
3747                 val = superclass.getAttribute.call(this, attr);
3748             }
3749
3750             return val;
3751         };
3752
3753         proto.doMethod = function(attr, start, end) {
3754             var val = null;
3755
3756             if (this.patterns.points.test(attr)) {
3757                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3758                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3759             } else {
3760                 val = superclass.doMethod.call(this, attr, start, end);
3761             }
3762             return val;
3763         };
3764
3765         proto.setRuntimeAttribute = function(attr) {
3766             if (this.patterns.points.test(attr)) {
3767                 var el = this.getEl();
3768                 var attributes = this.attributes;
3769                 var start;
3770                 var control = attributes['points']['control'] || [];
3771                 var end;
3772                 var i, len;
3773
3774                 if (control.length > 0 && !(control[0] instanceof Array)) {
3775                     control = [control];
3776                 } else {
3777                     var tmp = [];
3778                     for (i = 0,len = control.length; i < len; ++i) {
3779                         tmp[i] = control[i];
3780                     }
3781                     control = tmp;
3782                 }
3783
3784                 Roo.fly(el).position();
3785
3786                 if (isset(attributes['points']['from'])) {
3787                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3788                 }
3789                 else {
3790                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3791                 }
3792
3793                 start = this.getAttribute('points');
3794
3795
3796                 if (isset(attributes['points']['to'])) {
3797                     end = translateValues.call(this, attributes['points']['to'], start);
3798
3799                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3800                     for (i = 0,len = control.length; i < len; ++i) {
3801                         control[i] = translateValues.call(this, control[i], start);
3802                     }
3803
3804
3805                 } else if (isset(attributes['points']['by'])) {
3806                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3807
3808                     for (i = 0,len = control.length; i < len; ++i) {
3809                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3810                     }
3811                 }
3812
3813                 this.runtimeAttributes[attr] = [start];
3814
3815                 if (control.length > 0) {
3816                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3817                 }
3818
3819                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3820             }
3821             else {
3822                 superclass.setRuntimeAttribute.call(this, attr);
3823             }
3824         };
3825
3826         var translateValues = function(val, start) {
3827             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3828             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3829
3830             return val;
3831         };
3832
3833         var isset = function(prop) {
3834             return (typeof prop !== 'undefined');
3835         };
3836     })();
3837 /*
3838  * Portions of this file are based on pieces of Yahoo User Interface Library
3839  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3840  * YUI licensed under the BSD License:
3841  * http://developer.yahoo.net/yui/license.txt
3842  * <script type="text/javascript">
3843  *
3844  */
3845     (function() {
3846         Roo.lib.Scroll = function(el, attributes, duration, method) {
3847             if (el) {
3848                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3849             }
3850         };
3851
3852         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3853
3854
3855         var Y = Roo.lib;
3856         var superclass = Y.Scroll.superclass;
3857         var proto = Y.Scroll.prototype;
3858
3859         proto.toString = function() {
3860             var el = this.getEl();
3861             var id = el.id || el.tagName;
3862             return ("Scroll " + id);
3863         };
3864
3865         proto.doMethod = function(attr, start, end) {
3866             var val = null;
3867
3868             if (attr == 'scroll') {
3869                 val = [
3870                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3871                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3872                         ];
3873
3874             } else {
3875                 val = superclass.doMethod.call(this, attr, start, end);
3876             }
3877             return val;
3878         };
3879
3880         proto.getAttribute = function(attr) {
3881             var val = null;
3882             var el = this.getEl();
3883
3884             if (attr == 'scroll') {
3885                 val = [ el.scrollLeft, el.scrollTop ];
3886             } else {
3887                 val = superclass.getAttribute.call(this, attr);
3888             }
3889
3890             return val;
3891         };
3892
3893         proto.setAttribute = function(attr, val, unit) {
3894             var el = this.getEl();
3895
3896             if (attr == 'scroll') {
3897                 el.scrollLeft = val[0];
3898                 el.scrollTop = val[1];
3899             } else {
3900                 superclass.setAttribute.call(this, attr, val, unit);
3901             }
3902         };
3903     })();
3904 /*
3905  * Based on:
3906  * Ext JS Library 1.1.1
3907  * Copyright(c) 2006-2007, Ext JS, LLC.
3908  *
3909  * Originally Released Under LGPL - original licence link has changed is not relivant.
3910  *
3911  * Fork - LGPL
3912  * <script type="text/javascript">
3913  */
3914  
3915
3916 /**
3917  * @class Roo.DomHelper
3918  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3919  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3920  * @singleton
3921  */
3922 Roo.DomHelper = function(){
3923     var tempTableEl = null;
3924     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3925     var tableRe = /^table|tbody|tr|td$/i;
3926     var xmlns = {};
3927     // build as innerHTML where available
3928     /** @ignore */
3929     var createHtml = function(o){
3930         if(typeof o == 'string'){
3931             return o;
3932         }
3933         var b = "";
3934         if(!o.tag){
3935             o.tag = "div";
3936         }
3937         b += "<" + o.tag;
3938         for(var attr in o){
3939             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3940             if(attr == "style"){
3941                 var s = o["style"];
3942                 if(typeof s == "function"){
3943                     s = s.call();
3944                 }
3945                 if(typeof s == "string"){
3946                     b += ' style="' + s + '"';
3947                 }else if(typeof s == "object"){
3948                     b += ' style="';
3949                     for(var key in s){
3950                         if(typeof s[key] != "function"){
3951                             b += key + ":" + s[key] + ";";
3952                         }
3953                     }
3954                     b += '"';
3955                 }
3956             }else{
3957                 if(attr == "cls"){
3958                     b += ' class="' + o["cls"] + '"';
3959                 }else if(attr == "htmlFor"){
3960                     b += ' for="' + o["htmlFor"] + '"';
3961                 }else{
3962                     b += " " + attr + '="' + o[attr] + '"';
3963                 }
3964             }
3965         }
3966         if(emptyTags.test(o.tag)){
3967             b += "/>";
3968         }else{
3969             b += ">";
3970             var cn = o.children || o.cn;
3971             if(cn){
3972                 //http://bugs.kde.org/show_bug.cgi?id=71506
3973                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3974                     for(var i = 0, len = cn.length; i < len; i++) {
3975                         b += createHtml(cn[i], b);
3976                     }
3977                 }else{
3978                     b += createHtml(cn, b);
3979                 }
3980             }
3981             if(o.html){
3982                 b += o.html;
3983             }
3984             b += "</" + o.tag + ">";
3985         }
3986         return b;
3987     };
3988
3989     // build as dom
3990     /** @ignore */
3991     var createDom = function(o, parentNode){
3992          
3993         // defininition craeted..
3994         var ns = false;
3995         if (o.ns && o.ns != 'html') {
3996                
3997             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3998                 xmlns[o.ns] = o.xmlns;
3999                 ns = o.xmlns;
4000             }
4001             if (typeof(xmlns[o.ns]) == 'undefined') {
4002                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4003             }
4004             ns = xmlns[o.ns];
4005         }
4006         
4007         
4008         if (typeof(o) == 'string') {
4009             return parentNode.appendChild(document.createTextNode(o));
4010         }
4011         o.tag = o.tag || div;
4012         if (o.ns && Roo.isIE) {
4013             ns = false;
4014             o.tag = o.ns + ':' + o.tag;
4015             
4016         }
4017         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4018         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4019         for(var attr in o){
4020             
4021             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4022                     attr == "style" || typeof o[attr] == "function") continue;
4023                     
4024             if(attr=="cls" && Roo.isIE){
4025                 el.className = o["cls"];
4026             }else{
4027                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4028                 else el[attr] = o[attr];
4029             }
4030         }
4031         Roo.DomHelper.applyStyles(el, o.style);
4032         var cn = o.children || o.cn;
4033         if(cn){
4034             //http://bugs.kde.org/show_bug.cgi?id=71506
4035              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4036                 for(var i = 0, len = cn.length; i < len; i++) {
4037                     createDom(cn[i], el);
4038                 }
4039             }else{
4040                 createDom(cn, el);
4041             }
4042         }
4043         if(o.html){
4044             el.innerHTML = o.html;
4045         }
4046         if(parentNode){
4047            parentNode.appendChild(el);
4048         }
4049         return el;
4050     };
4051
4052     var ieTable = function(depth, s, h, e){
4053         tempTableEl.innerHTML = [s, h, e].join('');
4054         var i = -1, el = tempTableEl;
4055         while(++i < depth){
4056             el = el.firstChild;
4057         }
4058         return el;
4059     };
4060
4061     // kill repeat to save bytes
4062     var ts = '<table>',
4063         te = '</table>',
4064         tbs = ts+'<tbody>',
4065         tbe = '</tbody>'+te,
4066         trs = tbs + '<tr>',
4067         tre = '</tr>'+tbe;
4068
4069     /**
4070      * @ignore
4071      * Nasty code for IE's broken table implementation
4072      */
4073     var insertIntoTable = function(tag, where, el, html){
4074         if(!tempTableEl){
4075             tempTableEl = document.createElement('div');
4076         }
4077         var node;
4078         var before = null;
4079         if(tag == 'td'){
4080             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4081                 return;
4082             }
4083             if(where == 'beforebegin'){
4084                 before = el;
4085                 el = el.parentNode;
4086             } else{
4087                 before = el.nextSibling;
4088                 el = el.parentNode;
4089             }
4090             node = ieTable(4, trs, html, tre);
4091         }
4092         else if(tag == 'tr'){
4093             if(where == 'beforebegin'){
4094                 before = el;
4095                 el = el.parentNode;
4096                 node = ieTable(3, tbs, html, tbe);
4097             } else if(where == 'afterend'){
4098                 before = el.nextSibling;
4099                 el = el.parentNode;
4100                 node = ieTable(3, tbs, html, tbe);
4101             } else{ // INTO a TR
4102                 if(where == 'afterbegin'){
4103                     before = el.firstChild;
4104                 }
4105                 node = ieTable(4, trs, html, tre);
4106             }
4107         } else if(tag == 'tbody'){
4108             if(where == 'beforebegin'){
4109                 before = el;
4110                 el = el.parentNode;
4111                 node = ieTable(2, ts, html, te);
4112             } else if(where == 'afterend'){
4113                 before = el.nextSibling;
4114                 el = el.parentNode;
4115                 node = ieTable(2, ts, html, te);
4116             } else{
4117                 if(where == 'afterbegin'){
4118                     before = el.firstChild;
4119                 }
4120                 node = ieTable(3, tbs, html, tbe);
4121             }
4122         } else{ // TABLE
4123             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4124                 return;
4125             }
4126             if(where == 'afterbegin'){
4127                 before = el.firstChild;
4128             }
4129             node = ieTable(2, ts, html, te);
4130         }
4131         el.insertBefore(node, before);
4132         return node;
4133     };
4134
4135     return {
4136     /** True to force the use of DOM instead of html fragments @type Boolean */
4137     useDom : false,
4138
4139     /**
4140      * Returns the markup for the passed Element(s) config
4141      * @param {Object} o The Dom object spec (and children)
4142      * @return {String}
4143      */
4144     markup : function(o){
4145         return createHtml(o);
4146     },
4147
4148     /**
4149      * Applies a style specification to an element
4150      * @param {String/HTMLElement} el The element to apply styles to
4151      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4152      * a function which returns such a specification.
4153      */
4154     applyStyles : function(el, styles){
4155         if(styles){
4156            el = Roo.fly(el);
4157            if(typeof styles == "string"){
4158                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4159                var matches;
4160                while ((matches = re.exec(styles)) != null){
4161                    el.setStyle(matches[1], matches[2]);
4162                }
4163            }else if (typeof styles == "object"){
4164                for (var style in styles){
4165                   el.setStyle(style, styles[style]);
4166                }
4167            }else if (typeof styles == "function"){
4168                 Roo.DomHelper.applyStyles(el, styles.call());
4169            }
4170         }
4171     },
4172
4173     /**
4174      * Inserts an HTML fragment into the Dom
4175      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4176      * @param {HTMLElement} el The context element
4177      * @param {String} html The HTML fragmenet
4178      * @return {HTMLElement} The new node
4179      */
4180     insertHtml : function(where, el, html){
4181         where = where.toLowerCase();
4182         if(el.insertAdjacentHTML){
4183             if(tableRe.test(el.tagName)){
4184                 var rs;
4185                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4186                     return rs;
4187                 }
4188             }
4189             switch(where){
4190                 case "beforebegin":
4191                     el.insertAdjacentHTML('BeforeBegin', html);
4192                     return el.previousSibling;
4193                 case "afterbegin":
4194                     el.insertAdjacentHTML('AfterBegin', html);
4195                     return el.firstChild;
4196                 case "beforeend":
4197                     el.insertAdjacentHTML('BeforeEnd', html);
4198                     return el.lastChild;
4199                 case "afterend":
4200                     el.insertAdjacentHTML('AfterEnd', html);
4201                     return el.nextSibling;
4202             }
4203             throw 'Illegal insertion point -> "' + where + '"';
4204         }
4205         var range = el.ownerDocument.createRange();
4206         var frag;
4207         switch(where){
4208              case "beforebegin":
4209                 range.setStartBefore(el);
4210                 frag = range.createContextualFragment(html);
4211                 el.parentNode.insertBefore(frag, el);
4212                 return el.previousSibling;
4213              case "afterbegin":
4214                 if(el.firstChild){
4215                     range.setStartBefore(el.firstChild);
4216                     frag = range.createContextualFragment(html);
4217                     el.insertBefore(frag, el.firstChild);
4218                     return el.firstChild;
4219                 }else{
4220                     el.innerHTML = html;
4221                     return el.firstChild;
4222                 }
4223             case "beforeend":
4224                 if(el.lastChild){
4225                     range.setStartAfter(el.lastChild);
4226                     frag = range.createContextualFragment(html);
4227                     el.appendChild(frag);
4228                     return el.lastChild;
4229                 }else{
4230                     el.innerHTML = html;
4231                     return el.lastChild;
4232                 }
4233             case "afterend":
4234                 range.setStartAfter(el);
4235                 frag = range.createContextualFragment(html);
4236                 el.parentNode.insertBefore(frag, el.nextSibling);
4237                 return el.nextSibling;
4238             }
4239             throw 'Illegal insertion point -> "' + where + '"';
4240     },
4241
4242     /**
4243      * Creates new Dom element(s) and inserts them before el
4244      * @param {String/HTMLElement/Element} el The context element
4245      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4246      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4247      * @return {HTMLElement/Roo.Element} The new node
4248      */
4249     insertBefore : function(el, o, returnElement){
4250         return this.doInsert(el, o, returnElement, "beforeBegin");
4251     },
4252
4253     /**
4254      * Creates new Dom element(s) and inserts them after el
4255      * @param {String/HTMLElement/Element} el The context element
4256      * @param {Object} o The Dom object spec (and children)
4257      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4258      * @return {HTMLElement/Roo.Element} The new node
4259      */
4260     insertAfter : function(el, o, returnElement){
4261         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4262     },
4263
4264     /**
4265      * Creates new Dom element(s) and inserts them as the first child of el
4266      * @param {String/HTMLElement/Element} el The context element
4267      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4268      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4269      * @return {HTMLElement/Roo.Element} The new node
4270      */
4271     insertFirst : function(el, o, returnElement){
4272         return this.doInsert(el, o, returnElement, "afterBegin");
4273     },
4274
4275     // private
4276     doInsert : function(el, o, returnElement, pos, sibling){
4277         el = Roo.getDom(el);
4278         var newNode;
4279         if(this.useDom || o.ns){
4280             newNode = createDom(o, null);
4281             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4282         }else{
4283             var html = createHtml(o);
4284             newNode = this.insertHtml(pos, el, html);
4285         }
4286         return returnElement ? Roo.get(newNode, true) : newNode;
4287     },
4288
4289     /**
4290      * Creates new Dom element(s) and appends them to el
4291      * @param {String/HTMLElement/Element} el The context element
4292      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4293      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4294      * @return {HTMLElement/Roo.Element} The new node
4295      */
4296     append : function(el, o, returnElement){
4297         el = Roo.getDom(el);
4298         var newNode;
4299         if(this.useDom || o.ns){
4300             newNode = createDom(o, null);
4301             el.appendChild(newNode);
4302         }else{
4303             var html = createHtml(o);
4304             newNode = this.insertHtml("beforeEnd", el, html);
4305         }
4306         return returnElement ? Roo.get(newNode, true) : newNode;
4307     },
4308
4309     /**
4310      * Creates new Dom element(s) and overwrites the contents of el with them
4311      * @param {String/HTMLElement/Element} el The context element
4312      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4313      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4314      * @return {HTMLElement/Roo.Element} The new node
4315      */
4316     overwrite : function(el, o, returnElement){
4317         el = Roo.getDom(el);
4318         if (o.ns) {
4319           
4320             while (el.childNodes.length) {
4321                 el.removeChild(el.firstChild);
4322             }
4323             createDom(o, el);
4324         } else {
4325             el.innerHTML = createHtml(o);   
4326         }
4327         
4328         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4329     },
4330
4331     /**
4332      * Creates a new Roo.DomHelper.Template from the Dom object spec
4333      * @param {Object} o The Dom object spec (and children)
4334      * @return {Roo.DomHelper.Template} The new template
4335      */
4336     createTemplate : function(o){
4337         var html = createHtml(o);
4338         return new Roo.Template(html);
4339     }
4340     };
4341 }();
4342 /*
4343  * Based on:
4344  * Ext JS Library 1.1.1
4345  * Copyright(c) 2006-2007, Ext JS, LLC.
4346  *
4347  * Originally Released Under LGPL - original licence link has changed is not relivant.
4348  *
4349  * Fork - LGPL
4350  * <script type="text/javascript">
4351  */
4352  
4353 /**
4354 * @class Roo.Template
4355 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4356 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4357 * Usage:
4358 <pre><code>
4359 var t = new Roo.Template(
4360     '&lt;div name="{id}"&gt;',
4361         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4362     '&lt;/div&gt;'
4363 );
4364 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4365 </code></pre>
4366 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4367 * @constructor
4368 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4369 */
4370 Roo.Template = function(html){
4371     if(html instanceof Array){
4372         html = html.join("");
4373     }else if(arguments.length > 1){
4374         html = Array.prototype.join.call(arguments, "");
4375     }
4376     /**@private*/
4377     this.html = html;
4378     
4379 };
4380 Roo.Template.prototype = {
4381     /**
4382      * Returns an HTML fragment of this template with the specified values applied.
4383      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4384      * @return {String} The HTML fragment
4385      */
4386     applyTemplate : function(values){
4387         if(this.compiled){
4388             return this.compiled(values);
4389         }
4390         var useF = this.disableFormats !== true;
4391         var fm = Roo.util.Format, tpl = this;
4392         var fn = function(m, name, format, args){
4393             if(format && useF){
4394                 if(format.substr(0, 5) == "this."){
4395                     return tpl.call(format.substr(5), values[name], values);
4396                 }else{
4397                     if(args){
4398                         // quoted values are required for strings in compiled templates, 
4399                         // but for non compiled we need to strip them
4400                         // quoted reversed for jsmin
4401                         var re = /^\s*['"](.*)["']\s*$/;
4402                         args = args.split(',');
4403                         for(var i = 0, len = args.length; i < len; i++){
4404                             args[i] = args[i].replace(re, "$1");
4405                         }
4406                         args = [values[name]].concat(args);
4407                     }else{
4408                         args = [values[name]];
4409                     }
4410                     return fm[format].apply(fm, args);
4411                 }
4412             }else{
4413                 return values[name] !== undefined ? values[name] : "";
4414             }
4415         };
4416         return this.html.replace(this.re, fn);
4417     },
4418     
4419     /**
4420      * Sets the HTML used as the template and optionally compiles it.
4421      * @param {String} html
4422      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4423      * @return {Roo.Template} this
4424      */
4425     set : function(html, compile){
4426         this.html = html;
4427         this.compiled = null;
4428         if(compile){
4429             this.compile();
4430         }
4431         return this;
4432     },
4433     
4434     /**
4435      * True to disable format functions (defaults to false)
4436      * @type Boolean
4437      */
4438     disableFormats : false,
4439     
4440     /**
4441     * The regular expression used to match template variables 
4442     * @type RegExp
4443     * @property 
4444     */
4445     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4446     
4447     /**
4448      * Compiles the template into an internal function, eliminating the RegEx overhead.
4449      * @return {Roo.Template} this
4450      */
4451     compile : function(){
4452         var fm = Roo.util.Format;
4453         var useF = this.disableFormats !== true;
4454         var sep = Roo.isGecko ? "+" : ",";
4455         var fn = function(m, name, format, args){
4456             if(format && useF){
4457                 args = args ? ',' + args : "";
4458                 if(format.substr(0, 5) != "this."){
4459                     format = "fm." + format + '(';
4460                 }else{
4461                     format = 'this.call("'+ format.substr(5) + '", ';
4462                     args = ", values";
4463                 }
4464             }else{
4465                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4466             }
4467             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4468         };
4469         var body;
4470         // branched to use + in gecko and [].join() in others
4471         if(Roo.isGecko){
4472             body = "this.compiled = function(values){ return '" +
4473                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4474                     "';};";
4475         }else{
4476             body = ["this.compiled = function(values){ return ['"];
4477             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4478             body.push("'].join('');};");
4479             body = body.join('');
4480         }
4481         /**
4482          * eval:var:values
4483          * eval:var:fm
4484          */
4485         eval(body);
4486         return this;
4487     },
4488     
4489     // private function used to call members
4490     call : function(fnName, value, allValues){
4491         return this[fnName](value, allValues);
4492     },
4493     
4494     /**
4495      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4496      * @param {String/HTMLElement/Roo.Element} el The context element
4497      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4498      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4499      * @return {HTMLElement/Roo.Element} The new node or Element
4500      */
4501     insertFirst: function(el, values, returnElement){
4502         return this.doInsert('afterBegin', el, values, returnElement);
4503     },
4504
4505     /**
4506      * Applies the supplied values to the template and inserts the new node(s) before el.
4507      * @param {String/HTMLElement/Roo.Element} el The context element
4508      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4510      * @return {HTMLElement/Roo.Element} The new node or Element
4511      */
4512     insertBefore: function(el, values, returnElement){
4513         return this.doInsert('beforeBegin', el, values, returnElement);
4514     },
4515
4516     /**
4517      * Applies the supplied values to the template and inserts the new node(s) after el.
4518      * @param {String/HTMLElement/Roo.Element} el The context element
4519      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4520      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4521      * @return {HTMLElement/Roo.Element} The new node or Element
4522      */
4523     insertAfter : function(el, values, returnElement){
4524         return this.doInsert('afterEnd', el, values, returnElement);
4525     },
4526     
4527     /**
4528      * Applies the supplied values to the template and appends the new node(s) to el.
4529      * @param {String/HTMLElement/Roo.Element} el The context element
4530      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4531      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4532      * @return {HTMLElement/Roo.Element} The new node or Element
4533      */
4534     append : function(el, values, returnElement){
4535         return this.doInsert('beforeEnd', el, values, returnElement);
4536     },
4537
4538     doInsert : function(where, el, values, returnEl){
4539         el = Roo.getDom(el);
4540         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4541         return returnEl ? Roo.get(newNode, true) : newNode;
4542     },
4543
4544     /**
4545      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4546      * @param {String/HTMLElement/Roo.Element} el The context element
4547      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4548      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4549      * @return {HTMLElement/Roo.Element} The new node or Element
4550      */
4551     overwrite : function(el, values, returnElement){
4552         el = Roo.getDom(el);
4553         el.innerHTML = this.applyTemplate(values);
4554         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4555     }
4556 };
4557 /**
4558  * Alias for {@link #applyTemplate}
4559  * @method
4560  */
4561 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4562
4563 // backwards compat
4564 Roo.DomHelper.Template = Roo.Template;
4565
4566 /**
4567  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4568  * @param {String/HTMLElement} el A DOM element or its id
4569  * @returns {Roo.Template} The created template
4570  * @static
4571  */
4572 Roo.Template.from = function(el){
4573     el = Roo.getDom(el);
4574     return new Roo.Template(el.value || el.innerHTML);
4575 };/*
4576  * Based on:
4577  * Ext JS Library 1.1.1
4578  * Copyright(c) 2006-2007, Ext JS, LLC.
4579  *
4580  * Originally Released Under LGPL - original licence link has changed is not relivant.
4581  *
4582  * Fork - LGPL
4583  * <script type="text/javascript">
4584  */
4585  
4586
4587 /*
4588  * This is code is also distributed under MIT license for use
4589  * with jQuery and prototype JavaScript libraries.
4590  */
4591 /**
4592  * @class Roo.DomQuery
4593 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4594 <p>
4595 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4596
4597 <p>
4598 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4599 </p>
4600 <h4>Element Selectors:</h4>
4601 <ul class="list">
4602     <li> <b>*</b> any element</li>
4603     <li> <b>E</b> an element with the tag E</li>
4604     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4605     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4606     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4607     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4608 </ul>
4609 <h4>Attribute Selectors:</h4>
4610 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4611 <ul class="list">
4612     <li> <b>E[foo]</b> has an attribute "foo"</li>
4613     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4614     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4615     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4616     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4617     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4618     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4619 </ul>
4620 <h4>Pseudo Classes:</h4>
4621 <ul class="list">
4622     <li> <b>E:first-child</b> E is the first child of its parent</li>
4623     <li> <b>E:last-child</b> E is the last child of its parent</li>
4624     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4625     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4626     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4627     <li> <b>E:only-child</b> E is the only child of its parent</li>
4628     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4629     <li> <b>E:first</b> the first E in the resultset</li>
4630     <li> <b>E:last</b> the last E in the resultset</li>
4631     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4632     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4633     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4634     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4635     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4636     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4637     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4638     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4639     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4640 </ul>
4641 <h4>CSS Value Selectors:</h4>
4642 <ul class="list">
4643     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4644     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4645     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4646     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4647     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4648     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4649 </ul>
4650  * @singleton
4651  */
4652 Roo.DomQuery = function(){
4653     var cache = {}, simpleCache = {}, valueCache = {};
4654     var nonSpace = /\S/;
4655     var trimRe = /^\s+|\s+$/g;
4656     var tplRe = /\{(\d+)\}/g;
4657     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4658     var tagTokenRe = /^(#)?([\w-\*]+)/;
4659     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4660
4661     function child(p, index){
4662         var i = 0;
4663         var n = p.firstChild;
4664         while(n){
4665             if(n.nodeType == 1){
4666                if(++i == index){
4667                    return n;
4668                }
4669             }
4670             n = n.nextSibling;
4671         }
4672         return null;
4673     };
4674
4675     function next(n){
4676         while((n = n.nextSibling) && n.nodeType != 1);
4677         return n;
4678     };
4679
4680     function prev(n){
4681         while((n = n.previousSibling) && n.nodeType != 1);
4682         return n;
4683     };
4684
4685     function children(d){
4686         var n = d.firstChild, ni = -1;
4687             while(n){
4688                 var nx = n.nextSibling;
4689                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4690                     d.removeChild(n);
4691                 }else{
4692                     n.nodeIndex = ++ni;
4693                 }
4694                 n = nx;
4695             }
4696             return this;
4697         };
4698
4699     function byClassName(c, a, v){
4700         if(!v){
4701             return c;
4702         }
4703         var r = [], ri = -1, cn;
4704         for(var i = 0, ci; ci = c[i]; i++){
4705             if((' '+ci.className+' ').indexOf(v) != -1){
4706                 r[++ri] = ci;
4707             }
4708         }
4709         return r;
4710     };
4711
4712     function attrValue(n, attr){
4713         if(!n.tagName && typeof n.length != "undefined"){
4714             n = n[0];
4715         }
4716         if(!n){
4717             return null;
4718         }
4719         if(attr == "for"){
4720             return n.htmlFor;
4721         }
4722         if(attr == "class" || attr == "className"){
4723             return n.className;
4724         }
4725         return n.getAttribute(attr) || n[attr];
4726
4727     };
4728
4729     function getNodes(ns, mode, tagName){
4730         var result = [], ri = -1, cs;
4731         if(!ns){
4732             return result;
4733         }
4734         tagName = tagName || "*";
4735         if(typeof ns.getElementsByTagName != "undefined"){
4736             ns = [ns];
4737         }
4738         if(!mode){
4739             for(var i = 0, ni; ni = ns[i]; i++){
4740                 cs = ni.getElementsByTagName(tagName);
4741                 for(var j = 0, ci; ci = cs[j]; j++){
4742                     result[++ri] = ci;
4743                 }
4744             }
4745         }else if(mode == "/" || mode == ">"){
4746             var utag = tagName.toUpperCase();
4747             for(var i = 0, ni, cn; ni = ns[i]; i++){
4748                 cn = ni.children || ni.childNodes;
4749                 for(var j = 0, cj; cj = cn[j]; j++){
4750                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4751                         result[++ri] = cj;
4752                     }
4753                 }
4754             }
4755         }else if(mode == "+"){
4756             var utag = tagName.toUpperCase();
4757             for(var i = 0, n; n = ns[i]; i++){
4758                 while((n = n.nextSibling) && n.nodeType != 1);
4759                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4760                     result[++ri] = n;
4761                 }
4762             }
4763         }else if(mode == "~"){
4764             for(var i = 0, n; n = ns[i]; i++){
4765                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4766                 if(n){
4767                     result[++ri] = n;
4768                 }
4769             }
4770         }
4771         return result;
4772     };
4773
4774     function concat(a, b){
4775         if(b.slice){
4776             return a.concat(b);
4777         }
4778         for(var i = 0, l = b.length; i < l; i++){
4779             a[a.length] = b[i];
4780         }
4781         return a;
4782     }
4783
4784     function byTag(cs, tagName){
4785         if(cs.tagName || cs == document){
4786             cs = [cs];
4787         }
4788         if(!tagName){
4789             return cs;
4790         }
4791         var r = [], ri = -1;
4792         tagName = tagName.toLowerCase();
4793         for(var i = 0, ci; ci = cs[i]; i++){
4794             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4795                 r[++ri] = ci;
4796             }
4797         }
4798         return r;
4799     };
4800
4801     function byId(cs, attr, id){
4802         if(cs.tagName || cs == document){
4803             cs = [cs];
4804         }
4805         if(!id){
4806             return cs;
4807         }
4808         var r = [], ri = -1;
4809         for(var i = 0,ci; ci = cs[i]; i++){
4810             if(ci && ci.id == id){
4811                 r[++ri] = ci;
4812                 return r;
4813             }
4814         }
4815         return r;
4816     };
4817
4818     function byAttribute(cs, attr, value, op, custom){
4819         var r = [], ri = -1, st = custom=="{";
4820         var f = Roo.DomQuery.operators[op];
4821         for(var i = 0, ci; ci = cs[i]; i++){
4822             var a;
4823             if(st){
4824                 a = Roo.DomQuery.getStyle(ci, attr);
4825             }
4826             else if(attr == "class" || attr == "className"){
4827                 a = ci.className;
4828             }else if(attr == "for"){
4829                 a = ci.htmlFor;
4830             }else if(attr == "href"){
4831                 a = ci.getAttribute("href", 2);
4832             }else{
4833                 a = ci.getAttribute(attr);
4834             }
4835             if((f && f(a, value)) || (!f && a)){
4836                 r[++ri] = ci;
4837             }
4838         }
4839         return r;
4840     };
4841
4842     function byPseudo(cs, name, value){
4843         return Roo.DomQuery.pseudos[name](cs, value);
4844     };
4845
4846     // This is for IE MSXML which does not support expandos.
4847     // IE runs the same speed using setAttribute, however FF slows way down
4848     // and Safari completely fails so they need to continue to use expandos.
4849     var isIE = window.ActiveXObject ? true : false;
4850
4851     // this eval is stop the compressor from
4852     // renaming the variable to something shorter
4853     
4854     /** eval:var:batch */
4855     var batch = 30803; 
4856
4857     var key = 30803;
4858
4859     function nodupIEXml(cs){
4860         var d = ++key;
4861         cs[0].setAttribute("_nodup", d);
4862         var r = [cs[0]];
4863         for(var i = 1, len = cs.length; i < len; i++){
4864             var c = cs[i];
4865             if(!c.getAttribute("_nodup") != d){
4866                 c.setAttribute("_nodup", d);
4867                 r[r.length] = c;
4868             }
4869         }
4870         for(var i = 0, len = cs.length; i < len; i++){
4871             cs[i].removeAttribute("_nodup");
4872         }
4873         return r;
4874     }
4875
4876     function nodup(cs){
4877         if(!cs){
4878             return [];
4879         }
4880         var len = cs.length, c, i, r = cs, cj, ri = -1;
4881         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4882             return cs;
4883         }
4884         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4885             return nodupIEXml(cs);
4886         }
4887         var d = ++key;
4888         cs[0]._nodup = d;
4889         for(i = 1; c = cs[i]; i++){
4890             if(c._nodup != d){
4891                 c._nodup = d;
4892             }else{
4893                 r = [];
4894                 for(var j = 0; j < i; j++){
4895                     r[++ri] = cs[j];
4896                 }
4897                 for(j = i+1; cj = cs[j]; j++){
4898                     if(cj._nodup != d){
4899                         cj._nodup = d;
4900                         r[++ri] = cj;
4901                     }
4902                 }
4903                 return r;
4904             }
4905         }
4906         return r;
4907     }
4908
4909     function quickDiffIEXml(c1, c2){
4910         var d = ++key;
4911         for(var i = 0, len = c1.length; i < len; i++){
4912             c1[i].setAttribute("_qdiff", d);
4913         }
4914         var r = [];
4915         for(var i = 0, len = c2.length; i < len; i++){
4916             if(c2[i].getAttribute("_qdiff") != d){
4917                 r[r.length] = c2[i];
4918             }
4919         }
4920         for(var i = 0, len = c1.length; i < len; i++){
4921            c1[i].removeAttribute("_qdiff");
4922         }
4923         return r;
4924     }
4925
4926     function quickDiff(c1, c2){
4927         var len1 = c1.length;
4928         if(!len1){
4929             return c2;
4930         }
4931         if(isIE && c1[0].selectSingleNode){
4932             return quickDiffIEXml(c1, c2);
4933         }
4934         var d = ++key;
4935         for(var i = 0; i < len1; i++){
4936             c1[i]._qdiff = d;
4937         }
4938         var r = [];
4939         for(var i = 0, len = c2.length; i < len; i++){
4940             if(c2[i]._qdiff != d){
4941                 r[r.length] = c2[i];
4942             }
4943         }
4944         return r;
4945     }
4946
4947     function quickId(ns, mode, root, id){
4948         if(ns == root){
4949            var d = root.ownerDocument || root;
4950            return d.getElementById(id);
4951         }
4952         ns = getNodes(ns, mode, "*");
4953         return byId(ns, null, id);
4954     }
4955
4956     return {
4957         getStyle : function(el, name){
4958             return Roo.fly(el).getStyle(name);
4959         },
4960         /**
4961          * Compiles a selector/xpath query into a reusable function. The returned function
4962          * takes one parameter "root" (optional), which is the context node from where the query should start.
4963          * @param {String} selector The selector/xpath query
4964          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4965          * @return {Function}
4966          */
4967         compile : function(path, type){
4968             type = type || "select";
4969             
4970             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4971             var q = path, mode, lq;
4972             var tk = Roo.DomQuery.matchers;
4973             var tklen = tk.length;
4974             var mm;
4975
4976             // accept leading mode switch
4977             var lmode = q.match(modeRe);
4978             if(lmode && lmode[1]){
4979                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4980                 q = q.replace(lmode[1], "");
4981             }
4982             // strip leading slashes
4983             while(path.substr(0, 1)=="/"){
4984                 path = path.substr(1);
4985             }
4986
4987             while(q && lq != q){
4988                 lq = q;
4989                 var tm = q.match(tagTokenRe);
4990                 if(type == "select"){
4991                     if(tm){
4992                         if(tm[1] == "#"){
4993                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4994                         }else{
4995                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4996                         }
4997                         q = q.replace(tm[0], "");
4998                     }else if(q.substr(0, 1) != '@'){
4999                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5000                     }
5001                 }else{
5002                     if(tm){
5003                         if(tm[1] == "#"){
5004                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5005                         }else{
5006                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5007                         }
5008                         q = q.replace(tm[0], "");
5009                     }
5010                 }
5011                 while(!(mm = q.match(modeRe))){
5012                     var matched = false;
5013                     for(var j = 0; j < tklen; j++){
5014                         var t = tk[j];
5015                         var m = q.match(t.re);
5016                         if(m){
5017                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5018                                                     return m[i];
5019                                                 });
5020                             q = q.replace(m[0], "");
5021                             matched = true;
5022                             break;
5023                         }
5024                     }
5025                     // prevent infinite loop on bad selector
5026                     if(!matched){
5027                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5028                     }
5029                 }
5030                 if(mm[1]){
5031                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5032                     q = q.replace(mm[1], "");
5033                 }
5034             }
5035             fn[fn.length] = "return nodup(n);\n}";
5036             
5037              /** 
5038               * list of variables that need from compression as they are used by eval.
5039              *  eval:var:batch 
5040              *  eval:var:nodup
5041              *  eval:var:byTag
5042              *  eval:var:ById
5043              *  eval:var:getNodes
5044              *  eval:var:quickId
5045              *  eval:var:mode
5046              *  eval:var:root
5047              *  eval:var:n
5048              *  eval:var:byClassName
5049              *  eval:var:byPseudo
5050              *  eval:var:byAttribute
5051              *  eval:var:attrValue
5052              * 
5053              **/ 
5054             eval(fn.join(""));
5055             return f;
5056         },
5057
5058         /**
5059          * Selects a group of elements.
5060          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5061          * @param {Node} root (optional) The start of the query (defaults to document).
5062          * @return {Array}
5063          */
5064         select : function(path, root, type){
5065             if(!root || root == document){
5066                 root = document;
5067             }
5068             if(typeof root == "string"){
5069                 root = document.getElementById(root);
5070             }
5071             var paths = path.split(",");
5072             var results = [];
5073             for(var i = 0, len = paths.length; i < len; i++){
5074                 var p = paths[i].replace(trimRe, "");
5075                 if(!cache[p]){
5076                     cache[p] = Roo.DomQuery.compile(p);
5077                     if(!cache[p]){
5078                         throw p + " is not a valid selector";
5079                     }
5080                 }
5081                 var result = cache[p](root);
5082                 if(result && result != document){
5083                     results = results.concat(result);
5084                 }
5085             }
5086             if(paths.length > 1){
5087                 return nodup(results);
5088             }
5089             return results;
5090         },
5091
5092         /**
5093          * Selects a single element.
5094          * @param {String} selector The selector/xpath query
5095          * @param {Node} root (optional) The start of the query (defaults to document).
5096          * @return {Element}
5097          */
5098         selectNode : function(path, root){
5099             return Roo.DomQuery.select(path, root)[0];
5100         },
5101
5102         /**
5103          * Selects the value of a node, optionally replacing null with the defaultValue.
5104          * @param {String} selector The selector/xpath query
5105          * @param {Node} root (optional) The start of the query (defaults to document).
5106          * @param {String} defaultValue
5107          */
5108         selectValue : function(path, root, defaultValue){
5109             path = path.replace(trimRe, "");
5110             if(!valueCache[path]){
5111                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5112             }
5113             var n = valueCache[path](root);
5114             n = n[0] ? n[0] : n;
5115             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5116             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5117         },
5118
5119         /**
5120          * Selects the value of a node, parsing integers and floats.
5121          * @param {String} selector The selector/xpath query
5122          * @param {Node} root (optional) The start of the query (defaults to document).
5123          * @param {Number} defaultValue
5124          * @return {Number}
5125          */
5126         selectNumber : function(path, root, defaultValue){
5127             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5128             return parseFloat(v);
5129         },
5130
5131         /**
5132          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5133          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5134          * @param {String} selector The simple selector to test
5135          * @return {Boolean}
5136          */
5137         is : function(el, ss){
5138             if(typeof el == "string"){
5139                 el = document.getElementById(el);
5140             }
5141             var isArray = (el instanceof Array);
5142             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5143             return isArray ? (result.length == el.length) : (result.length > 0);
5144         },
5145
5146         /**
5147          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5148          * @param {Array} el An array of elements to filter
5149          * @param {String} selector The simple selector to test
5150          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5151          * the selector instead of the ones that match
5152          * @return {Array}
5153          */
5154         filter : function(els, ss, nonMatches){
5155             ss = ss.replace(trimRe, "");
5156             if(!simpleCache[ss]){
5157                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5158             }
5159             var result = simpleCache[ss](els);
5160             return nonMatches ? quickDiff(result, els) : result;
5161         },
5162
5163         /**
5164          * Collection of matching regular expressions and code snippets.
5165          */
5166         matchers : [{
5167                 re: /^\.([\w-]+)/,
5168                 select: 'n = byClassName(n, null, " {1} ");'
5169             }, {
5170                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5171                 select: 'n = byPseudo(n, "{1}", "{2}");'
5172             },{
5173                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5174                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5175             }, {
5176                 re: /^#([\w-]+)/,
5177                 select: 'n = byId(n, null, "{1}");'
5178             },{
5179                 re: /^@([\w-]+)/,
5180                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5181             }
5182         ],
5183
5184         /**
5185          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5186          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5187          */
5188         operators : {
5189             "=" : function(a, v){
5190                 return a == v;
5191             },
5192             "!=" : function(a, v){
5193                 return a != v;
5194             },
5195             "^=" : function(a, v){
5196                 return a && a.substr(0, v.length) == v;
5197             },
5198             "$=" : function(a, v){
5199                 return a && a.substr(a.length-v.length) == v;
5200             },
5201             "*=" : function(a, v){
5202                 return a && a.indexOf(v) !== -1;
5203             },
5204             "%=" : function(a, v){
5205                 return (a % v) == 0;
5206             },
5207             "|=" : function(a, v){
5208                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5209             },
5210             "~=" : function(a, v){
5211                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5212             }
5213         },
5214
5215         /**
5216          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5217          * and the argument (if any) supplied in the selector.
5218          */
5219         pseudos : {
5220             "first-child" : function(c){
5221                 var r = [], ri = -1, n;
5222                 for(var i = 0, ci; ci = n = c[i]; i++){
5223                     while((n = n.previousSibling) && n.nodeType != 1);
5224                     if(!n){
5225                         r[++ri] = ci;
5226                     }
5227                 }
5228                 return r;
5229             },
5230
5231             "last-child" : function(c){
5232                 var r = [], ri = -1, n;
5233                 for(var i = 0, ci; ci = n = c[i]; i++){
5234                     while((n = n.nextSibling) && n.nodeType != 1);
5235                     if(!n){
5236                         r[++ri] = ci;
5237                     }
5238                 }
5239                 return r;
5240             },
5241
5242             "nth-child" : function(c, a) {
5243                 var r = [], ri = -1;
5244                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5245                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5246                 for(var i = 0, n; n = c[i]; i++){
5247                     var pn = n.parentNode;
5248                     if (batch != pn._batch) {
5249                         var j = 0;
5250                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5251                             if(cn.nodeType == 1){
5252                                cn.nodeIndex = ++j;
5253                             }
5254                         }
5255                         pn._batch = batch;
5256                     }
5257                     if (f == 1) {
5258                         if (l == 0 || n.nodeIndex == l){
5259                             r[++ri] = n;
5260                         }
5261                     } else if ((n.nodeIndex + l) % f == 0){
5262                         r[++ri] = n;
5263                     }
5264                 }
5265
5266                 return r;
5267             },
5268
5269             "only-child" : function(c){
5270                 var r = [], ri = -1;;
5271                 for(var i = 0, ci; ci = c[i]; i++){
5272                     if(!prev(ci) && !next(ci)){
5273                         r[++ri] = ci;
5274                     }
5275                 }
5276                 return r;
5277             },
5278
5279             "empty" : function(c){
5280                 var r = [], ri = -1;
5281                 for(var i = 0, ci; ci = c[i]; i++){
5282                     var cns = ci.childNodes, j = 0, cn, empty = true;
5283                     while(cn = cns[j]){
5284                         ++j;
5285                         if(cn.nodeType == 1 || cn.nodeType == 3){
5286                             empty = false;
5287                             break;
5288                         }
5289                     }
5290                     if(empty){
5291                         r[++ri] = ci;
5292                     }
5293                 }
5294                 return r;
5295             },
5296
5297             "contains" : function(c, v){
5298                 var r = [], ri = -1;
5299                 for(var i = 0, ci; ci = c[i]; i++){
5300                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5301                         r[++ri] = ci;
5302                     }
5303                 }
5304                 return r;
5305             },
5306
5307             "nodeValue" : function(c, v){
5308                 var r = [], ri = -1;
5309                 for(var i = 0, ci; ci = c[i]; i++){
5310                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5311                         r[++ri] = ci;
5312                     }
5313                 }
5314                 return r;
5315             },
5316
5317             "checked" : function(c){
5318                 var r = [], ri = -1;
5319                 for(var i = 0, ci; ci = c[i]; i++){
5320                     if(ci.checked == true){
5321                         r[++ri] = ci;
5322                     }
5323                 }
5324                 return r;
5325             },
5326
5327             "not" : function(c, ss){
5328                 return Roo.DomQuery.filter(c, ss, true);
5329             },
5330
5331             "odd" : function(c){
5332                 return this["nth-child"](c, "odd");
5333             },
5334
5335             "even" : function(c){
5336                 return this["nth-child"](c, "even");
5337             },
5338
5339             "nth" : function(c, a){
5340                 return c[a-1] || [];
5341             },
5342
5343             "first" : function(c){
5344                 return c[0] || [];
5345             },
5346
5347             "last" : function(c){
5348                 return c[c.length-1] || [];
5349             },
5350
5351             "has" : function(c, ss){
5352                 var s = Roo.DomQuery.select;
5353                 var r = [], ri = -1;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(s(ss, ci).length > 0){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "next" : function(c, ss){
5363                 var is = Roo.DomQuery.is;
5364                 var r = [], ri = -1;
5365                 for(var i = 0, ci; ci = c[i]; i++){
5366                     var n = next(ci);
5367                     if(n && is(n, ss)){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "prev" : function(c, ss){
5375                 var is = Roo.DomQuery.is;
5376                 var r = [], ri = -1;
5377                 for(var i = 0, ci; ci = c[i]; i++){
5378                     var n = prev(ci);
5379                     if(n && is(n, ss)){
5380                         r[++ri] = ci;
5381                     }
5382                 }
5383                 return r;
5384             }
5385         }
5386     };
5387 }();
5388
5389 /**
5390  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5391  * @param {String} path The selector/xpath query
5392  * @param {Node} root (optional) The start of the query (defaults to document).
5393  * @return {Array}
5394  * @member Roo
5395  * @method query
5396  */
5397 Roo.query = Roo.DomQuery.select;
5398 /*
5399  * Based on:
5400  * Ext JS Library 1.1.1
5401  * Copyright(c) 2006-2007, Ext JS, LLC.
5402  *
5403  * Originally Released Under LGPL - original licence link has changed is not relivant.
5404  *
5405  * Fork - LGPL
5406  * <script type="text/javascript">
5407  */
5408
5409 /**
5410  * @class Roo.util.Observable
5411  * Base class that provides a common interface for publishing events. Subclasses are expected to
5412  * to have a property "events" with all the events defined.<br>
5413  * For example:
5414  * <pre><code>
5415  Employee = function(name){
5416     this.name = name;
5417     this.addEvents({
5418         "fired" : true,
5419         "quit" : true
5420     });
5421  }
5422  Roo.extend(Employee, Roo.util.Observable);
5423 </code></pre>
5424  * @param {Object} config properties to use (incuding events / listeners)
5425  */
5426
5427 Roo.util.Observable = function(cfg){
5428     
5429     cfg = cfg|| {};
5430     this.addEvents(cfg.events || {});
5431     if (cfg.events) {
5432         delete cfg.events; // make sure
5433     }
5434      
5435     Roo.apply(this, cfg);
5436     
5437     if(this.listeners){
5438         this.on(this.listeners);
5439         delete this.listeners;
5440     }
5441 };
5442 Roo.util.Observable.prototype = {
5443     /** 
5444  * @cfg {Object} listeners  list of events and functions to call for this object, 
5445  * For example :
5446  * <pre><code>
5447     listeners :  { 
5448        'click' : function(e) {
5449            ..... 
5450         } ,
5451         .... 
5452     } 
5453   </code></pre>
5454  */
5455     
5456     
5457     /**
5458      * Fires the specified event with the passed parameters (minus the event name).
5459      * @param {String} eventName
5460      * @param {Object...} args Variable number of parameters are passed to handlers
5461      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5462      */
5463     fireEvent : function(){
5464         var ce = this.events[arguments[0].toLowerCase()];
5465         if(typeof ce == "object"){
5466             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5467         }else{
5468             return true;
5469         }
5470     },
5471
5472     // private
5473     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5474
5475     /**
5476      * Appends an event handler to this component
5477      * @param {String}   eventName The type of event to listen for
5478      * @param {Function} handler The method the event invokes
5479      * @param {Object}   scope (optional) The scope in which to execute the handler
5480      * function. The handler function's "this" context.
5481      * @param {Object}   options (optional) An object containing handler configuration
5482      * properties. This may contain any of the following properties:<ul>
5483      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5484      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5485      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5486      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5487      * by the specified number of milliseconds. If the event fires again within that time, the original
5488      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5489      * </ul><br>
5490      * <p>
5491      * <b>Combining Options</b><br>
5492      * Using the options argument, it is possible to combine different types of listeners:<br>
5493      * <br>
5494      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5495                 <pre><code>
5496                 el.on('click', this.onClick, this, {
5497                         single: true,
5498                 delay: 100,
5499                 forumId: 4
5500                 });
5501                 </code></pre>
5502      * <p>
5503      * <b>Attaching multiple handlers in 1 call</b><br>
5504      * The method also allows for a single argument to be passed which is a config object containing properties
5505      * which specify multiple handlers.
5506      * <pre><code>
5507                 el.on({
5508                         'click': {
5509                         fn: this.onClick,
5510                         scope: this,
5511                         delay: 100
5512                 }, 
5513                 'mouseover': {
5514                         fn: this.onMouseOver,
5515                         scope: this
5516                 },
5517                 'mouseout': {
5518                         fn: this.onMouseOut,
5519                         scope: this
5520                 }
5521                 });
5522                 </code></pre>
5523      * <p>
5524      * Or a shorthand syntax which passes the same scope object to all handlers:
5525         <pre><code>
5526                 el.on({
5527                         'click': this.onClick,
5528                 'mouseover': this.onMouseOver,
5529                 'mouseout': this.onMouseOut,
5530                 scope: this
5531                 });
5532                 </code></pre>
5533      */
5534     addListener : function(eventName, fn, scope, o){
5535         if(typeof eventName == "object"){
5536             o = eventName;
5537             for(var e in o){
5538                 if(this.filterOptRe.test(e)){
5539                     continue;
5540                 }
5541                 if(typeof o[e] == "function"){
5542                     // shared options
5543                     this.addListener(e, o[e], o.scope,  o);
5544                 }else{
5545                     // individual options
5546                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5547                 }
5548             }
5549             return;
5550         }
5551         o = (!o || typeof o == "boolean") ? {} : o;
5552         eventName = eventName.toLowerCase();
5553         var ce = this.events[eventName] || true;
5554         if(typeof ce == "boolean"){
5555             ce = new Roo.util.Event(this, eventName);
5556             this.events[eventName] = ce;
5557         }
5558         ce.addListener(fn, scope, o);
5559     },
5560
5561     /**
5562      * Removes a listener
5563      * @param {String}   eventName     The type of event to listen for
5564      * @param {Function} handler        The handler to remove
5565      * @param {Object}   scope  (optional) The scope (this object) for the handler
5566      */
5567     removeListener : function(eventName, fn, scope){
5568         var ce = this.events[eventName.toLowerCase()];
5569         if(typeof ce == "object"){
5570             ce.removeListener(fn, scope);
5571         }
5572     },
5573
5574     /**
5575      * Removes all listeners for this object
5576      */
5577     purgeListeners : function(){
5578         for(var evt in this.events){
5579             if(typeof this.events[evt] == "object"){
5580                  this.events[evt].clearListeners();
5581             }
5582         }
5583     },
5584
5585     relayEvents : function(o, events){
5586         var createHandler = function(ename){
5587             return function(){
5588                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5589             };
5590         };
5591         for(var i = 0, len = events.length; i < len; i++){
5592             var ename = events[i];
5593             if(!this.events[ename]){ this.events[ename] = true; };
5594             o.on(ename, createHandler(ename), this);
5595         }
5596     },
5597
5598     /**
5599      * Used to define events on this Observable
5600      * @param {Object} object The object with the events defined
5601      */
5602     addEvents : function(o){
5603         if(!this.events){
5604             this.events = {};
5605         }
5606         Roo.applyIf(this.events, o);
5607     },
5608
5609     /**
5610      * Checks to see if this object has any listeners for a specified event
5611      * @param {String} eventName The name of the event to check for
5612      * @return {Boolean} True if the event is being listened for, else false
5613      */
5614     hasListener : function(eventName){
5615         var e = this.events[eventName];
5616         return typeof e == "object" && e.listeners.length > 0;
5617     }
5618 };
5619 /**
5620  * Appends an event handler to this element (shorthand for addListener)
5621  * @param {String}   eventName     The type of event to listen for
5622  * @param {Function} handler        The method the event invokes
5623  * @param {Object}   scope (optional) The scope in which to execute the handler
5624  * function. The handler function's "this" context.
5625  * @param {Object}   options  (optional)
5626  * @method
5627  */
5628 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5629 /**
5630  * Removes a listener (shorthand for removeListener)
5631  * @param {String}   eventName     The type of event to listen for
5632  * @param {Function} handler        The handler to remove
5633  * @param {Object}   scope  (optional) The scope (this object) for the handler
5634  * @method
5635  */
5636 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5637
5638 /**
5639  * Starts capture on the specified Observable. All events will be passed
5640  * to the supplied function with the event name + standard signature of the event
5641  * <b>before</b> the event is fired. If the supplied function returns false,
5642  * the event will not fire.
5643  * @param {Observable} o The Observable to capture
5644  * @param {Function} fn The function to call
5645  * @param {Object} scope (optional) The scope (this object) for the fn
5646  * @static
5647  */
5648 Roo.util.Observable.capture = function(o, fn, scope){
5649     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5650 };
5651
5652 /**
5653  * Removes <b>all</b> added captures from the Observable.
5654  * @param {Observable} o The Observable to release
5655  * @static
5656  */
5657 Roo.util.Observable.releaseCapture = function(o){
5658     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5659 };
5660
5661 (function(){
5662
5663     var createBuffered = function(h, o, scope){
5664         var task = new Roo.util.DelayedTask();
5665         return function(){
5666             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5667         };
5668     };
5669
5670     var createSingle = function(h, e, fn, scope){
5671         return function(){
5672             e.removeListener(fn, scope);
5673             return h.apply(scope, arguments);
5674         };
5675     };
5676
5677     var createDelayed = function(h, o, scope){
5678         return function(){
5679             var args = Array.prototype.slice.call(arguments, 0);
5680             setTimeout(function(){
5681                 h.apply(scope, args);
5682             }, o.delay || 10);
5683         };
5684     };
5685
5686     Roo.util.Event = function(obj, name){
5687         this.name = name;
5688         this.obj = obj;
5689         this.listeners = [];
5690     };
5691
5692     Roo.util.Event.prototype = {
5693         addListener : function(fn, scope, options){
5694             var o = options || {};
5695             scope = scope || this.obj;
5696             if(!this.isListening(fn, scope)){
5697                 var l = {fn: fn, scope: scope, options: o};
5698                 var h = fn;
5699                 if(o.delay){
5700                     h = createDelayed(h, o, scope);
5701                 }
5702                 if(o.single){
5703                     h = createSingle(h, this, fn, scope);
5704                 }
5705                 if(o.buffer){
5706                     h = createBuffered(h, o, scope);
5707                 }
5708                 l.fireFn = h;
5709                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5710                     this.listeners.push(l);
5711                 }else{
5712                     this.listeners = this.listeners.slice(0);
5713                     this.listeners.push(l);
5714                 }
5715             }
5716         },
5717
5718         findListener : function(fn, scope){
5719             scope = scope || this.obj;
5720             var ls = this.listeners;
5721             for(var i = 0, len = ls.length; i < len; i++){
5722                 var l = ls[i];
5723                 if(l.fn == fn && l.scope == scope){
5724                     return i;
5725                 }
5726             }
5727             return -1;
5728         },
5729
5730         isListening : function(fn, scope){
5731             return this.findListener(fn, scope) != -1;
5732         },
5733
5734         removeListener : function(fn, scope){
5735             var index;
5736             if((index = this.findListener(fn, scope)) != -1){
5737                 if(!this.firing){
5738                     this.listeners.splice(index, 1);
5739                 }else{
5740                     this.listeners = this.listeners.slice(0);
5741                     this.listeners.splice(index, 1);
5742                 }
5743                 return true;
5744             }
5745             return false;
5746         },
5747
5748         clearListeners : function(){
5749             this.listeners = [];
5750         },
5751
5752         fire : function(){
5753             var ls = this.listeners, scope, len = ls.length;
5754             if(len > 0){
5755                 this.firing = true;
5756                 var args = Array.prototype.slice.call(arguments, 0);
5757                 for(var i = 0; i < len; i++){
5758                     var l = ls[i];
5759                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5760                         this.firing = false;
5761                         return false;
5762                     }
5763                 }
5764                 this.firing = false;
5765             }
5766             return true;
5767         }
5768     };
5769 })();/*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779
5780 /**
5781  * @class Roo.EventManager
5782  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5783  * several useful events directly.
5784  * See {@link Roo.EventObject} for more details on normalized event objects.
5785  * @singleton
5786  */
5787 Roo.EventManager = function(){
5788     var docReadyEvent, docReadyProcId, docReadyState = false;
5789     var resizeEvent, resizeTask, textEvent, textSize;
5790     var E = Roo.lib.Event;
5791     var D = Roo.lib.Dom;
5792
5793
5794     var fireDocReady = function(){
5795         if(!docReadyState){
5796             docReadyState = true;
5797             Roo.isReady = true;
5798             if(docReadyProcId){
5799                 clearInterval(docReadyProcId);
5800             }
5801             if(Roo.isGecko || Roo.isOpera) {
5802                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5803             }
5804             if(Roo.isIE){
5805                 var defer = document.getElementById("ie-deferred-loader");
5806                 if(defer){
5807                     defer.onreadystatechange = null;
5808                     defer.parentNode.removeChild(defer);
5809                 }
5810             }
5811             if(docReadyEvent){
5812                 docReadyEvent.fire();
5813                 docReadyEvent.clearListeners();
5814             }
5815         }
5816     };
5817     
5818     var initDocReady = function(){
5819         docReadyEvent = new Roo.util.Event();
5820         if(Roo.isGecko || Roo.isOpera) {
5821             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5822         }else if(Roo.isIE){
5823             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5824             var defer = document.getElementById("ie-deferred-loader");
5825             defer.onreadystatechange = function(){
5826                 if(this.readyState == "complete"){
5827                     fireDocReady();
5828                 }
5829             };
5830         }else if(Roo.isSafari){ 
5831             docReadyProcId = setInterval(function(){
5832                 var rs = document.readyState;
5833                 if(rs == "complete") {
5834                     fireDocReady();     
5835                  }
5836             }, 10);
5837         }
5838         // no matter what, make sure it fires on load
5839         E.on(window, "load", fireDocReady);
5840     };
5841
5842     var createBuffered = function(h, o){
5843         var task = new Roo.util.DelayedTask(h);
5844         return function(e){
5845             // create new event object impl so new events don't wipe out properties
5846             e = new Roo.EventObjectImpl(e);
5847             task.delay(o.buffer, h, null, [e]);
5848         };
5849     };
5850
5851     var createSingle = function(h, el, ename, fn){
5852         return function(e){
5853             Roo.EventManager.removeListener(el, ename, fn);
5854             h(e);
5855         };
5856     };
5857
5858     var createDelayed = function(h, o){
5859         return function(e){
5860             // create new event object impl so new events don't wipe out properties
5861             e = new Roo.EventObjectImpl(e);
5862             setTimeout(function(){
5863                 h(e);
5864             }, o.delay || 10);
5865         };
5866     };
5867
5868     var listen = function(element, ename, opt, fn, scope){
5869         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5870         fn = fn || o.fn; scope = scope || o.scope;
5871         var el = Roo.getDom(element);
5872         if(!el){
5873             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5874         }
5875         var h = function(e){
5876             e = Roo.EventObject.setEvent(e);
5877             var t;
5878             if(o.delegate){
5879                 t = e.getTarget(o.delegate, el);
5880                 if(!t){
5881                     return;
5882                 }
5883             }else{
5884                 t = e.target;
5885             }
5886             if(o.stopEvent === true){
5887                 e.stopEvent();
5888             }
5889             if(o.preventDefault === true){
5890                e.preventDefault();
5891             }
5892             if(o.stopPropagation === true){
5893                 e.stopPropagation();
5894             }
5895
5896             if(o.normalized === false){
5897                 e = e.browserEvent;
5898             }
5899
5900             fn.call(scope || el, e, t, o);
5901         };
5902         if(o.delay){
5903             h = createDelayed(h, o);
5904         }
5905         if(o.single){
5906             h = createSingle(h, el, ename, fn);
5907         }
5908         if(o.buffer){
5909             h = createBuffered(h, o);
5910         }
5911         fn._handlers = fn._handlers || [];
5912         fn._handlers.push([Roo.id(el), ename, h]);
5913
5914         E.on(el, ename, h);
5915         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5916             el.addEventListener("DOMMouseScroll", h, false);
5917             E.on(window, 'unload', function(){
5918                 el.removeEventListener("DOMMouseScroll", h, false);
5919             });
5920         }
5921         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5922             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5923         }
5924         return h;
5925     };
5926
5927     var stopListening = function(el, ename, fn){
5928         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5929         if(hds){
5930             for(var i = 0, len = hds.length; i < len; i++){
5931                 var h = hds[i];
5932                 if(h[0] == id && h[1] == ename){
5933                     hd = h[2];
5934                     hds.splice(i, 1);
5935                     break;
5936                 }
5937             }
5938         }
5939         E.un(el, ename, hd);
5940         el = Roo.getDom(el);
5941         if(ename == "mousewheel" && el.addEventListener){
5942             el.removeEventListener("DOMMouseScroll", hd, false);
5943         }
5944         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5945             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5946         }
5947     };
5948
5949     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5950     
5951     var pub = {
5952         
5953         
5954         /** 
5955          * Fix for doc tools
5956          * @scope Roo.EventManager
5957          */
5958         
5959         
5960         /** 
5961          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5962          * object with a Roo.EventObject
5963          * @param {Function} fn        The method the event invokes
5964          * @param {Object}   scope    An object that becomes the scope of the handler
5965          * @param {boolean}  override If true, the obj passed in becomes
5966          *                             the execution scope of the listener
5967          * @return {Function} The wrapped function
5968          * @deprecated
5969          */
5970         wrap : function(fn, scope, override){
5971             return function(e){
5972                 Roo.EventObject.setEvent(e);
5973                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5974             };
5975         },
5976         
5977         /**
5978      * Appends an event handler to an element (shorthand for addListener)
5979      * @param {String/HTMLElement}   element        The html element or id to assign the
5980      * @param {String}   eventName The type of event to listen for
5981      * @param {Function} handler The method the event invokes
5982      * @param {Object}   scope (optional) The scope in which to execute the handler
5983      * function. The handler function's "this" context.
5984      * @param {Object}   options (optional) An object containing handler configuration
5985      * properties. This may contain any of the following properties:<ul>
5986      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5987      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5988      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5989      * <li>preventDefault {Boolean} True to prevent the default action</li>
5990      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5991      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5992      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5993      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5994      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5995      * by the specified number of milliseconds. If the event fires again within that time, the original
5996      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5997      * </ul><br>
5998      * <p>
5999      * <b>Combining Options</b><br>
6000      * Using the options argument, it is possible to combine different types of listeners:<br>
6001      * <br>
6002      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6003      * Code:<pre><code>
6004 el.on('click', this.onClick, this, {
6005     single: true,
6006     delay: 100,
6007     stopEvent : true,
6008     forumId: 4
6009 });</code></pre>
6010      * <p>
6011      * <b>Attaching multiple handlers in 1 call</b><br>
6012       * The method also allows for a single argument to be passed which is a config object containing properties
6013      * which specify multiple handlers.
6014      * <p>
6015      * Code:<pre><code>
6016 el.on({
6017     'click' : {
6018         fn: this.onClick
6019         scope: this,
6020         delay: 100
6021     },
6022     'mouseover' : {
6023         fn: this.onMouseOver
6024         scope: this
6025     },
6026     'mouseout' : {
6027         fn: this.onMouseOut
6028         scope: this
6029     }
6030 });</code></pre>
6031      * <p>
6032      * Or a shorthand syntax:<br>
6033      * Code:<pre><code>
6034 el.on({
6035     'click' : this.onClick,
6036     'mouseover' : this.onMouseOver,
6037     'mouseout' : this.onMouseOut
6038     scope: this
6039 });</code></pre>
6040      */
6041         addListener : function(element, eventName, fn, scope, options){
6042             if(typeof eventName == "object"){
6043                 var o = eventName;
6044                 for(var e in o){
6045                     if(propRe.test(e)){
6046                         continue;
6047                     }
6048                     if(typeof o[e] == "function"){
6049                         // shared options
6050                         listen(element, e, o, o[e], o.scope);
6051                     }else{
6052                         // individual options
6053                         listen(element, e, o[e]);
6054                     }
6055                 }
6056                 return;
6057             }
6058             return listen(element, eventName, options, fn, scope);
6059         },
6060         
6061         /**
6062          * Removes an event handler
6063          *
6064          * @param {String/HTMLElement}   element        The id or html element to remove the 
6065          *                             event from
6066          * @param {String}   eventName     The type of event
6067          * @param {Function} fn
6068          * @return {Boolean} True if a listener was actually removed
6069          */
6070         removeListener : function(element, eventName, fn){
6071             return stopListening(element, eventName, fn);
6072         },
6073         
6074         /**
6075          * Fires when the document is ready (before onload and before images are loaded). Can be 
6076          * accessed shorthanded Roo.onReady().
6077          * @param {Function} fn        The method the event invokes
6078          * @param {Object}   scope    An  object that becomes the scope of the handler
6079          * @param {boolean}  options
6080          */
6081         onDocumentReady : function(fn, scope, options){
6082             if(docReadyState){ // if it already fired
6083                 docReadyEvent.addListener(fn, scope, options);
6084                 docReadyEvent.fire();
6085                 docReadyEvent.clearListeners();
6086                 return;
6087             }
6088             if(!docReadyEvent){
6089                 initDocReady();
6090             }
6091             docReadyEvent.addListener(fn, scope, options);
6092         },
6093         
6094         /**
6095          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6096          * @param {Function} fn        The method the event invokes
6097          * @param {Object}   scope    An object that becomes the scope of the handler
6098          * @param {boolean}  options
6099          */
6100         onWindowResize : function(fn, scope, options){
6101             if(!resizeEvent){
6102                 resizeEvent = new Roo.util.Event();
6103                 resizeTask = new Roo.util.DelayedTask(function(){
6104                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6105                 });
6106                 E.on(window, "resize", function(){
6107                     if(Roo.isIE){
6108                         resizeTask.delay(50);
6109                     }else{
6110                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6111                     }
6112                 });
6113             }
6114             resizeEvent.addListener(fn, scope, options);
6115         },
6116
6117         /**
6118          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6119          * @param {Function} fn        The method the event invokes
6120          * @param {Object}   scope    An object that becomes the scope of the handler
6121          * @param {boolean}  options
6122          */
6123         onTextResize : function(fn, scope, options){
6124             if(!textEvent){
6125                 textEvent = new Roo.util.Event();
6126                 var textEl = new Roo.Element(document.createElement('div'));
6127                 textEl.dom.className = 'x-text-resize';
6128                 textEl.dom.innerHTML = 'X';
6129                 textEl.appendTo(document.body);
6130                 textSize = textEl.dom.offsetHeight;
6131                 setInterval(function(){
6132                     if(textEl.dom.offsetHeight != textSize){
6133                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6134                     }
6135                 }, this.textResizeInterval);
6136             }
6137             textEvent.addListener(fn, scope, options);
6138         },
6139
6140         /**
6141          * Removes the passed window resize listener.
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    The scope of handler
6144          */
6145         removeResizeListener : function(fn, scope){
6146             if(resizeEvent){
6147                 resizeEvent.removeListener(fn, scope);
6148             }
6149         },
6150
6151         // private
6152         fireResize : function(){
6153             if(resizeEvent){
6154                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6155             }   
6156         },
6157         /**
6158          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6159          */
6160         ieDeferSrc : false,
6161         /**
6162          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6163          */
6164         textResizeInterval : 50
6165     };
6166     
6167     /**
6168      * Fix for doc tools
6169      * @scopeAlias pub=Roo.EventManager
6170      */
6171     
6172      /**
6173      * Appends an event handler to an element (shorthand for addListener)
6174      * @param {String/HTMLElement}   element        The html element or id to assign the
6175      * @param {String}   eventName The type of event to listen for
6176      * @param {Function} handler The method the event invokes
6177      * @param {Object}   scope (optional) The scope in which to execute the handler
6178      * function. The handler function's "this" context.
6179      * @param {Object}   options (optional) An object containing handler configuration
6180      * properties. This may contain any of the following properties:<ul>
6181      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6182      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6183      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6184      * <li>preventDefault {Boolean} True to prevent the default action</li>
6185      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6186      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6187      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6188      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6189      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6190      * by the specified number of milliseconds. If the event fires again within that time, the original
6191      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6192      * </ul><br>
6193      * <p>
6194      * <b>Combining Options</b><br>
6195      * Using the options argument, it is possible to combine different types of listeners:<br>
6196      * <br>
6197      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6198      * Code:<pre><code>
6199 el.on('click', this.onClick, this, {
6200     single: true,
6201     delay: 100,
6202     stopEvent : true,
6203     forumId: 4
6204 });</code></pre>
6205      * <p>
6206      * <b>Attaching multiple handlers in 1 call</b><br>
6207       * The method also allows for a single argument to be passed which is a config object containing properties
6208      * which specify multiple handlers.
6209      * <p>
6210      * Code:<pre><code>
6211 el.on({
6212     'click' : {
6213         fn: this.onClick
6214         scope: this,
6215         delay: 100
6216     },
6217     'mouseover' : {
6218         fn: this.onMouseOver
6219         scope: this
6220     },
6221     'mouseout' : {
6222         fn: this.onMouseOut
6223         scope: this
6224     }
6225 });</code></pre>
6226      * <p>
6227      * Or a shorthand syntax:<br>
6228      * Code:<pre><code>
6229 el.on({
6230     'click' : this.onClick,
6231     'mouseover' : this.onMouseOver,
6232     'mouseout' : this.onMouseOut
6233     scope: this
6234 });</code></pre>
6235      */
6236     pub.on = pub.addListener;
6237     pub.un = pub.removeListener;
6238
6239     pub.stoppedMouseDownEvent = new Roo.util.Event();
6240     return pub;
6241 }();
6242 /**
6243   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6244   * @param {Function} fn        The method the event invokes
6245   * @param {Object}   scope    An  object that becomes the scope of the handler
6246   * @param {boolean}  override If true, the obj passed in becomes
6247   *                             the execution scope of the listener
6248   * @member Roo
6249   * @method onReady
6250  */
6251 Roo.onReady = Roo.EventManager.onDocumentReady;
6252
6253 Roo.onReady(function(){
6254     var bd = Roo.get(document.body);
6255     if(!bd){ return; }
6256
6257     var cls = [
6258             Roo.isIE ? "roo-ie"
6259             : Roo.isGecko ? "roo-gecko"
6260             : Roo.isOpera ? "roo-opera"
6261             : Roo.isSafari ? "roo-safari" : ""];
6262
6263     if(Roo.isMac){
6264         cls.push("roo-mac");
6265     }
6266     if(Roo.isLinux){
6267         cls.push("roo-linux");
6268     }
6269     if(Roo.isBorderBox){
6270         cls.push('roo-border-box');
6271     }
6272     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6273         var p = bd.dom.parentNode;
6274         if(p){
6275             p.className += ' roo-strict';
6276         }
6277     }
6278     bd.addClass(cls.join(' '));
6279 });
6280
6281 /**
6282  * @class Roo.EventObject
6283  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6284  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6285  * Example:
6286  * <pre><code>
6287  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6288     e.preventDefault();
6289     var target = e.getTarget();
6290     ...
6291  }
6292  var myDiv = Roo.get("myDiv");
6293  myDiv.on("click", handleClick);
6294  //or
6295  Roo.EventManager.on("myDiv", 'click', handleClick);
6296  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6297  </code></pre>
6298  * @singleton
6299  */
6300 Roo.EventObject = function(){
6301     
6302     var E = Roo.lib.Event;
6303     
6304     // safari keypress events for special keys return bad keycodes
6305     var safariKeys = {
6306         63234 : 37, // left
6307         63235 : 39, // right
6308         63232 : 38, // up
6309         63233 : 40, // down
6310         63276 : 33, // page up
6311         63277 : 34, // page down
6312         63272 : 46, // delete
6313         63273 : 36, // home
6314         63275 : 35  // end
6315     };
6316
6317     // normalize button clicks
6318     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6319                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6320
6321     Roo.EventObjectImpl = function(e){
6322         if(e){
6323             this.setEvent(e.browserEvent || e);
6324         }
6325     };
6326     Roo.EventObjectImpl.prototype = {
6327         /**
6328          * Used to fix doc tools.
6329          * @scope Roo.EventObject.prototype
6330          */
6331             
6332
6333         
6334         
6335         /** The normal browser event */
6336         browserEvent : null,
6337         /** The button pressed in a mouse event */
6338         button : -1,
6339         /** True if the shift key was down during the event */
6340         shiftKey : false,
6341         /** True if the control key was down during the event */
6342         ctrlKey : false,
6343         /** True if the alt key was down during the event */
6344         altKey : false,
6345
6346         /** Key constant 
6347         * @type Number */
6348         BACKSPACE : 8,
6349         /** Key constant 
6350         * @type Number */
6351         TAB : 9,
6352         /** Key constant 
6353         * @type Number */
6354         RETURN : 13,
6355         /** Key constant 
6356         * @type Number */
6357         ENTER : 13,
6358         /** Key constant 
6359         * @type Number */
6360         SHIFT : 16,
6361         /** Key constant 
6362         * @type Number */
6363         CONTROL : 17,
6364         /** Key constant 
6365         * @type Number */
6366         ESC : 27,
6367         /** Key constant 
6368         * @type Number */
6369         SPACE : 32,
6370         /** Key constant 
6371         * @type Number */
6372         PAGEUP : 33,
6373         /** Key constant 
6374         * @type Number */
6375         PAGEDOWN : 34,
6376         /** Key constant 
6377         * @type Number */
6378         END : 35,
6379         /** Key constant 
6380         * @type Number */
6381         HOME : 36,
6382         /** Key constant 
6383         * @type Number */
6384         LEFT : 37,
6385         /** Key constant 
6386         * @type Number */
6387         UP : 38,
6388         /** Key constant 
6389         * @type Number */
6390         RIGHT : 39,
6391         /** Key constant 
6392         * @type Number */
6393         DOWN : 40,
6394         /** Key constant 
6395         * @type Number */
6396         DELETE : 46,
6397         /** Key constant 
6398         * @type Number */
6399         F5 : 116,
6400
6401            /** @private */
6402         setEvent : function(e){
6403             if(e == this || (e && e.browserEvent)){ // already wrapped
6404                 return e;
6405             }
6406             this.browserEvent = e;
6407             if(e){
6408                 // normalize buttons
6409                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6410                 if(e.type == 'click' && this.button == -1){
6411                     this.button = 0;
6412                 }
6413                 this.type = e.type;
6414                 this.shiftKey = e.shiftKey;
6415                 // mac metaKey behaves like ctrlKey
6416                 this.ctrlKey = e.ctrlKey || e.metaKey;
6417                 this.altKey = e.altKey;
6418                 // in getKey these will be normalized for the mac
6419                 this.keyCode = e.keyCode;
6420                 // keyup warnings on firefox.
6421                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6422                 // cache the target for the delayed and or buffered events
6423                 this.target = E.getTarget(e);
6424                 // same for XY
6425                 this.xy = E.getXY(e);
6426             }else{
6427                 this.button = -1;
6428                 this.shiftKey = false;
6429                 this.ctrlKey = false;
6430                 this.altKey = false;
6431                 this.keyCode = 0;
6432                 this.charCode =0;
6433                 this.target = null;
6434                 this.xy = [0, 0];
6435             }
6436             return this;
6437         },
6438
6439         /**
6440          * Stop the event (preventDefault and stopPropagation)
6441          */
6442         stopEvent : function(){
6443             if(this.browserEvent){
6444                 if(this.browserEvent.type == 'mousedown'){
6445                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6446                 }
6447                 E.stopEvent(this.browserEvent);
6448             }
6449         },
6450
6451         /**
6452          * Prevents the browsers default handling of the event.
6453          */
6454         preventDefault : function(){
6455             if(this.browserEvent){
6456                 E.preventDefault(this.browserEvent);
6457             }
6458         },
6459
6460         /** @private */
6461         isNavKeyPress : function(){
6462             var k = this.keyCode;
6463             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6464             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6465         },
6466
6467         isSpecialKey : function(){
6468             var k = this.keyCode;
6469             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6470             (k == 16) || (k == 17) ||
6471             (k >= 18 && k <= 20) ||
6472             (k >= 33 && k <= 35) ||
6473             (k >= 36 && k <= 39) ||
6474             (k >= 44 && k <= 45);
6475         },
6476         /**
6477          * Cancels bubbling of the event.
6478          */
6479         stopPropagation : function(){
6480             if(this.browserEvent){
6481                 if(this.type == 'mousedown'){
6482                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6483                 }
6484                 E.stopPropagation(this.browserEvent);
6485             }
6486         },
6487
6488         /**
6489          * Gets the key code for the event.
6490          * @return {Number}
6491          */
6492         getCharCode : function(){
6493             return this.charCode || this.keyCode;
6494         },
6495
6496         /**
6497          * Returns a normalized keyCode for the event.
6498          * @return {Number} The key code
6499          */
6500         getKey : function(){
6501             var k = this.keyCode || this.charCode;
6502             return Roo.isSafari ? (safariKeys[k] || k) : k;
6503         },
6504
6505         /**
6506          * Gets the x coordinate of the event.
6507          * @return {Number}
6508          */
6509         getPageX : function(){
6510             return this.xy[0];
6511         },
6512
6513         /**
6514          * Gets the y coordinate of the event.
6515          * @return {Number}
6516          */
6517         getPageY : function(){
6518             return this.xy[1];
6519         },
6520
6521         /**
6522          * Gets the time of the event.
6523          * @return {Number}
6524          */
6525         getTime : function(){
6526             if(this.browserEvent){
6527                 return E.getTime(this.browserEvent);
6528             }
6529             return null;
6530         },
6531
6532         /**
6533          * Gets the page coordinates of the event.
6534          * @return {Array} The xy values like [x, y]
6535          */
6536         getXY : function(){
6537             return this.xy;
6538         },
6539
6540         /**
6541          * Gets the target for the event.
6542          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6543          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6544                 search as a number or element (defaults to 10 || document.body)
6545          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6546          * @return {HTMLelement}
6547          */
6548         getTarget : function(selector, maxDepth, returnEl){
6549             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6550         },
6551         /**
6552          * Gets the related target.
6553          * @return {HTMLElement}
6554          */
6555         getRelatedTarget : function(){
6556             if(this.browserEvent){
6557                 return E.getRelatedTarget(this.browserEvent);
6558             }
6559             return null;
6560         },
6561
6562         /**
6563          * Normalizes mouse wheel delta across browsers
6564          * @return {Number} The delta
6565          */
6566         getWheelDelta : function(){
6567             var e = this.browserEvent;
6568             var delta = 0;
6569             if(e.wheelDelta){ /* IE/Opera. */
6570                 delta = e.wheelDelta/120;
6571             }else if(e.detail){ /* Mozilla case. */
6572                 delta = -e.detail/3;
6573             }
6574             return delta;
6575         },
6576
6577         /**
6578          * Returns true if the control, meta, shift or alt key was pressed during this event.
6579          * @return {Boolean}
6580          */
6581         hasModifier : function(){
6582             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6583         },
6584
6585         /**
6586          * Returns true if the target of this event equals el or is a child of el
6587          * @param {String/HTMLElement/Element} el
6588          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6589          * @return {Boolean}
6590          */
6591         within : function(el, related){
6592             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6593             return t && Roo.fly(el).contains(t);
6594         },
6595
6596         getPoint : function(){
6597             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6598         }
6599     };
6600
6601     return new Roo.EventObjectImpl();
6602 }();
6603             
6604     /*
6605  * Based on:
6606  * Ext JS Library 1.1.1
6607  * Copyright(c) 2006-2007, Ext JS, LLC.
6608  *
6609  * Originally Released Under LGPL - original licence link has changed is not relivant.
6610  *
6611  * Fork - LGPL
6612  * <script type="text/javascript">
6613  */
6614
6615  
6616 // was in Composite Element!??!?!
6617  
6618 (function(){
6619     var D = Roo.lib.Dom;
6620     var E = Roo.lib.Event;
6621     var A = Roo.lib.Anim;
6622
6623     // local style camelizing for speed
6624     var propCache = {};
6625     var camelRe = /(-[a-z])/gi;
6626     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6627     var view = document.defaultView;
6628
6629 /**
6630  * @class Roo.Element
6631  * Represents an Element in the DOM.<br><br>
6632  * Usage:<br>
6633 <pre><code>
6634 var el = Roo.get("my-div");
6635
6636 // or with getEl
6637 var el = getEl("my-div");
6638
6639 // or with a DOM element
6640 var el = Roo.get(myDivElement);
6641 </code></pre>
6642  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6643  * each call instead of constructing a new one.<br><br>
6644  * <b>Animations</b><br />
6645  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6646  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6647 <pre>
6648 Option    Default   Description
6649 --------- --------  ---------------------------------------------
6650 duration  .35       The duration of the animation in seconds
6651 easing    easeOut   The YUI easing method
6652 callback  none      A function to execute when the anim completes
6653 scope     this      The scope (this) of the callback function
6654 </pre>
6655 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6656 * manipulate the animation. Here's an example:
6657 <pre><code>
6658 var el = Roo.get("my-div");
6659
6660 // no animation
6661 el.setWidth(100);
6662
6663 // default animation
6664 el.setWidth(100, true);
6665
6666 // animation with some options set
6667 el.setWidth(100, {
6668     duration: 1,
6669     callback: this.foo,
6670     scope: this
6671 });
6672
6673 // using the "anim" property to get the Anim object
6674 var opt = {
6675     duration: 1,
6676     callback: this.foo,
6677     scope: this
6678 };
6679 el.setWidth(100, opt);
6680 ...
6681 if(opt.anim.isAnimated()){
6682     opt.anim.stop();
6683 }
6684 </code></pre>
6685 * <b> Composite (Collections of) Elements</b><br />
6686  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6687  * @constructor Create a new Element directly.
6688  * @param {String/HTMLElement} element
6689  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6690  */
6691     Roo.Element = function(element, forceNew){
6692         var dom = typeof element == "string" ?
6693                 document.getElementById(element) : element;
6694         if(!dom){ // invalid id/element
6695             return null;
6696         }
6697         var id = dom.id;
6698         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6699             return Roo.Element.cache[id];
6700         }
6701
6702         /**
6703          * The DOM element
6704          * @type HTMLElement
6705          */
6706         this.dom = dom;
6707
6708         /**
6709          * The DOM element ID
6710          * @type String
6711          */
6712         this.id = id || Roo.id(dom);
6713     };
6714
6715     var El = Roo.Element;
6716
6717     El.prototype = {
6718         /**
6719          * The element's default display mode  (defaults to "")
6720          * @type String
6721          */
6722         originalDisplay : "",
6723
6724         visibilityMode : 1,
6725         /**
6726          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6727          * @type String
6728          */
6729         defaultUnit : "px",
6730         /**
6731          * Sets the element's visibility mode. When setVisible() is called it
6732          * will use this to determine whether to set the visibility or the display property.
6733          * @param visMode Element.VISIBILITY or Element.DISPLAY
6734          * @return {Roo.Element} this
6735          */
6736         setVisibilityMode : function(visMode){
6737             this.visibilityMode = visMode;
6738             return this;
6739         },
6740         /**
6741          * Convenience method for setVisibilityMode(Element.DISPLAY)
6742          * @param {String} display (optional) What to set display to when visible
6743          * @return {Roo.Element} this
6744          */
6745         enableDisplayMode : function(display){
6746             this.setVisibilityMode(El.DISPLAY);
6747             if(typeof display != "undefined") this.originalDisplay = display;
6748             return this;
6749         },
6750
6751         /**
6752          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6753          * @param {String} selector The simple selector to test
6754          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6755                 search as a number or element (defaults to 10 || document.body)
6756          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6757          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6758          */
6759         findParent : function(simpleSelector, maxDepth, returnEl){
6760             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6761             maxDepth = maxDepth || 50;
6762             if(typeof maxDepth != "number"){
6763                 stopEl = Roo.getDom(maxDepth);
6764                 maxDepth = 10;
6765             }
6766             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6767                 if(dq.is(p, simpleSelector)){
6768                     return returnEl ? Roo.get(p) : p;
6769                 }
6770                 depth++;
6771                 p = p.parentNode;
6772             }
6773             return null;
6774         },
6775
6776
6777         /**
6778          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6779          * @param {String} selector The simple selector to test
6780          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6781                 search as a number or element (defaults to 10 || document.body)
6782          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6783          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6784          */
6785         findParentNode : function(simpleSelector, maxDepth, returnEl){
6786             var p = Roo.fly(this.dom.parentNode, '_internal');
6787             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6788         },
6789
6790         /**
6791          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6792          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6793          * @param {String} selector The simple selector to test
6794          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6795                 search as a number or element (defaults to 10 || document.body)
6796          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6797          */
6798         up : function(simpleSelector, maxDepth){
6799             return this.findParentNode(simpleSelector, maxDepth, true);
6800         },
6801
6802
6803
6804         /**
6805          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6806          * @param {String} selector The simple selector to test
6807          * @return {Boolean} True if this element matches the selector, else false
6808          */
6809         is : function(simpleSelector){
6810             return Roo.DomQuery.is(this.dom, simpleSelector);
6811         },
6812
6813         /**
6814          * Perform animation on this element.
6815          * @param {Object} args The YUI animation control args
6816          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6817          * @param {Function} onComplete (optional) Function to call when animation completes
6818          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6819          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6820          * @return {Roo.Element} this
6821          */
6822         animate : function(args, duration, onComplete, easing, animType){
6823             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6824             return this;
6825         },
6826
6827         /*
6828          * @private Internal animation call
6829          */
6830         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6831             animType = animType || 'run';
6832             opt = opt || {};
6833             var anim = Roo.lib.Anim[animType](
6834                 this.dom, args,
6835                 (opt.duration || defaultDur) || .35,
6836                 (opt.easing || defaultEase) || 'easeOut',
6837                 function(){
6838                     Roo.callback(cb, this);
6839                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6840                 },
6841                 this
6842             );
6843             opt.anim = anim;
6844             return anim;
6845         },
6846
6847         // private legacy anim prep
6848         preanim : function(a, i){
6849             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6850         },
6851
6852         /**
6853          * Removes worthless text nodes
6854          * @param {Boolean} forceReclean (optional) By default the element
6855          * keeps track if it has been cleaned already so
6856          * you can call this over and over. However, if you update the element and
6857          * need to force a reclean, you can pass true.
6858          */
6859         clean : function(forceReclean){
6860             if(this.isCleaned && forceReclean !== true){
6861                 return this;
6862             }
6863             var ns = /\S/;
6864             var d = this.dom, n = d.firstChild, ni = -1;
6865             while(n){
6866                 var nx = n.nextSibling;
6867                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6868                     d.removeChild(n);
6869                 }else{
6870                     n.nodeIndex = ++ni;
6871                 }
6872                 n = nx;
6873             }
6874             this.isCleaned = true;
6875             return this;
6876         },
6877
6878         // private
6879         calcOffsetsTo : function(el){
6880             el = Roo.get(el);
6881             var d = el.dom;
6882             var restorePos = false;
6883             if(el.getStyle('position') == 'static'){
6884                 el.position('relative');
6885                 restorePos = true;
6886             }
6887             var x = 0, y =0;
6888             var op = this.dom;
6889             while(op && op != d && op.tagName != 'HTML'){
6890                 x+= op.offsetLeft;
6891                 y+= op.offsetTop;
6892                 op = op.offsetParent;
6893             }
6894             if(restorePos){
6895                 el.position('static');
6896             }
6897             return [x, y];
6898         },
6899
6900         /**
6901          * Scrolls this element into view within the passed container.
6902          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6903          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6904          * @return {Roo.Element} this
6905          */
6906         scrollIntoView : function(container, hscroll){
6907             var c = Roo.getDom(container) || document.body;
6908             var el = this.dom;
6909
6910             var o = this.calcOffsetsTo(c),
6911                 l = o[0],
6912                 t = o[1],
6913                 b = t+el.offsetHeight,
6914                 r = l+el.offsetWidth;
6915
6916             var ch = c.clientHeight;
6917             var ct = parseInt(c.scrollTop, 10);
6918             var cl = parseInt(c.scrollLeft, 10);
6919             var cb = ct + ch;
6920             var cr = cl + c.clientWidth;
6921
6922             if(t < ct){
6923                 c.scrollTop = t;
6924             }else if(b > cb){
6925                 c.scrollTop = b-ch;
6926             }
6927
6928             if(hscroll !== false){
6929                 if(l < cl){
6930                     c.scrollLeft = l;
6931                 }else if(r > cr){
6932                     c.scrollLeft = r-c.clientWidth;
6933                 }
6934             }
6935             return this;
6936         },
6937
6938         // private
6939         scrollChildIntoView : function(child, hscroll){
6940             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6941         },
6942
6943         /**
6944          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6945          * the new height may not be available immediately.
6946          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6947          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6948          * @param {Function} onComplete (optional) Function to call when animation completes
6949          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6950          * @return {Roo.Element} this
6951          */
6952         autoHeight : function(animate, duration, onComplete, easing){
6953             var oldHeight = this.getHeight();
6954             this.clip();
6955             this.setHeight(1); // force clipping
6956             setTimeout(function(){
6957                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6958                 if(!animate){
6959                     this.setHeight(height);
6960                     this.unclip();
6961                     if(typeof onComplete == "function"){
6962                         onComplete();
6963                     }
6964                 }else{
6965                     this.setHeight(oldHeight); // restore original height
6966                     this.setHeight(height, animate, duration, function(){
6967                         this.unclip();
6968                         if(typeof onComplete == "function") onComplete();
6969                     }.createDelegate(this), easing);
6970                 }
6971             }.createDelegate(this), 0);
6972             return this;
6973         },
6974
6975         /**
6976          * Returns true if this element is an ancestor of the passed element
6977          * @param {HTMLElement/String} el The element to check
6978          * @return {Boolean} True if this element is an ancestor of el, else false
6979          */
6980         contains : function(el){
6981             if(!el){return false;}
6982             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6983         },
6984
6985         /**
6986          * Checks whether the element is currently visible using both visibility and display properties.
6987          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6988          * @return {Boolean} True if the element is currently visible, else false
6989          */
6990         isVisible : function(deep) {
6991             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6992             if(deep !== true || !vis){
6993                 return vis;
6994             }
6995             var p = this.dom.parentNode;
6996             while(p && p.tagName.toLowerCase() != "body"){
6997                 if(!Roo.fly(p, '_isVisible').isVisible()){
6998                     return false;
6999                 }
7000                 p = p.parentNode;
7001             }
7002             return true;
7003         },
7004
7005         /**
7006          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7007          * @param {String} selector The CSS selector
7008          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7009          * @return {CompositeElement/CompositeElementLite} The composite element
7010          */
7011         select : function(selector, unique){
7012             return El.select(selector, unique, this.dom);
7013         },
7014
7015         /**
7016          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7017          * @param {String} selector The CSS selector
7018          * @return {Array} An array of the matched nodes
7019          */
7020         query : function(selector, unique){
7021             return Roo.DomQuery.select(selector, this.dom);
7022         },
7023
7024         /**
7025          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7026          * @param {String} selector The CSS selector
7027          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7028          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7029          */
7030         child : function(selector, returnDom){
7031             var n = Roo.DomQuery.selectNode(selector, this.dom);
7032             return returnDom ? n : Roo.get(n);
7033         },
7034
7035         /**
7036          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7037          * @param {String} selector The CSS selector
7038          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7039          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7040          */
7041         down : function(selector, returnDom){
7042             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7043             return returnDom ? n : Roo.get(n);
7044         },
7045
7046         /**
7047          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7048          * @param {String} group The group the DD object is member of
7049          * @param {Object} config The DD config object
7050          * @param {Object} overrides An object containing methods to override/implement on the DD object
7051          * @return {Roo.dd.DD} The DD object
7052          */
7053         initDD : function(group, config, overrides){
7054             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7055             return Roo.apply(dd, overrides);
7056         },
7057
7058         /**
7059          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7060          * @param {String} group The group the DDProxy object is member of
7061          * @param {Object} config The DDProxy config object
7062          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7063          * @return {Roo.dd.DDProxy} The DDProxy object
7064          */
7065         initDDProxy : function(group, config, overrides){
7066             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7067             return Roo.apply(dd, overrides);
7068         },
7069
7070         /**
7071          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7072          * @param {String} group The group the DDTarget object is member of
7073          * @param {Object} config The DDTarget config object
7074          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7075          * @return {Roo.dd.DDTarget} The DDTarget object
7076          */
7077         initDDTarget : function(group, config, overrides){
7078             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7079             return Roo.apply(dd, overrides);
7080         },
7081
7082         /**
7083          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7084          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7085          * @param {Boolean} visible Whether the element is visible
7086          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7087          * @return {Roo.Element} this
7088          */
7089          setVisible : function(visible, animate){
7090             if(!animate || !A){
7091                 if(this.visibilityMode == El.DISPLAY){
7092                     this.setDisplayed(visible);
7093                 }else{
7094                     this.fixDisplay();
7095                     this.dom.style.visibility = visible ? "visible" : "hidden";
7096                 }
7097             }else{
7098                 // closure for composites
7099                 var dom = this.dom;
7100                 var visMode = this.visibilityMode;
7101                 if(visible){
7102                     this.setOpacity(.01);
7103                     this.setVisible(true);
7104                 }
7105                 this.anim({opacity: { to: (visible?1:0) }},
7106                       this.preanim(arguments, 1),
7107                       null, .35, 'easeIn', function(){
7108                          if(!visible){
7109                              if(visMode == El.DISPLAY){
7110                                  dom.style.display = "none";
7111                              }else{
7112                                  dom.style.visibility = "hidden";
7113                              }
7114                              Roo.get(dom).setOpacity(1);
7115                          }
7116                      });
7117             }
7118             return this;
7119         },
7120
7121         /**
7122          * Returns true if display is not "none"
7123          * @return {Boolean}
7124          */
7125         isDisplayed : function() {
7126             return this.getStyle("display") != "none";
7127         },
7128
7129         /**
7130          * Toggles the element's visibility or display, depending on visibility mode.
7131          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7132          * @return {Roo.Element} this
7133          */
7134         toggle : function(animate){
7135             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7136             return this;
7137         },
7138
7139         /**
7140          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7141          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7142          * @return {Roo.Element} this
7143          */
7144         setDisplayed : function(value) {
7145             if(typeof value == "boolean"){
7146                value = value ? this.originalDisplay : "none";
7147             }
7148             this.setStyle("display", value);
7149             return this;
7150         },
7151
7152         /**
7153          * Tries to focus the element. Any exceptions are caught and ignored.
7154          * @return {Roo.Element} this
7155          */
7156         focus : function() {
7157             try{
7158                 this.dom.focus();
7159             }catch(e){}
7160             return this;
7161         },
7162
7163         /**
7164          * Tries to blur the element. Any exceptions are caught and ignored.
7165          * @return {Roo.Element} this
7166          */
7167         blur : function() {
7168             try{
7169                 this.dom.blur();
7170             }catch(e){}
7171             return this;
7172         },
7173
7174         /**
7175          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7176          * @param {String/Array} className The CSS class to add, or an array of classes
7177          * @return {Roo.Element} this
7178          */
7179         addClass : function(className){
7180             if(className instanceof Array){
7181                 for(var i = 0, len = className.length; i < len; i++) {
7182                     this.addClass(className[i]);
7183                 }
7184             }else{
7185                 if(className && !this.hasClass(className)){
7186                     this.dom.className = this.dom.className + " " + className;
7187                 }
7188             }
7189             return this;
7190         },
7191
7192         /**
7193          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7194          * @param {String/Array} className The CSS class to add, or an array of classes
7195          * @return {Roo.Element} this
7196          */
7197         radioClass : function(className){
7198             var siblings = this.dom.parentNode.childNodes;
7199             for(var i = 0; i < siblings.length; i++) {
7200                 var s = siblings[i];
7201                 if(s.nodeType == 1){
7202                     Roo.get(s).removeClass(className);
7203                 }
7204             }
7205             this.addClass(className);
7206             return this;
7207         },
7208
7209         /**
7210          * Removes one or more CSS classes from the element.
7211          * @param {String/Array} className The CSS class to remove, or an array of classes
7212          * @return {Roo.Element} this
7213          */
7214         removeClass : function(className){
7215             if(!className || !this.dom.className){
7216                 return this;
7217             }
7218             if(className instanceof Array){
7219                 for(var i = 0, len = className.length; i < len; i++) {
7220                     this.removeClass(className[i]);
7221                 }
7222             }else{
7223                 if(this.hasClass(className)){
7224                     var re = this.classReCache[className];
7225                     if (!re) {
7226                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7227                        this.classReCache[className] = re;
7228                     }
7229                     this.dom.className =
7230                         this.dom.className.replace(re, " ");
7231                 }
7232             }
7233             return this;
7234         },
7235
7236         // private
7237         classReCache: {},
7238
7239         /**
7240          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7241          * @param {String} className The CSS class to toggle
7242          * @return {Roo.Element} this
7243          */
7244         toggleClass : function(className){
7245             if(this.hasClass(className)){
7246                 this.removeClass(className);
7247             }else{
7248                 this.addClass(className);
7249             }
7250             return this;
7251         },
7252
7253         /**
7254          * Checks if the specified CSS class exists on this element's DOM node.
7255          * @param {String} className The CSS class to check for
7256          * @return {Boolean} True if the class exists, else false
7257          */
7258         hasClass : function(className){
7259             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7260         },
7261
7262         /**
7263          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7264          * @param {String} oldClassName The CSS class to replace
7265          * @param {String} newClassName The replacement CSS class
7266          * @return {Roo.Element} this
7267          */
7268         replaceClass : function(oldClassName, newClassName){
7269             this.removeClass(oldClassName);
7270             this.addClass(newClassName);
7271             return this;
7272         },
7273
7274         /**
7275          * Returns an object with properties matching the styles requested.
7276          * For example, el.getStyles('color', 'font-size', 'width') might return
7277          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7278          * @param {String} style1 A style name
7279          * @param {String} style2 A style name
7280          * @param {String} etc.
7281          * @return {Object} The style object
7282          */
7283         getStyles : function(){
7284             var a = arguments, len = a.length, r = {};
7285             for(var i = 0; i < len; i++){
7286                 r[a[i]] = this.getStyle(a[i]);
7287             }
7288             return r;
7289         },
7290
7291         /**
7292          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7293          * @param {String} property The style property whose value is returned.
7294          * @return {String} The current value of the style property for this element.
7295          */
7296         getStyle : function(){
7297             return view && view.getComputedStyle ?
7298                 function(prop){
7299                     var el = this.dom, v, cs, camel;
7300                     if(prop == 'float'){
7301                         prop = "cssFloat";
7302                     }
7303                     if(el.style && (v = el.style[prop])){
7304                         return v;
7305                     }
7306                     if(cs = view.getComputedStyle(el, "")){
7307                         if(!(camel = propCache[prop])){
7308                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7309                         }
7310                         return cs[camel];
7311                     }
7312                     return null;
7313                 } :
7314                 function(prop){
7315                     var el = this.dom, v, cs, camel;
7316                     if(prop == 'opacity'){
7317                         if(typeof el.style.filter == 'string'){
7318                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7319                             if(m){
7320                                 var fv = parseFloat(m[1]);
7321                                 if(!isNaN(fv)){
7322                                     return fv ? fv / 100 : 0;
7323                                 }
7324                             }
7325                         }
7326                         return 1;
7327                     }else if(prop == 'float'){
7328                         prop = "styleFloat";
7329                     }
7330                     if(!(camel = propCache[prop])){
7331                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7332                     }
7333                     if(v = el.style[camel]){
7334                         return v;
7335                     }
7336                     if(cs = el.currentStyle){
7337                         return cs[camel];
7338                     }
7339                     return null;
7340                 };
7341         }(),
7342
7343         /**
7344          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7345          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7346          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7347          * @return {Roo.Element} this
7348          */
7349         setStyle : function(prop, value){
7350             if(typeof prop == "string"){
7351                 var camel;
7352                 if(!(camel = propCache[prop])){
7353                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7354                 }
7355                 if(camel == 'opacity') {
7356                     this.setOpacity(value);
7357                 }else{
7358                     this.dom.style[camel] = value;
7359                 }
7360             }else{
7361                 for(var style in prop){
7362                     if(typeof prop[style] != "function"){
7363                        this.setStyle(style, prop[style]);
7364                     }
7365                 }
7366             }
7367             return this;
7368         },
7369
7370         /**
7371          * More flexible version of {@link #setStyle} for setting style properties.
7372          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7373          * a function which returns such a specification.
7374          * @return {Roo.Element} this
7375          */
7376         applyStyles : function(style){
7377             Roo.DomHelper.applyStyles(this.dom, style);
7378             return this;
7379         },
7380
7381         /**
7382           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7383           * @return {Number} The X position of the element
7384           */
7385         getX : function(){
7386             return D.getX(this.dom);
7387         },
7388
7389         /**
7390           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7391           * @return {Number} The Y position of the element
7392           */
7393         getY : function(){
7394             return D.getY(this.dom);
7395         },
7396
7397         /**
7398           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7399           * @return {Array} The XY position of the element
7400           */
7401         getXY : function(){
7402             return D.getXY(this.dom);
7403         },
7404
7405         /**
7406          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7407          * @param {Number} The X position of the element
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411         setX : function(x, animate){
7412             if(!animate || !A){
7413                 D.setX(this.dom, x);
7414             }else{
7415                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7416             }
7417             return this;
7418         },
7419
7420         /**
7421          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7422          * @param {Number} The Y position of the element
7423          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7424          * @return {Roo.Element} this
7425          */
7426         setY : function(y, animate){
7427             if(!animate || !A){
7428                 D.setY(this.dom, y);
7429             }else{
7430                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7431             }
7432             return this;
7433         },
7434
7435         /**
7436          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7437          * @param {String} left The left CSS property value
7438          * @return {Roo.Element} this
7439          */
7440         setLeft : function(left){
7441             this.setStyle("left", this.addUnits(left));
7442             return this;
7443         },
7444
7445         /**
7446          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7447          * @param {String} top The top CSS property value
7448          * @return {Roo.Element} this
7449          */
7450         setTop : function(top){
7451             this.setStyle("top", this.addUnits(top));
7452             return this;
7453         },
7454
7455         /**
7456          * Sets the element's CSS right style.
7457          * @param {String} right The right CSS property value
7458          * @return {Roo.Element} this
7459          */
7460         setRight : function(right){
7461             this.setStyle("right", this.addUnits(right));
7462             return this;
7463         },
7464
7465         /**
7466          * Sets the element's CSS bottom style.
7467          * @param {String} bottom The bottom CSS property value
7468          * @return {Roo.Element} this
7469          */
7470         setBottom : function(bottom){
7471             this.setStyle("bottom", this.addUnits(bottom));
7472             return this;
7473         },
7474
7475         /**
7476          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7477          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7478          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7479          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7480          * @return {Roo.Element} this
7481          */
7482         setXY : function(pos, animate){
7483             if(!animate || !A){
7484                 D.setXY(this.dom, pos);
7485             }else{
7486                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7487             }
7488             return this;
7489         },
7490
7491         /**
7492          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7493          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7494          * @param {Number} x X value for new position (coordinates are page-based)
7495          * @param {Number} y Y value for new position (coordinates are page-based)
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Roo.Element} this
7498          */
7499         setLocation : function(x, y, animate){
7500             this.setXY([x, y], this.preanim(arguments, 2));
7501             return this;
7502         },
7503
7504         /**
7505          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7506          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7507          * @param {Number} x X value for new position (coordinates are page-based)
7508          * @param {Number} y Y value for new position (coordinates are page-based)
7509          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7510          * @return {Roo.Element} this
7511          */
7512         moveTo : function(x, y, animate){
7513             this.setXY([x, y], this.preanim(arguments, 2));
7514             return this;
7515         },
7516
7517         /**
7518          * Returns the region of the given element.
7519          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7520          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7521          */
7522         getRegion : function(){
7523             return D.getRegion(this.dom);
7524         },
7525
7526         /**
7527          * Returns the offset height of the element
7528          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7529          * @return {Number} The element's height
7530          */
7531         getHeight : function(contentHeight){
7532             var h = this.dom.offsetHeight || 0;
7533             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7534         },
7535
7536         /**
7537          * Returns the offset width of the element
7538          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7539          * @return {Number} The element's width
7540          */
7541         getWidth : function(contentWidth){
7542             var w = this.dom.offsetWidth || 0;
7543             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7544         },
7545
7546         /**
7547          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7548          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7549          * if a height has not been set using CSS.
7550          * @return {Number}
7551          */
7552         getComputedHeight : function(){
7553             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7554             if(!h){
7555                 h = parseInt(this.getStyle('height'), 10) || 0;
7556                 if(!this.isBorderBox()){
7557                     h += this.getFrameWidth('tb');
7558                 }
7559             }
7560             return h;
7561         },
7562
7563         /**
7564          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7565          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7566          * if a width has not been set using CSS.
7567          * @return {Number}
7568          */
7569         getComputedWidth : function(){
7570             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7571             if(!w){
7572                 w = parseInt(this.getStyle('width'), 10) || 0;
7573                 if(!this.isBorderBox()){
7574                     w += this.getFrameWidth('lr');
7575                 }
7576             }
7577             return w;
7578         },
7579
7580         /**
7581          * Returns the size of the element.
7582          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7583          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7584          */
7585         getSize : function(contentSize){
7586             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7587         },
7588
7589         /**
7590          * Returns the width and height of the viewport.
7591          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7592          */
7593         getViewSize : function(){
7594             var d = this.dom, doc = document, aw = 0, ah = 0;
7595             if(d == doc || d == doc.body){
7596                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7597             }else{
7598                 return {
7599                     width : d.clientWidth,
7600                     height: d.clientHeight
7601                 };
7602             }
7603         },
7604
7605         /**
7606          * Returns the value of the "value" attribute
7607          * @param {Boolean} asNumber true to parse the value as a number
7608          * @return {String/Number}
7609          */
7610         getValue : function(asNumber){
7611             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7612         },
7613
7614         // private
7615         adjustWidth : function(width){
7616             if(typeof width == "number"){
7617                 if(this.autoBoxAdjust && !this.isBorderBox()){
7618                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7619                 }
7620                 if(width < 0){
7621                     width = 0;
7622                 }
7623             }
7624             return width;
7625         },
7626
7627         // private
7628         adjustHeight : function(height){
7629             if(typeof height == "number"){
7630                if(this.autoBoxAdjust && !this.isBorderBox()){
7631                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7632                }
7633                if(height < 0){
7634                    height = 0;
7635                }
7636             }
7637             return height;
7638         },
7639
7640         /**
7641          * Set the width of the element
7642          * @param {Number} width The new width
7643          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7644          * @return {Roo.Element} this
7645          */
7646         setWidth : function(width, animate){
7647             width = this.adjustWidth(width);
7648             if(!animate || !A){
7649                 this.dom.style.width = this.addUnits(width);
7650             }else{
7651                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7652             }
7653             return this;
7654         },
7655
7656         /**
7657          * Set the height of the element
7658          * @param {Number} height The new height
7659          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662          setHeight : function(height, animate){
7663             height = this.adjustHeight(height);
7664             if(!animate || !A){
7665                 this.dom.style.height = this.addUnits(height);
7666             }else{
7667                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7668             }
7669             return this;
7670         },
7671
7672         /**
7673          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7674          * @param {Number} width The new width
7675          * @param {Number} height The new height
7676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7677          * @return {Roo.Element} this
7678          */
7679          setSize : function(width, height, animate){
7680             if(typeof width == "object"){ // in case of object from getSize()
7681                 height = width.height; width = width.width;
7682             }
7683             width = this.adjustWidth(width); height = this.adjustHeight(height);
7684             if(!animate || !A){
7685                 this.dom.style.width = this.addUnits(width);
7686                 this.dom.style.height = this.addUnits(height);
7687             }else{
7688                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7689             }
7690             return this;
7691         },
7692
7693         /**
7694          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7695          * @param {Number} x X value for new position (coordinates are page-based)
7696          * @param {Number} y Y value for new position (coordinates are page-based)
7697          * @param {Number} width The new width
7698          * @param {Number} height The new height
7699          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7700          * @return {Roo.Element} this
7701          */
7702         setBounds : function(x, y, width, height, animate){
7703             if(!animate || !A){
7704                 this.setSize(width, height);
7705                 this.setLocation(x, y);
7706             }else{
7707                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7708                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7709                               this.preanim(arguments, 4), 'motion');
7710             }
7711             return this;
7712         },
7713
7714         /**
7715          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7716          * @param {Roo.lib.Region} region The region to fill
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setRegion : function(region, animate){
7721             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7722             return this;
7723         },
7724
7725         /**
7726          * Appends an event handler
7727          *
7728          * @param {String}   eventName     The type of event to append
7729          * @param {Function} fn        The method the event invokes
7730          * @param {Object} scope       (optional) The scope (this object) of the fn
7731          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7732          */
7733         addListener : function(eventName, fn, scope, options){
7734             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7735         },
7736
7737         /**
7738          * Removes an event handler from this element
7739          * @param {String} eventName the type of event to remove
7740          * @param {Function} fn the method the event invokes
7741          * @return {Roo.Element} this
7742          */
7743         removeListener : function(eventName, fn){
7744             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7745             return this;
7746         },
7747
7748         /**
7749          * Removes all previous added listeners from this element
7750          * @return {Roo.Element} this
7751          */
7752         removeAllListeners : function(){
7753             E.purgeElement(this.dom);
7754             return this;
7755         },
7756
7757         relayEvent : function(eventName, observable){
7758             this.on(eventName, function(e){
7759                 observable.fireEvent(eventName, e);
7760             });
7761         },
7762
7763         /**
7764          * Set the opacity of the element
7765          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setOpacity : function(opacity, animate){
7770             if(!animate || !A){
7771                 var s = this.dom.style;
7772                 if(Roo.isIE){
7773                     s.zoom = 1;
7774                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7775                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7776                 }else{
7777                     s.opacity = opacity;
7778                 }
7779             }else{
7780                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7781             }
7782             return this;
7783         },
7784
7785         /**
7786          * Gets the left X coordinate
7787          * @param {Boolean} local True to get the local css position instead of page coordinate
7788          * @return {Number}
7789          */
7790         getLeft : function(local){
7791             if(!local){
7792                 return this.getX();
7793             }else{
7794                 return parseInt(this.getStyle("left"), 10) || 0;
7795             }
7796         },
7797
7798         /**
7799          * Gets the right X coordinate of the element (element X position + element width)
7800          * @param {Boolean} local True to get the local css position instead of page coordinate
7801          * @return {Number}
7802          */
7803         getRight : function(local){
7804             if(!local){
7805                 return this.getX() + this.getWidth();
7806             }else{
7807                 return (this.getLeft(true) + this.getWidth()) || 0;
7808             }
7809         },
7810
7811         /**
7812          * Gets the top Y coordinate
7813          * @param {Boolean} local True to get the local css position instead of page coordinate
7814          * @return {Number}
7815          */
7816         getTop : function(local) {
7817             if(!local){
7818                 return this.getY();
7819             }else{
7820                 return parseInt(this.getStyle("top"), 10) || 0;
7821             }
7822         },
7823
7824         /**
7825          * Gets the bottom Y coordinate of the element (element Y position + element height)
7826          * @param {Boolean} local True to get the local css position instead of page coordinate
7827          * @return {Number}
7828          */
7829         getBottom : function(local){
7830             if(!local){
7831                 return this.getY() + this.getHeight();
7832             }else{
7833                 return (this.getTop(true) + this.getHeight()) || 0;
7834             }
7835         },
7836
7837         /**
7838         * Initializes positioning on this element. If a desired position is not passed, it will make the
7839         * the element positioned relative IF it is not already positioned.
7840         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7841         * @param {Number} zIndex (optional) The zIndex to apply
7842         * @param {Number} x (optional) Set the page X position
7843         * @param {Number} y (optional) Set the page Y position
7844         */
7845         position : function(pos, zIndex, x, y){
7846             if(!pos){
7847                if(this.getStyle('position') == 'static'){
7848                    this.setStyle('position', 'relative');
7849                }
7850             }else{
7851                 this.setStyle("position", pos);
7852             }
7853             if(zIndex){
7854                 this.setStyle("z-index", zIndex);
7855             }
7856             if(x !== undefined && y !== undefined){
7857                 this.setXY([x, y]);
7858             }else if(x !== undefined){
7859                 this.setX(x);
7860             }else if(y !== undefined){
7861                 this.setY(y);
7862             }
7863         },
7864
7865         /**
7866         * Clear positioning back to the default when the document was loaded
7867         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7868         * @return {Roo.Element} this
7869          */
7870         clearPositioning : function(value){
7871             value = value ||'';
7872             this.setStyle({
7873                 "left": value,
7874                 "right": value,
7875                 "top": value,
7876                 "bottom": value,
7877                 "z-index": "",
7878                 "position" : "static"
7879             });
7880             return this;
7881         },
7882
7883         /**
7884         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7885         * snapshot before performing an update and then restoring the element.
7886         * @return {Object}
7887         */
7888         getPositioning : function(){
7889             var l = this.getStyle("left");
7890             var t = this.getStyle("top");
7891             return {
7892                 "position" : this.getStyle("position"),
7893                 "left" : l,
7894                 "right" : l ? "" : this.getStyle("right"),
7895                 "top" : t,
7896                 "bottom" : t ? "" : this.getStyle("bottom"),
7897                 "z-index" : this.getStyle("z-index")
7898             };
7899         },
7900
7901         /**
7902          * Gets the width of the border(s) for the specified side(s)
7903          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7904          * passing lr would get the border (l)eft width + the border (r)ight width.
7905          * @return {Number} The width of the sides passed added together
7906          */
7907         getBorderWidth : function(side){
7908             return this.addStyles(side, El.borders);
7909         },
7910
7911         /**
7912          * Gets the width of the padding(s) for the specified side(s)
7913          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7914          * passing lr would get the padding (l)eft + the padding (r)ight.
7915          * @return {Number} The padding of the sides passed added together
7916          */
7917         getPadding : function(side){
7918             return this.addStyles(side, El.paddings);
7919         },
7920
7921         /**
7922         * Set positioning with an object returned by getPositioning().
7923         * @param {Object} posCfg
7924         * @return {Roo.Element} this
7925          */
7926         setPositioning : function(pc){
7927             this.applyStyles(pc);
7928             if(pc.right == "auto"){
7929                 this.dom.style.right = "";
7930             }
7931             if(pc.bottom == "auto"){
7932                 this.dom.style.bottom = "";
7933             }
7934             return this;
7935         },
7936
7937         // private
7938         fixDisplay : function(){
7939             if(this.getStyle("display") == "none"){
7940                 this.setStyle("visibility", "hidden");
7941                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7942                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7943                     this.setStyle("display", "block");
7944                 }
7945             }
7946         },
7947
7948         /**
7949          * Quick set left and top adding default units
7950          * @param {String} left The left CSS property value
7951          * @param {String} top The top CSS property value
7952          * @return {Roo.Element} this
7953          */
7954          setLeftTop : function(left, top){
7955             this.dom.style.left = this.addUnits(left);
7956             this.dom.style.top = this.addUnits(top);
7957             return this;
7958         },
7959
7960         /**
7961          * Move this element relative to its current position.
7962          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7963          * @param {Number} distance How far to move the element in pixels
7964          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7965          * @return {Roo.Element} this
7966          */
7967          move : function(direction, distance, animate){
7968             var xy = this.getXY();
7969             direction = direction.toLowerCase();
7970             switch(direction){
7971                 case "l":
7972                 case "left":
7973                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7974                     break;
7975                case "r":
7976                case "right":
7977                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7978                     break;
7979                case "t":
7980                case "top":
7981                case "up":
7982                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7983                     break;
7984                case "b":
7985                case "bottom":
7986                case "down":
7987                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7988                     break;
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7995          * @return {Roo.Element} this
7996          */
7997         clip : function(){
7998             if(!this.isClipped){
7999                this.isClipped = true;
8000                this.originalClip = {
8001                    "o": this.getStyle("overflow"),
8002                    "x": this.getStyle("overflow-x"),
8003                    "y": this.getStyle("overflow-y")
8004                };
8005                this.setStyle("overflow", "hidden");
8006                this.setStyle("overflow-x", "hidden");
8007                this.setStyle("overflow-y", "hidden");
8008             }
8009             return this;
8010         },
8011
8012         /**
8013          *  Return clipping (overflow) to original clipping before clip() was called
8014          * @return {Roo.Element} this
8015          */
8016         unclip : function(){
8017             if(this.isClipped){
8018                 this.isClipped = false;
8019                 var o = this.originalClip;
8020                 if(o.o){this.setStyle("overflow", o.o);}
8021                 if(o.x){this.setStyle("overflow-x", o.x);}
8022                 if(o.y){this.setStyle("overflow-y", o.y);}
8023             }
8024             return this;
8025         },
8026
8027
8028         /**
8029          * Gets the x,y coordinates specified by the anchor position on the element.
8030          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8031          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8032          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8033          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8034          * @return {Array} [x, y] An array containing the element's x and y coordinates
8035          */
8036         getAnchorXY : function(anchor, local, s){
8037             //Passing a different size is useful for pre-calculating anchors,
8038             //especially for anchored animations that change the el size.
8039
8040             var w, h, vp = false;
8041             if(!s){
8042                 var d = this.dom;
8043                 if(d == document.body || d == document){
8044                     vp = true;
8045                     w = D.getViewWidth(); h = D.getViewHeight();
8046                 }else{
8047                     w = this.getWidth(); h = this.getHeight();
8048                 }
8049             }else{
8050                 w = s.width;  h = s.height;
8051             }
8052             var x = 0, y = 0, r = Math.round;
8053             switch((anchor || "tl").toLowerCase()){
8054                 case "c":
8055                     x = r(w*.5);
8056                     y = r(h*.5);
8057                 break;
8058                 case "t":
8059                     x = r(w*.5);
8060                     y = 0;
8061                 break;
8062                 case "l":
8063                     x = 0;
8064                     y = r(h*.5);
8065                 break;
8066                 case "r":
8067                     x = w;
8068                     y = r(h*.5);
8069                 break;
8070                 case "b":
8071                     x = r(w*.5);
8072                     y = h;
8073                 break;
8074                 case "tl":
8075                     x = 0;
8076                     y = 0;
8077                 break;
8078                 case "bl":
8079                     x = 0;
8080                     y = h;
8081                 break;
8082                 case "br":
8083                     x = w;
8084                     y = h;
8085                 break;
8086                 case "tr":
8087                     x = w;
8088                     y = 0;
8089                 break;
8090             }
8091             if(local === true){
8092                 return [x, y];
8093             }
8094             if(vp){
8095                 var sc = this.getScroll();
8096                 return [x + sc.left, y + sc.top];
8097             }
8098             //Add the element's offset xy
8099             var o = this.getXY();
8100             return [x+o[0], y+o[1]];
8101         },
8102
8103         /**
8104          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8105          * supported position values.
8106          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8107          * @param {String} position The position to align to.
8108          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8109          * @return {Array} [x, y]
8110          */
8111         getAlignToXY : function(el, p, o){
8112             el = Roo.get(el);
8113             var d = this.dom;
8114             if(!el.dom){
8115                 throw "Element.alignTo with an element that doesn't exist";
8116             }
8117             var c = false; //constrain to viewport
8118             var p1 = "", p2 = "";
8119             o = o || [0,0];
8120
8121             if(!p){
8122                 p = "tl-bl";
8123             }else if(p == "?"){
8124                 p = "tl-bl?";
8125             }else if(p.indexOf("-") == -1){
8126                 p = "tl-" + p;
8127             }
8128             p = p.toLowerCase();
8129             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8130             if(!m){
8131                throw "Element.alignTo with an invalid alignment " + p;
8132             }
8133             p1 = m[1]; p2 = m[2]; c = !!m[3];
8134
8135             //Subtract the aligned el's internal xy from the target's offset xy
8136             //plus custom offset to get the aligned el's new offset xy
8137             var a1 = this.getAnchorXY(p1, true);
8138             var a2 = el.getAnchorXY(p2, false);
8139             var x = a2[0] - a1[0] + o[0];
8140             var y = a2[1] - a1[1] + o[1];
8141             if(c){
8142                 //constrain the aligned el to viewport if necessary
8143                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8144                 // 5px of margin for ie
8145                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8146
8147                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8148                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8149                 //otherwise swap the aligned el to the opposite border of the target.
8150                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8151                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8152                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8153                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8154
8155                var doc = document;
8156                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8157                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8158
8159                if((x+w) > dw + scrollX){
8160                     x = swapX ? r.left-w : dw+scrollX-w;
8161                 }
8162                if(x < scrollX){
8163                    x = swapX ? r.right : scrollX;
8164                }
8165                if((y+h) > dh + scrollY){
8166                     y = swapY ? r.top-h : dh+scrollY-h;
8167                 }
8168                if (y < scrollY){
8169                    y = swapY ? r.bottom : scrollY;
8170                }
8171             }
8172             return [x,y];
8173         },
8174
8175         // private
8176         getConstrainToXY : function(){
8177             var os = {top:0, left:0, bottom:0, right: 0};
8178
8179             return function(el, local, offsets, proposedXY){
8180                 el = Roo.get(el);
8181                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8182
8183                 var vw, vh, vx = 0, vy = 0;
8184                 if(el.dom == document.body || el.dom == document){
8185                     vw = Roo.lib.Dom.getViewWidth();
8186                     vh = Roo.lib.Dom.getViewHeight();
8187                 }else{
8188                     vw = el.dom.clientWidth;
8189                     vh = el.dom.clientHeight;
8190                     if(!local){
8191                         var vxy = el.getXY();
8192                         vx = vxy[0];
8193                         vy = vxy[1];
8194                     }
8195                 }
8196
8197                 var s = el.getScroll();
8198
8199                 vx += offsets.left + s.left;
8200                 vy += offsets.top + s.top;
8201
8202                 vw -= offsets.right;
8203                 vh -= offsets.bottom;
8204
8205                 var vr = vx+vw;
8206                 var vb = vy+vh;
8207
8208                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8209                 var x = xy[0], y = xy[1];
8210                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8211
8212                 // only move it if it needs it
8213                 var moved = false;
8214
8215                 // first validate right/bottom
8216                 if((x + w) > vr){
8217                     x = vr - w;
8218                     moved = true;
8219                 }
8220                 if((y + h) > vb){
8221                     y = vb - h;
8222                     moved = true;
8223                 }
8224                 // then make sure top/left isn't negative
8225                 if(x < vx){
8226                     x = vx;
8227                     moved = true;
8228                 }
8229                 if(y < vy){
8230                     y = vy;
8231                     moved = true;
8232                 }
8233                 return moved ? [x, y] : false;
8234             };
8235         }(),
8236
8237         // private
8238         adjustForConstraints : function(xy, parent, offsets){
8239             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8240         },
8241
8242         /**
8243          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8244          * document it aligns it to the viewport.
8245          * The position parameter is optional, and can be specified in any one of the following formats:
8246          * <ul>
8247          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8248          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8249          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8250          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8251          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8252          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8253          * </ul>
8254          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8255          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8256          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8257          * that specified in order to enforce the viewport constraints.
8258          * Following are all of the supported anchor positions:
8259     <pre>
8260     Value  Description
8261     -----  -----------------------------
8262     tl     The top left corner (default)
8263     t      The center of the top edge
8264     tr     The top right corner
8265     l      The center of the left edge
8266     c      In the center of the element
8267     r      The center of the right edge
8268     bl     The bottom left corner
8269     b      The center of the bottom edge
8270     br     The bottom right corner
8271     </pre>
8272     Example Usage:
8273     <pre><code>
8274     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8275     el.alignTo("other-el");
8276
8277     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8278     el.alignTo("other-el", "tr?");
8279
8280     // align the bottom right corner of el with the center left edge of other-el
8281     el.alignTo("other-el", "br-l?");
8282
8283     // align the center of el with the bottom left corner of other-el and
8284     // adjust the x position by -6 pixels (and the y position by 0)
8285     el.alignTo("other-el", "c-bl", [-6, 0]);
8286     </code></pre>
8287          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8288          * @param {String} position The position to align to.
8289          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8290          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8291          * @return {Roo.Element} this
8292          */
8293         alignTo : function(element, position, offsets, animate){
8294             var xy = this.getAlignToXY(element, position, offsets);
8295             this.setXY(xy, this.preanim(arguments, 3));
8296             return this;
8297         },
8298
8299         /**
8300          * Anchors an element to another element and realigns it when the window is resized.
8301          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8302          * @param {String} position The position to align to.
8303          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8304          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8305          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8306          * is a number, it is used as the buffer delay (defaults to 50ms).
8307          * @param {Function} callback The function to call after the animation finishes
8308          * @return {Roo.Element} this
8309          */
8310         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8311             var action = function(){
8312                 this.alignTo(el, alignment, offsets, animate);
8313                 Roo.callback(callback, this);
8314             };
8315             Roo.EventManager.onWindowResize(action, this);
8316             var tm = typeof monitorScroll;
8317             if(tm != 'undefined'){
8318                 Roo.EventManager.on(window, 'scroll', action, this,
8319                     {buffer: tm == 'number' ? monitorScroll : 50});
8320             }
8321             action.call(this); // align immediately
8322             return this;
8323         },
8324         /**
8325          * Clears any opacity settings from this element. Required in some cases for IE.
8326          * @return {Roo.Element} this
8327          */
8328         clearOpacity : function(){
8329             if (window.ActiveXObject) {
8330                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8331                     this.dom.style.filter = "";
8332                 }
8333             } else {
8334                 this.dom.style.opacity = "";
8335                 this.dom.style["-moz-opacity"] = "";
8336                 this.dom.style["-khtml-opacity"] = "";
8337             }
8338             return this;
8339         },
8340
8341         /**
8342          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8343          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8344          * @return {Roo.Element} this
8345          */
8346         hide : function(animate){
8347             this.setVisible(false, this.preanim(arguments, 0));
8348             return this;
8349         },
8350
8351         /**
8352         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8353         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8354          * @return {Roo.Element} this
8355          */
8356         show : function(animate){
8357             this.setVisible(true, this.preanim(arguments, 0));
8358             return this;
8359         },
8360
8361         /**
8362          * @private Test if size has a unit, otherwise appends the default
8363          */
8364         addUnits : function(size){
8365             return Roo.Element.addUnits(size, this.defaultUnit);
8366         },
8367
8368         /**
8369          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8370          * @return {Roo.Element} this
8371          */
8372         beginMeasure : function(){
8373             var el = this.dom;
8374             if(el.offsetWidth || el.offsetHeight){
8375                 return this; // offsets work already
8376             }
8377             var changed = [];
8378             var p = this.dom, b = document.body; // start with this element
8379             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8380                 var pe = Roo.get(p);
8381                 if(pe.getStyle('display') == 'none'){
8382                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8383                     p.style.visibility = "hidden";
8384                     p.style.display = "block";
8385                 }
8386                 p = p.parentNode;
8387             }
8388             this._measureChanged = changed;
8389             return this;
8390
8391         },
8392
8393         /**
8394          * Restores displays to before beginMeasure was called
8395          * @return {Roo.Element} this
8396          */
8397         endMeasure : function(){
8398             var changed = this._measureChanged;
8399             if(changed){
8400                 for(var i = 0, len = changed.length; i < len; i++) {
8401                     var r = changed[i];
8402                     r.el.style.visibility = r.visibility;
8403                     r.el.style.display = "none";
8404                 }
8405                 this._measureChanged = null;
8406             }
8407             return this;
8408         },
8409
8410         /**
8411         * Update the innerHTML of this element, optionally searching for and processing scripts
8412         * @param {String} html The new HTML
8413         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8414         * @param {Function} callback For async script loading you can be noticed when the update completes
8415         * @return {Roo.Element} this
8416          */
8417         update : function(html, loadScripts, callback){
8418             if(typeof html == "undefined"){
8419                 html = "";
8420             }
8421             if(loadScripts !== true){
8422                 this.dom.innerHTML = html;
8423                 if(typeof callback == "function"){
8424                     callback();
8425                 }
8426                 return this;
8427             }
8428             var id = Roo.id();
8429             var dom = this.dom;
8430
8431             html += '<span id="' + id + '"></span>';
8432
8433             E.onAvailable(id, function(){
8434                 var hd = document.getElementsByTagName("head")[0];
8435                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8436                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8437                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8438
8439                 var match;
8440                 while(match = re.exec(html)){
8441                     var attrs = match[1];
8442                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8443                     if(srcMatch && srcMatch[2]){
8444                        var s = document.createElement("script");
8445                        s.src = srcMatch[2];
8446                        var typeMatch = attrs.match(typeRe);
8447                        if(typeMatch && typeMatch[2]){
8448                            s.type = typeMatch[2];
8449                        }
8450                        hd.appendChild(s);
8451                     }else if(match[2] && match[2].length > 0){
8452                         if(window.execScript) {
8453                            window.execScript(match[2]);
8454                         } else {
8455                             /**
8456                              * eval:var:id
8457                              * eval:var:dom
8458                              * eval:var:html
8459                              * 
8460                              */
8461                            window.eval(match[2]);
8462                         }
8463                     }
8464                 }
8465                 var el = document.getElementById(id);
8466                 if(el){el.parentNode.removeChild(el);}
8467                 if(typeof callback == "function"){
8468                     callback();
8469                 }
8470             });
8471             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8472             return this;
8473         },
8474
8475         /**
8476          * Direct access to the UpdateManager update() method (takes the same parameters).
8477          * @param {String/Function} url The url for this request or a function to call to get the url
8478          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8479          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8480          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8481          * @return {Roo.Element} this
8482          */
8483         load : function(){
8484             var um = this.getUpdateManager();
8485             um.update.apply(um, arguments);
8486             return this;
8487         },
8488
8489         /**
8490         * Gets this element's UpdateManager
8491         * @return {Roo.UpdateManager} The UpdateManager
8492         */
8493         getUpdateManager : function(){
8494             if(!this.updateManager){
8495                 this.updateManager = new Roo.UpdateManager(this);
8496             }
8497             return this.updateManager;
8498         },
8499
8500         /**
8501          * Disables text selection for this element (normalized across browsers)
8502          * @return {Roo.Element} this
8503          */
8504         unselectable : function(){
8505             this.dom.unselectable = "on";
8506             this.swallowEvent("selectstart", true);
8507             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8508             this.addClass("x-unselectable");
8509             return this;
8510         },
8511
8512         /**
8513         * Calculates the x, y to center this element on the screen
8514         * @return {Array} The x, y values [x, y]
8515         */
8516         getCenterXY : function(){
8517             return this.getAlignToXY(document, 'c-c');
8518         },
8519
8520         /**
8521         * Centers the Element in either the viewport, or another Element.
8522         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8523         */
8524         center : function(centerIn){
8525             this.alignTo(centerIn || document, 'c-c');
8526             return this;
8527         },
8528
8529         /**
8530          * Tests various css rules/browsers to determine if this element uses a border box
8531          * @return {Boolean}
8532          */
8533         isBorderBox : function(){
8534             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8535         },
8536
8537         /**
8538          * Return a box {x, y, width, height} that can be used to set another elements
8539          * size/location to match this element.
8540          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8541          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8542          * @return {Object} box An object in the format {x, y, width, height}
8543          */
8544         getBox : function(contentBox, local){
8545             var xy;
8546             if(!local){
8547                 xy = this.getXY();
8548             }else{
8549                 var left = parseInt(this.getStyle("left"), 10) || 0;
8550                 var top = parseInt(this.getStyle("top"), 10) || 0;
8551                 xy = [left, top];
8552             }
8553             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8554             if(!contentBox){
8555                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8556             }else{
8557                 var l = this.getBorderWidth("l")+this.getPadding("l");
8558                 var r = this.getBorderWidth("r")+this.getPadding("r");
8559                 var t = this.getBorderWidth("t")+this.getPadding("t");
8560                 var b = this.getBorderWidth("b")+this.getPadding("b");
8561                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8562             }
8563             bx.right = bx.x + bx.width;
8564             bx.bottom = bx.y + bx.height;
8565             return bx;
8566         },
8567
8568         /**
8569          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8570          for more information about the sides.
8571          * @param {String} sides
8572          * @return {Number}
8573          */
8574         getFrameWidth : function(sides, onlyContentBox){
8575             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8576         },
8577
8578         /**
8579          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8580          * @param {Object} box The box to fill {x, y, width, height}
8581          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8582          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8583          * @return {Roo.Element} this
8584          */
8585         setBox : function(box, adjust, animate){
8586             var w = box.width, h = box.height;
8587             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8588                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8589                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8590             }
8591             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8592             return this;
8593         },
8594
8595         /**
8596          * Forces the browser to repaint this element
8597          * @return {Roo.Element} this
8598          */
8599          repaint : function(){
8600             var dom = this.dom;
8601             this.addClass("x-repaint");
8602             setTimeout(function(){
8603                 Roo.get(dom).removeClass("x-repaint");
8604             }, 1);
8605             return this;
8606         },
8607
8608         /**
8609          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8610          * then it returns the calculated width of the sides (see getPadding)
8611          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8612          * @return {Object/Number}
8613          */
8614         getMargins : function(side){
8615             if(!side){
8616                 return {
8617                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8618                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8619                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8620                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8621                 };
8622             }else{
8623                 return this.addStyles(side, El.margins);
8624              }
8625         },
8626
8627         // private
8628         addStyles : function(sides, styles){
8629             var val = 0, v, w;
8630             for(var i = 0, len = sides.length; i < len; i++){
8631                 v = this.getStyle(styles[sides.charAt(i)]);
8632                 if(v){
8633                      w = parseInt(v, 10);
8634                      if(w){ val += w; }
8635                 }
8636             }
8637             return val;
8638         },
8639
8640         /**
8641          * Creates a proxy element of this element
8642          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8643          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8644          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8645          * @return {Roo.Element} The new proxy element
8646          */
8647         createProxy : function(config, renderTo, matchBox){
8648             if(renderTo){
8649                 renderTo = Roo.getDom(renderTo);
8650             }else{
8651                 renderTo = document.body;
8652             }
8653             config = typeof config == "object" ?
8654                 config : {tag : "div", cls: config};
8655             var proxy = Roo.DomHelper.append(renderTo, config, true);
8656             if(matchBox){
8657                proxy.setBox(this.getBox());
8658             }
8659             return proxy;
8660         },
8661
8662         /**
8663          * Puts a mask over this element to disable user interaction. Requires core.css.
8664          * This method can only be applied to elements which accept child nodes.
8665          * @param {String} msg (optional) A message to display in the mask
8666          * @param {String} msgCls (optional) A css class to apply to the msg element
8667          * @return {Element} The mask  element
8668          */
8669         mask : function(msg, msgCls){
8670             if(this.getStyle("position") == "static"){
8671                 this.setStyle("position", "relative");
8672             }
8673             if(!this._mask){
8674                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8675             }
8676             this.addClass("x-masked");
8677             this._mask.setDisplayed(true);
8678             if(typeof msg == 'string'){
8679                 if(!this._maskMsg){
8680                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8681                 }
8682                 var mm = this._maskMsg;
8683                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8684                 mm.dom.firstChild.innerHTML = msg;
8685                 mm.setDisplayed(true);
8686                 mm.center(this);
8687             }
8688             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8689                 this._mask.setHeight(this.getHeight());
8690             }
8691             return this._mask;
8692         },
8693
8694         /**
8695          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8696          * it is cached for reuse.
8697          */
8698         unmask : function(removeEl){
8699             if(this._mask){
8700                 if(removeEl === true){
8701                     this._mask.remove();
8702                     delete this._mask;
8703                     if(this._maskMsg){
8704                         this._maskMsg.remove();
8705                         delete this._maskMsg;
8706                     }
8707                 }else{
8708                     this._mask.setDisplayed(false);
8709                     if(this._maskMsg){
8710                         this._maskMsg.setDisplayed(false);
8711                     }
8712                 }
8713             }
8714             this.removeClass("x-masked");
8715         },
8716
8717         /**
8718          * Returns true if this element is masked
8719          * @return {Boolean}
8720          */
8721         isMasked : function(){
8722             return this._mask && this._mask.isVisible();
8723         },
8724
8725         /**
8726          * Creates an iframe shim for this element to keep selects and other windowed objects from
8727          * showing through.
8728          * @return {Roo.Element} The new shim element
8729          */
8730         createShim : function(){
8731             var el = document.createElement('iframe');
8732             el.frameBorder = 'no';
8733             el.className = 'roo-shim';
8734             if(Roo.isIE && Roo.isSecure){
8735                 el.src = Roo.SSL_SECURE_URL;
8736             }
8737             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8738             shim.autoBoxAdjust = false;
8739             return shim;
8740         },
8741
8742         /**
8743          * Removes this element from the DOM and deletes it from the cache
8744          */
8745         remove : function(){
8746             if(this.dom.parentNode){
8747                 this.dom.parentNode.removeChild(this.dom);
8748             }
8749             delete El.cache[this.dom.id];
8750         },
8751
8752         /**
8753          * Sets up event handlers to add and remove a css class when the mouse is over this element
8754          * @param {String} className
8755          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8756          * mouseout events for children elements
8757          * @return {Roo.Element} this
8758          */
8759         addClassOnOver : function(className, preventFlicker){
8760             this.on("mouseover", function(){
8761                 Roo.fly(this, '_internal').addClass(className);
8762             }, this.dom);
8763             var removeFn = function(e){
8764                 if(preventFlicker !== true || !e.within(this, true)){
8765                     Roo.fly(this, '_internal').removeClass(className);
8766                 }
8767             };
8768             this.on("mouseout", removeFn, this.dom);
8769             return this;
8770         },
8771
8772         /**
8773          * Sets up event handlers to add and remove a css class when this element has the focus
8774          * @param {String} className
8775          * @return {Roo.Element} this
8776          */
8777         addClassOnFocus : function(className){
8778             this.on("focus", function(){
8779                 Roo.fly(this, '_internal').addClass(className);
8780             }, this.dom);
8781             this.on("blur", function(){
8782                 Roo.fly(this, '_internal').removeClass(className);
8783             }, this.dom);
8784             return this;
8785         },
8786         /**
8787          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8788          * @param {String} className
8789          * @return {Roo.Element} this
8790          */
8791         addClassOnClick : function(className){
8792             var dom = this.dom;
8793             this.on("mousedown", function(){
8794                 Roo.fly(dom, '_internal').addClass(className);
8795                 var d = Roo.get(document);
8796                 var fn = function(){
8797                     Roo.fly(dom, '_internal').removeClass(className);
8798                     d.removeListener("mouseup", fn);
8799                 };
8800                 d.on("mouseup", fn);
8801             });
8802             return this;
8803         },
8804
8805         /**
8806          * Stops the specified event from bubbling and optionally prevents the default action
8807          * @param {String} eventName
8808          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8809          * @return {Roo.Element} this
8810          */
8811         swallowEvent : function(eventName, preventDefault){
8812             var fn = function(e){
8813                 e.stopPropagation();
8814                 if(preventDefault){
8815                     e.preventDefault();
8816                 }
8817             };
8818             if(eventName instanceof Array){
8819                 for(var i = 0, len = eventName.length; i < len; i++){
8820                      this.on(eventName[i], fn);
8821                 }
8822                 return this;
8823             }
8824             this.on(eventName, fn);
8825             return this;
8826         },
8827
8828         /**
8829          * @private
8830          */
8831       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8832
8833         /**
8834          * Sizes this element to its parent element's dimensions performing
8835          * neccessary box adjustments.
8836          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8837          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8838          * @return {Roo.Element} this
8839          */
8840         fitToParent : function(monitorResize, targetParent) {
8841           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8842           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8843           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8844             return;
8845           }
8846           var p = Roo.get(targetParent || this.dom.parentNode);
8847           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8848           if (monitorResize === true) {
8849             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8850             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8851           }
8852           return this;
8853         },
8854
8855         /**
8856          * Gets the next sibling, skipping text nodes
8857          * @return {HTMLElement} The next sibling or null
8858          */
8859         getNextSibling : function(){
8860             var n = this.dom.nextSibling;
8861             while(n && n.nodeType != 1){
8862                 n = n.nextSibling;
8863             }
8864             return n;
8865         },
8866
8867         /**
8868          * Gets the previous sibling, skipping text nodes
8869          * @return {HTMLElement} The previous sibling or null
8870          */
8871         getPrevSibling : function(){
8872             var n = this.dom.previousSibling;
8873             while(n && n.nodeType != 1){
8874                 n = n.previousSibling;
8875             }
8876             return n;
8877         },
8878
8879
8880         /**
8881          * Appends the passed element(s) to this element
8882          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8883          * @return {Roo.Element} this
8884          */
8885         appendChild: function(el){
8886             el = Roo.get(el);
8887             el.appendTo(this);
8888             return this;
8889         },
8890
8891         /**
8892          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8893          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8894          * automatically generated with the specified attributes.
8895          * @param {HTMLElement} insertBefore (optional) a child element of this element
8896          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8897          * @return {Roo.Element} The new child element
8898          */
8899         createChild: function(config, insertBefore, returnDom){
8900             config = config || {tag:'div'};
8901             if(insertBefore){
8902                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8903             }
8904             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8905         },
8906
8907         /**
8908          * Appends this element to the passed element
8909          * @param {String/HTMLElement/Element} el The new parent element
8910          * @return {Roo.Element} this
8911          */
8912         appendTo: function(el){
8913             el = Roo.getDom(el);
8914             el.appendChild(this.dom);
8915             return this;
8916         },
8917
8918         /**
8919          * Inserts this element before the passed element in the DOM
8920          * @param {String/HTMLElement/Element} el The element to insert before
8921          * @return {Roo.Element} this
8922          */
8923         insertBefore: function(el){
8924             el = Roo.getDom(el);
8925             el.parentNode.insertBefore(this.dom, el);
8926             return this;
8927         },
8928
8929         /**
8930          * Inserts this element after the passed element in the DOM
8931          * @param {String/HTMLElement/Element} el The element to insert after
8932          * @return {Roo.Element} this
8933          */
8934         insertAfter: function(el){
8935             el = Roo.getDom(el);
8936             el.parentNode.insertBefore(this.dom, el.nextSibling);
8937             return this;
8938         },
8939
8940         /**
8941          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8942          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8943          * @return {Roo.Element} The new child
8944          */
8945         insertFirst: function(el, returnDom){
8946             el = el || {};
8947             if(typeof el == 'object' && !el.nodeType){ // dh config
8948                 return this.createChild(el, this.dom.firstChild, returnDom);
8949             }else{
8950                 el = Roo.getDom(el);
8951                 this.dom.insertBefore(el, this.dom.firstChild);
8952                 return !returnDom ? Roo.get(el) : el;
8953             }
8954         },
8955
8956         /**
8957          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8958          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8959          * @param {String} where (optional) 'before' or 'after' defaults to before
8960          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8961          * @return {Roo.Element} the inserted Element
8962          */
8963         insertSibling: function(el, where, returnDom){
8964             where = where ? where.toLowerCase() : 'before';
8965             el = el || {};
8966             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8967
8968             if(typeof el == 'object' && !el.nodeType){ // dh config
8969                 if(where == 'after' && !this.dom.nextSibling){
8970                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8971                 }else{
8972                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8973                 }
8974
8975             }else{
8976                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8977                             where == 'before' ? this.dom : this.dom.nextSibling);
8978                 if(!returnDom){
8979                     rt = Roo.get(rt);
8980                 }
8981             }
8982             return rt;
8983         },
8984
8985         /**
8986          * Creates and wraps this element with another element
8987          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8988          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8989          * @return {HTMLElement/Element} The newly created wrapper element
8990          */
8991         wrap: function(config, returnDom){
8992             if(!config){
8993                 config = {tag: "div"};
8994             }
8995             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8996             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8997             return newEl;
8998         },
8999
9000         /**
9001          * Replaces the passed element with this element
9002          * @param {String/HTMLElement/Element} el The element to replace
9003          * @return {Roo.Element} this
9004          */
9005         replace: function(el){
9006             el = Roo.get(el);
9007             this.insertBefore(el);
9008             el.remove();
9009             return this;
9010         },
9011
9012         /**
9013          * Inserts an html fragment into this element
9014          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9015          * @param {String} html The HTML fragment
9016          * @param {Boolean} returnEl True to return an Roo.Element
9017          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9018          */
9019         insertHtml : function(where, html, returnEl){
9020             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9021             return returnEl ? Roo.get(el) : el;
9022         },
9023
9024         /**
9025          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9026          * @param {Object} o The object with the attributes
9027          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9028          * @return {Roo.Element} this
9029          */
9030         set : function(o, useSet){
9031             var el = this.dom;
9032             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9033             for(var attr in o){
9034                 if(attr == "style" || typeof o[attr] == "function") continue;
9035                 if(attr=="cls"){
9036                     el.className = o["cls"];
9037                 }else{
9038                     if(useSet) el.setAttribute(attr, o[attr]);
9039                     else el[attr] = o[attr];
9040                 }
9041             }
9042             if(o.style){
9043                 Roo.DomHelper.applyStyles(el, o.style);
9044             }
9045             return this;
9046         },
9047
9048         /**
9049          * Convenience method for constructing a KeyMap
9050          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9051          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9052          * @param {Function} fn The function to call
9053          * @param {Object} scope (optional) The scope of the function
9054          * @return {Roo.KeyMap} The KeyMap created
9055          */
9056         addKeyListener : function(key, fn, scope){
9057             var config;
9058             if(typeof key != "object" || key instanceof Array){
9059                 config = {
9060                     key: key,
9061                     fn: fn,
9062                     scope: scope
9063                 };
9064             }else{
9065                 config = {
9066                     key : key.key,
9067                     shift : key.shift,
9068                     ctrl : key.ctrl,
9069                     alt : key.alt,
9070                     fn: fn,
9071                     scope: scope
9072                 };
9073             }
9074             return new Roo.KeyMap(this, config);
9075         },
9076
9077         /**
9078          * Creates a KeyMap for this element
9079          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9080          * @return {Roo.KeyMap} The KeyMap created
9081          */
9082         addKeyMap : function(config){
9083             return new Roo.KeyMap(this, config);
9084         },
9085
9086         /**
9087          * Returns true if this element is scrollable.
9088          * @return {Boolean}
9089          */
9090          isScrollable : function(){
9091             var dom = this.dom;
9092             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9093         },
9094
9095         /**
9096          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9097          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9098          * @param {Number} value The new scroll value
9099          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9100          * @return {Element} this
9101          */
9102
9103         scrollTo : function(side, value, animate){
9104             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9105             if(!animate || !A){
9106                 this.dom[prop] = value;
9107             }else{
9108                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9109                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9110             }
9111             return this;
9112         },
9113
9114         /**
9115          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9116          * within this element's scrollable range.
9117          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9118          * @param {Number} distance How far to scroll the element in pixels
9119          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9120          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9121          * was scrolled as far as it could go.
9122          */
9123          scroll : function(direction, distance, animate){
9124              if(!this.isScrollable()){
9125                  return;
9126              }
9127              var el = this.dom;
9128              var l = el.scrollLeft, t = el.scrollTop;
9129              var w = el.scrollWidth, h = el.scrollHeight;
9130              var cw = el.clientWidth, ch = el.clientHeight;
9131              direction = direction.toLowerCase();
9132              var scrolled = false;
9133              var a = this.preanim(arguments, 2);
9134              switch(direction){
9135                  case "l":
9136                  case "left":
9137                      if(w - l > cw){
9138                          var v = Math.min(l + distance, w-cw);
9139                          this.scrollTo("left", v, a);
9140                          scrolled = true;
9141                      }
9142                      break;
9143                 case "r":
9144                 case "right":
9145                      if(l > 0){
9146                          var v = Math.max(l - distance, 0);
9147                          this.scrollTo("left", v, a);
9148                          scrolled = true;
9149                      }
9150                      break;
9151                 case "t":
9152                 case "top":
9153                 case "up":
9154                      if(t > 0){
9155                          var v = Math.max(t - distance, 0);
9156                          this.scrollTo("top", v, a);
9157                          scrolled = true;
9158                      }
9159                      break;
9160                 case "b":
9161                 case "bottom":
9162                 case "down":
9163                      if(h - t > ch){
9164                          var v = Math.min(t + distance, h-ch);
9165                          this.scrollTo("top", v, a);
9166                          scrolled = true;
9167                      }
9168                      break;
9169              }
9170              return scrolled;
9171         },
9172
9173         /**
9174          * Translates the passed page coordinates into left/top css values for this element
9175          * @param {Number/Array} x The page x or an array containing [x, y]
9176          * @param {Number} y The page y
9177          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9178          */
9179         translatePoints : function(x, y){
9180             if(typeof x == 'object' || x instanceof Array){
9181                 y = x[1]; x = x[0];
9182             }
9183             var p = this.getStyle('position');
9184             var o = this.getXY();
9185
9186             var l = parseInt(this.getStyle('left'), 10);
9187             var t = parseInt(this.getStyle('top'), 10);
9188
9189             if(isNaN(l)){
9190                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9191             }
9192             if(isNaN(t)){
9193                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9194             }
9195
9196             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9197         },
9198
9199         /**
9200          * Returns the current scroll position of the element.
9201          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9202          */
9203         getScroll : function(){
9204             var d = this.dom, doc = document;
9205             if(d == doc || d == doc.body){
9206                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9207                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9208                 return {left: l, top: t};
9209             }else{
9210                 return {left: d.scrollLeft, top: d.scrollTop};
9211             }
9212         },
9213
9214         /**
9215          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9216          * are convert to standard 6 digit hex color.
9217          * @param {String} attr The css attribute
9218          * @param {String} defaultValue The default value to use when a valid color isn't found
9219          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9220          * YUI color anims.
9221          */
9222         getColor : function(attr, defaultValue, prefix){
9223             var v = this.getStyle(attr);
9224             if(!v || v == "transparent" || v == "inherit") {
9225                 return defaultValue;
9226             }
9227             var color = typeof prefix == "undefined" ? "#" : prefix;
9228             if(v.substr(0, 4) == "rgb("){
9229                 var rvs = v.slice(4, v.length -1).split(",");
9230                 for(var i = 0; i < 3; i++){
9231                     var h = parseInt(rvs[i]).toString(16);
9232                     if(h < 16){
9233                         h = "0" + h;
9234                     }
9235                     color += h;
9236                 }
9237             } else {
9238                 if(v.substr(0, 1) == "#"){
9239                     if(v.length == 4) {
9240                         for(var i = 1; i < 4; i++){
9241                             var c = v.charAt(i);
9242                             color +=  c + c;
9243                         }
9244                     }else if(v.length == 7){
9245                         color += v.substr(1);
9246                     }
9247                 }
9248             }
9249             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9250         },
9251
9252         /**
9253          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9254          * gradient background, rounded corners and a 4-way shadow.
9255          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9256          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9257          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9258          * @return {Roo.Element} this
9259          */
9260         boxWrap : function(cls){
9261             cls = cls || 'x-box';
9262             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9263             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9264             return el;
9265         },
9266
9267         /**
9268          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9269          * @param {String} namespace The namespace in which to look for the attribute
9270          * @param {String} name The attribute name
9271          * @return {String} The attribute value
9272          */
9273         getAttributeNS : Roo.isIE ? function(ns, name){
9274             var d = this.dom;
9275             var type = typeof d[ns+":"+name];
9276             if(type != 'undefined' && type != 'unknown'){
9277                 return d[ns+":"+name];
9278             }
9279             return d[name];
9280         } : function(ns, name){
9281             var d = this.dom;
9282             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9283         }
9284     };
9285
9286     var ep = El.prototype;
9287
9288     /**
9289      * Appends an event handler (Shorthand for addListener)
9290      * @param {String}   eventName     The type of event to append
9291      * @param {Function} fn        The method the event invokes
9292      * @param {Object} scope       (optional) The scope (this object) of the fn
9293      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9294      * @method
9295      */
9296     ep.on = ep.addListener;
9297         // backwards compat
9298     ep.mon = ep.addListener;
9299
9300     /**
9301      * Removes an event handler from this element (shorthand for removeListener)
9302      * @param {String} eventName the type of event to remove
9303      * @param {Function} fn the method the event invokes
9304      * @return {Roo.Element} this
9305      * @method
9306      */
9307     ep.un = ep.removeListener;
9308
9309     /**
9310      * true to automatically adjust width and height settings for box-model issues (default to true)
9311      */
9312     ep.autoBoxAdjust = true;
9313
9314     // private
9315     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9316
9317     // private
9318     El.addUnits = function(v, defaultUnit){
9319         if(v === "" || v == "auto"){
9320             return v;
9321         }
9322         if(v === undefined){
9323             return '';
9324         }
9325         if(typeof v == "number" || !El.unitPattern.test(v)){
9326             return v + (defaultUnit || 'px');
9327         }
9328         return v;
9329     };
9330
9331     // special markup used throughout Roo when box wrapping elements
9332     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9333     /**
9334      * Visibility mode constant - Use visibility to hide element
9335      * @static
9336      * @type Number
9337      */
9338     El.VISIBILITY = 1;
9339     /**
9340      * Visibility mode constant - Use display to hide element
9341      * @static
9342      * @type Number
9343      */
9344     El.DISPLAY = 2;
9345
9346     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9347     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9348     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9349
9350
9351
9352     /**
9353      * @private
9354      */
9355     El.cache = {};
9356
9357     var docEl;
9358
9359     /**
9360      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9361      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9362      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9363      * @return {Element} The Element object
9364      * @static
9365      */
9366     El.get = function(el){
9367         var ex, elm, id;
9368         if(!el){ return null; }
9369         if(typeof el == "string"){ // element id
9370             if(!(elm = document.getElementById(el))){
9371                 return null;
9372             }
9373             if(ex = El.cache[el]){
9374                 ex.dom = elm;
9375             }else{
9376                 ex = El.cache[el] = new El(elm);
9377             }
9378             return ex;
9379         }else if(el.tagName){ // dom element
9380             if(!(id = el.id)){
9381                 id = Roo.id(el);
9382             }
9383             if(ex = El.cache[id]){
9384                 ex.dom = el;
9385             }else{
9386                 ex = El.cache[id] = new El(el);
9387             }
9388             return ex;
9389         }else if(el instanceof El){
9390             if(el != docEl){
9391                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9392                                                               // catch case where it hasn't been appended
9393                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9394             }
9395             return el;
9396         }else if(el.isComposite){
9397             return el;
9398         }else if(el instanceof Array){
9399             return El.select(el);
9400         }else if(el == document){
9401             // create a bogus element object representing the document object
9402             if(!docEl){
9403                 var f = function(){};
9404                 f.prototype = El.prototype;
9405                 docEl = new f();
9406                 docEl.dom = document;
9407             }
9408             return docEl;
9409         }
9410         return null;
9411     };
9412
9413     // private
9414     El.uncache = function(el){
9415         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9416             if(a[i]){
9417                 delete El.cache[a[i].id || a[i]];
9418             }
9419         }
9420     };
9421
9422     // private
9423     // Garbage collection - uncache elements/purge listeners on orphaned elements
9424     // so we don't hold a reference and cause the browser to retain them
9425     El.garbageCollect = function(){
9426         if(!Roo.enableGarbageCollector){
9427             clearInterval(El.collectorThread);
9428             return;
9429         }
9430         for(var eid in El.cache){
9431             var el = El.cache[eid], d = el.dom;
9432             // -------------------------------------------------------
9433             // Determining what is garbage:
9434             // -------------------------------------------------------
9435             // !d
9436             // dom node is null, definitely garbage
9437             // -------------------------------------------------------
9438             // !d.parentNode
9439             // no parentNode == direct orphan, definitely garbage
9440             // -------------------------------------------------------
9441             // !d.offsetParent && !document.getElementById(eid)
9442             // display none elements have no offsetParent so we will
9443             // also try to look it up by it's id. However, check
9444             // offsetParent first so we don't do unneeded lookups.
9445             // This enables collection of elements that are not orphans
9446             // directly, but somewhere up the line they have an orphan
9447             // parent.
9448             // -------------------------------------------------------
9449             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9450                 delete El.cache[eid];
9451                 if(d && Roo.enableListenerCollection){
9452                     E.purgeElement(d);
9453                 }
9454             }
9455         }
9456     }
9457     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9458
9459
9460     // dom is optional
9461     El.Flyweight = function(dom){
9462         this.dom = dom;
9463     };
9464     El.Flyweight.prototype = El.prototype;
9465
9466     El._flyweights = {};
9467     /**
9468      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9469      * the dom node can be overwritten by other code.
9470      * @param {String/HTMLElement} el The dom node or id
9471      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9472      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9473      * @static
9474      * @return {Element} The shared Element object
9475      */
9476     El.fly = function(el, named){
9477         named = named || '_global';
9478         el = Roo.getDom(el);
9479         if(!el){
9480             return null;
9481         }
9482         if(!El._flyweights[named]){
9483             El._flyweights[named] = new El.Flyweight();
9484         }
9485         El._flyweights[named].dom = el;
9486         return El._flyweights[named];
9487     };
9488
9489     /**
9490      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9491      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9492      * Shorthand of {@link Roo.Element#get}
9493      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9494      * @return {Element} The Element object
9495      * @member Roo
9496      * @method get
9497      */
9498     Roo.get = El.get;
9499     /**
9500      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9501      * the dom node can be overwritten by other code.
9502      * Shorthand of {@link Roo.Element#fly}
9503      * @param {String/HTMLElement} el The dom node or id
9504      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9505      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9506      * @static
9507      * @return {Element} The shared Element object
9508      * @member Roo
9509      * @method fly
9510      */
9511     Roo.fly = El.fly;
9512
9513     // speedy lookup for elements never to box adjust
9514     var noBoxAdjust = Roo.isStrict ? {
9515         select:1
9516     } : {
9517         input:1, select:1, textarea:1
9518     };
9519     if(Roo.isIE || Roo.isGecko){
9520         noBoxAdjust['button'] = 1;
9521     }
9522
9523
9524     Roo.EventManager.on(window, 'unload', function(){
9525         delete El.cache;
9526         delete El._flyweights;
9527     });
9528 })();
9529
9530
9531
9532
9533 if(Roo.DomQuery){
9534     Roo.Element.selectorFunction = Roo.DomQuery.select;
9535 }
9536
9537 Roo.Element.select = function(selector, unique, root){
9538     var els;
9539     if(typeof selector == "string"){
9540         els = Roo.Element.selectorFunction(selector, root);
9541     }else if(selector.length !== undefined){
9542         els = selector;
9543     }else{
9544         throw "Invalid selector";
9545     }
9546     if(unique === true){
9547         return new Roo.CompositeElement(els);
9548     }else{
9549         return new Roo.CompositeElementLite(els);
9550     }
9551 };
9552 /**
9553  * Selects elements based on the passed CSS selector to enable working on them as 1.
9554  * @param {String/Array} selector The CSS selector or an array of elements
9555  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9556  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9557  * @return {CompositeElementLite/CompositeElement}
9558  * @member Roo
9559  * @method select
9560  */
9561 Roo.select = Roo.Element.select;
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576 /*
9577  * Based on:
9578  * Ext JS Library 1.1.1
9579  * Copyright(c) 2006-2007, Ext JS, LLC.
9580  *
9581  * Originally Released Under LGPL - original licence link has changed is not relivant.
9582  *
9583  * Fork - LGPL
9584  * <script type="text/javascript">
9585  */
9586
9587
9588
9589 //Notifies Element that fx methods are available
9590 Roo.enableFx = true;
9591
9592 /**
9593  * @class Roo.Fx
9594  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9595  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9596  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9597  * Element effects to work.</p><br/>
9598  *
9599  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9600  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9601  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9602  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9603  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9604  * expected results and should be done with care.</p><br/>
9605  *
9606  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9607  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9608 <pre>
9609 Value  Description
9610 -----  -----------------------------
9611 tl     The top left corner
9612 t      The center of the top edge
9613 tr     The top right corner
9614 l      The center of the left edge
9615 r      The center of the right edge
9616 bl     The bottom left corner
9617 b      The center of the bottom edge
9618 br     The bottom right corner
9619 </pre>
9620  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9621  * below are common options that can be passed to any Fx method.</b>
9622  * @cfg {Function} callback A function called when the effect is finished
9623  * @cfg {Object} scope The scope of the effect function
9624  * @cfg {String} easing A valid Easing value for the effect
9625  * @cfg {String} afterCls A css class to apply after the effect
9626  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9627  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9628  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9629  * effects that end with the element being visually hidden, ignored otherwise)
9630  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9631  * a function which returns such a specification that will be applied to the Element after the effect finishes
9632  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9633  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9634  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9635  */
9636 Roo.Fx = {
9637         /**
9638          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9639          * origin for the slide effect.  This function automatically handles wrapping the element with
9640          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9641          * Usage:
9642          *<pre><code>
9643 // default: slide the element in from the top
9644 el.slideIn();
9645
9646 // custom: slide the element in from the right with a 2-second duration
9647 el.slideIn('r', { duration: 2 });
9648
9649 // common config options shown with default values
9650 el.slideIn('t', {
9651     easing: 'easeOut',
9652     duration: .5
9653 });
9654 </code></pre>
9655          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9656          * @param {Object} options (optional) Object literal with any of the Fx config options
9657          * @return {Roo.Element} The Element
9658          */
9659     slideIn : function(anchor, o){
9660         var el = this.getFxEl();
9661         o = o || {};
9662
9663         el.queueFx(o, function(){
9664
9665             anchor = anchor || "t";
9666
9667             // fix display to visibility
9668             this.fixDisplay();
9669
9670             // restore values after effect
9671             var r = this.getFxRestore();
9672             var b = this.getBox();
9673             // fixed size for slide
9674             this.setSize(b);
9675
9676             // wrap if needed
9677             var wrap = this.fxWrap(r.pos, o, "hidden");
9678
9679             var st = this.dom.style;
9680             st.visibility = "visible";
9681             st.position = "absolute";
9682
9683             // clear out temp styles after slide and unwrap
9684             var after = function(){
9685                 el.fxUnwrap(wrap, r.pos, o);
9686                 st.width = r.width;
9687                 st.height = r.height;
9688                 el.afterFx(o);
9689             };
9690             // time to calc the positions
9691             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9692
9693             switch(anchor.toLowerCase()){
9694                 case "t":
9695                     wrap.setSize(b.width, 0);
9696                     st.left = st.bottom = "0";
9697                     a = {height: bh};
9698                 break;
9699                 case "l":
9700                     wrap.setSize(0, b.height);
9701                     st.right = st.top = "0";
9702                     a = {width: bw};
9703                 break;
9704                 case "r":
9705                     wrap.setSize(0, b.height);
9706                     wrap.setX(b.right);
9707                     st.left = st.top = "0";
9708                     a = {width: bw, points: pt};
9709                 break;
9710                 case "b":
9711                     wrap.setSize(b.width, 0);
9712                     wrap.setY(b.bottom);
9713                     st.left = st.top = "0";
9714                     a = {height: bh, points: pt};
9715                 break;
9716                 case "tl":
9717                     wrap.setSize(0, 0);
9718                     st.right = st.bottom = "0";
9719                     a = {width: bw, height: bh};
9720                 break;
9721                 case "bl":
9722                     wrap.setSize(0, 0);
9723                     wrap.setY(b.y+b.height);
9724                     st.right = st.top = "0";
9725                     a = {width: bw, height: bh, points: pt};
9726                 break;
9727                 case "br":
9728                     wrap.setSize(0, 0);
9729                     wrap.setXY([b.right, b.bottom]);
9730                     st.left = st.top = "0";
9731                     a = {width: bw, height: bh, points: pt};
9732                 break;
9733                 case "tr":
9734                     wrap.setSize(0, 0);
9735                     wrap.setX(b.x+b.width);
9736                     st.left = st.bottom = "0";
9737                     a = {width: bw, height: bh, points: pt};
9738                 break;
9739             }
9740             this.dom.style.visibility = "visible";
9741             wrap.show();
9742
9743             arguments.callee.anim = wrap.fxanim(a,
9744                 o,
9745                 'motion',
9746                 .5,
9747                 'easeOut', after);
9748         });
9749         return this;
9750     },
9751     
9752         /**
9753          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9754          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9755          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9756          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9757          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9758          * Usage:
9759          *<pre><code>
9760 // default: slide the element out to the top
9761 el.slideOut();
9762
9763 // custom: slide the element out to the right with a 2-second duration
9764 el.slideOut('r', { duration: 2 });
9765
9766 // common config options shown with default values
9767 el.slideOut('t', {
9768     easing: 'easeOut',
9769     duration: .5,
9770     remove: false,
9771     useDisplay: false
9772 });
9773 </code></pre>
9774          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9775          * @param {Object} options (optional) Object literal with any of the Fx config options
9776          * @return {Roo.Element} The Element
9777          */
9778     slideOut : function(anchor, o){
9779         var el = this.getFxEl();
9780         o = o || {};
9781
9782         el.queueFx(o, function(){
9783
9784             anchor = anchor || "t";
9785
9786             // restore values after effect
9787             var r = this.getFxRestore();
9788             
9789             var b = this.getBox();
9790             // fixed size for slide
9791             this.setSize(b);
9792
9793             // wrap if needed
9794             var wrap = this.fxWrap(r.pos, o, "visible");
9795
9796             var st = this.dom.style;
9797             st.visibility = "visible";
9798             st.position = "absolute";
9799
9800             wrap.setSize(b);
9801
9802             var after = function(){
9803                 if(o.useDisplay){
9804                     el.setDisplayed(false);
9805                 }else{
9806                     el.hide();
9807                 }
9808
9809                 el.fxUnwrap(wrap, r.pos, o);
9810
9811                 st.width = r.width;
9812                 st.height = r.height;
9813
9814                 el.afterFx(o);
9815             };
9816
9817             var a, zero = {to: 0};
9818             switch(anchor.toLowerCase()){
9819                 case "t":
9820                     st.left = st.bottom = "0";
9821                     a = {height: zero};
9822                 break;
9823                 case "l":
9824                     st.right = st.top = "0";
9825                     a = {width: zero};
9826                 break;
9827                 case "r":
9828                     st.left = st.top = "0";
9829                     a = {width: zero, points: {to:[b.right, b.y]}};
9830                 break;
9831                 case "b":
9832                     st.left = st.top = "0";
9833                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9834                 break;
9835                 case "tl":
9836                     st.right = st.bottom = "0";
9837                     a = {width: zero, height: zero};
9838                 break;
9839                 case "bl":
9840                     st.right = st.top = "0";
9841                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9842                 break;
9843                 case "br":
9844                     st.left = st.top = "0";
9845                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9846                 break;
9847                 case "tr":
9848                     st.left = st.bottom = "0";
9849                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9850                 break;
9851             }
9852
9853             arguments.callee.anim = wrap.fxanim(a,
9854                 o,
9855                 'motion',
9856                 .5,
9857                 "easeOut", after);
9858         });
9859         return this;
9860     },
9861
9862         /**
9863          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9864          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9865          * The element must be removed from the DOM using the 'remove' config option if desired.
9866          * Usage:
9867          *<pre><code>
9868 // default
9869 el.puff();
9870
9871 // common config options shown with default values
9872 el.puff({
9873     easing: 'easeOut',
9874     duration: .5,
9875     remove: false,
9876     useDisplay: false
9877 });
9878 </code></pre>
9879          * @param {Object} options (optional) Object literal with any of the Fx config options
9880          * @return {Roo.Element} The Element
9881          */
9882     puff : function(o){
9883         var el = this.getFxEl();
9884         o = o || {};
9885
9886         el.queueFx(o, function(){
9887             this.clearOpacity();
9888             this.show();
9889
9890             // restore values after effect
9891             var r = this.getFxRestore();
9892             var st = this.dom.style;
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.clearOpacity();
9902
9903                 el.setPositioning(r.pos);
9904                 st.width = r.width;
9905                 st.height = r.height;
9906                 st.fontSize = '';
9907                 el.afterFx(o);
9908             };
9909
9910             var width = this.getWidth();
9911             var height = this.getHeight();
9912
9913             arguments.callee.anim = this.fxanim({
9914                     width : {to: this.adjustWidth(width * 2)},
9915                     height : {to: this.adjustHeight(height * 2)},
9916                     points : {by: [-(width * .5), -(height * .5)]},
9917                     opacity : {to: 0},
9918                     fontSize: {to:200, unit: "%"}
9919                 },
9920                 o,
9921                 'motion',
9922                 .5,
9923                 "easeOut", after);
9924         });
9925         return this;
9926     },
9927
9928         /**
9929          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9930          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9931          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9932          * Usage:
9933          *<pre><code>
9934 // default
9935 el.switchOff();
9936
9937 // all config options shown with default values
9938 el.switchOff({
9939     easing: 'easeIn',
9940     duration: .3,
9941     remove: false,
9942     useDisplay: false
9943 });
9944 </code></pre>
9945          * @param {Object} options (optional) Object literal with any of the Fx config options
9946          * @return {Roo.Element} The Element
9947          */
9948     switchOff : function(o){
9949         var el = this.getFxEl();
9950         o = o || {};
9951
9952         el.queueFx(o, function(){
9953             this.clearOpacity();
9954             this.clip();
9955
9956             // restore values after effect
9957             var r = this.getFxRestore();
9958             var st = this.dom.style;
9959
9960             var after = function(){
9961                 if(o.useDisplay){
9962                     el.setDisplayed(false);
9963                 }else{
9964                     el.hide();
9965                 }
9966
9967                 el.clearOpacity();
9968                 el.setPositioning(r.pos);
9969                 st.width = r.width;
9970                 st.height = r.height;
9971
9972                 el.afterFx(o);
9973             };
9974
9975             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9976                 this.clearOpacity();
9977                 (function(){
9978                     this.fxanim({
9979                         height:{to:1},
9980                         points:{by:[0, this.getHeight() * .5]}
9981                     }, o, 'motion', 0.3, 'easeIn', after);
9982                 }).defer(100, this);
9983             });
9984         });
9985         return this;
9986     },
9987
9988     /**
9989      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9990      * changed using the "attr" config option) and then fading back to the original color. If no original
9991      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9992      * Usage:
9993 <pre><code>
9994 // default: highlight background to yellow
9995 el.highlight();
9996
9997 // custom: highlight foreground text to blue for 2 seconds
9998 el.highlight("0000ff", { attr: 'color', duration: 2 });
9999
10000 // common config options shown with default values
10001 el.highlight("ffff9c", {
10002     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10003     endColor: (current color) or "ffffff",
10004     easing: 'easeIn',
10005     duration: 1
10006 });
10007 </code></pre>
10008      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10009      * @param {Object} options (optional) Object literal with any of the Fx config options
10010      * @return {Roo.Element} The Element
10011      */ 
10012     highlight : function(color, o){
10013         var el = this.getFxEl();
10014         o = o || {};
10015
10016         el.queueFx(o, function(){
10017             color = color || "ffff9c";
10018             attr = o.attr || "backgroundColor";
10019
10020             this.clearOpacity();
10021             this.show();
10022
10023             var origColor = this.getColor(attr);
10024             var restoreColor = this.dom.style[attr];
10025             endColor = (o.endColor || origColor) || "ffffff";
10026
10027             var after = function(){
10028                 el.dom.style[attr] = restoreColor;
10029                 el.afterFx(o);
10030             };
10031
10032             var a = {};
10033             a[attr] = {from: color, to: endColor};
10034             arguments.callee.anim = this.fxanim(a,
10035                 o,
10036                 'color',
10037                 1,
10038                 'easeIn', after);
10039         });
10040         return this;
10041     },
10042
10043    /**
10044     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10045     * Usage:
10046 <pre><code>
10047 // default: a single light blue ripple
10048 el.frame();
10049
10050 // custom: 3 red ripples lasting 3 seconds total
10051 el.frame("ff0000", 3, { duration: 3 });
10052
10053 // common config options shown with default values
10054 el.frame("C3DAF9", 1, {
10055     duration: 1 //duration of entire animation (not each individual ripple)
10056     // Note: Easing is not configurable and will be ignored if included
10057 });
10058 </code></pre>
10059     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10060     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10061     * @param {Object} options (optional) Object literal with any of the Fx config options
10062     * @return {Roo.Element} The Element
10063     */
10064     frame : function(color, count, o){
10065         var el = this.getFxEl();
10066         o = o || {};
10067
10068         el.queueFx(o, function(){
10069             color = color || "#C3DAF9";
10070             if(color.length == 6){
10071                 color = "#" + color;
10072             }
10073             count = count || 1;
10074             duration = o.duration || 1;
10075             this.show();
10076
10077             var b = this.getBox();
10078             var animFn = function(){
10079                 var proxy = this.createProxy({
10080
10081                      style:{
10082                         visbility:"hidden",
10083                         position:"absolute",
10084                         "z-index":"35000", // yee haw
10085                         border:"0px solid " + color
10086                      }
10087                   });
10088                 var scale = Roo.isBorderBox ? 2 : 1;
10089                 proxy.animate({
10090                     top:{from:b.y, to:b.y - 20},
10091                     left:{from:b.x, to:b.x - 20},
10092                     borderWidth:{from:0, to:10},
10093                     opacity:{from:1, to:0},
10094                     height:{from:b.height, to:(b.height + (20*scale))},
10095                     width:{from:b.width, to:(b.width + (20*scale))}
10096                 }, duration, function(){
10097                     proxy.remove();
10098                 });
10099                 if(--count > 0){
10100                      animFn.defer((duration/2)*1000, this);
10101                 }else{
10102                     el.afterFx(o);
10103                 }
10104             };
10105             animFn.call(this);
10106         });
10107         return this;
10108     },
10109
10110    /**
10111     * Creates a pause before any subsequent queued effects begin.  If there are
10112     * no effects queued after the pause it will have no effect.
10113     * Usage:
10114 <pre><code>
10115 el.pause(1);
10116 </code></pre>
10117     * @param {Number} seconds The length of time to pause (in seconds)
10118     * @return {Roo.Element} The Element
10119     */
10120     pause : function(seconds){
10121         var el = this.getFxEl();
10122         var o = {};
10123
10124         el.queueFx(o, function(){
10125             setTimeout(function(){
10126                 el.afterFx(o);
10127             }, seconds * 1000);
10128         });
10129         return this;
10130     },
10131
10132    /**
10133     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10134     * using the "endOpacity" config option.
10135     * Usage:
10136 <pre><code>
10137 // default: fade in from opacity 0 to 100%
10138 el.fadeIn();
10139
10140 // custom: fade in from opacity 0 to 75% over 2 seconds
10141 el.fadeIn({ endOpacity: .75, duration: 2});
10142
10143 // common config options shown with default values
10144 el.fadeIn({
10145     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10146     easing: 'easeOut',
10147     duration: .5
10148 });
10149 </code></pre>
10150     * @param {Object} options (optional) Object literal with any of the Fx config options
10151     * @return {Roo.Element} The Element
10152     */
10153     fadeIn : function(o){
10154         var el = this.getFxEl();
10155         o = o || {};
10156         el.queueFx(o, function(){
10157             this.setOpacity(0);
10158             this.fixDisplay();
10159             this.dom.style.visibility = 'visible';
10160             var to = o.endOpacity || 1;
10161             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10162                 o, null, .5, "easeOut", function(){
10163                 if(to == 1){
10164                     this.clearOpacity();
10165                 }
10166                 el.afterFx(o);
10167             });
10168         });
10169         return this;
10170     },
10171
10172    /**
10173     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10174     * using the "endOpacity" config option.
10175     * Usage:
10176 <pre><code>
10177 // default: fade out from the element's current opacity to 0
10178 el.fadeOut();
10179
10180 // custom: fade out from the element's current opacity to 25% over 2 seconds
10181 el.fadeOut({ endOpacity: .25, duration: 2});
10182
10183 // common config options shown with default values
10184 el.fadeOut({
10185     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10186     easing: 'easeOut',
10187     duration: .5
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192     * @param {Object} options (optional) Object literal with any of the Fx config options
10193     * @return {Roo.Element} The Element
10194     */
10195     fadeOut : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198         el.queueFx(o, function(){
10199             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10200                 o, null, .5, "easeOut", function(){
10201                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10202                      this.dom.style.display = "none";
10203                 }else{
10204                      this.dom.style.visibility = "hidden";
10205                 }
10206                 this.clearOpacity();
10207                 el.afterFx(o);
10208             });
10209         });
10210         return this;
10211     },
10212
10213    /**
10214     * Animates the transition of an element's dimensions from a starting height/width
10215     * to an ending height/width.
10216     * Usage:
10217 <pre><code>
10218 // change height and width to 100x100 pixels
10219 el.scale(100, 100);
10220
10221 // common config options shown with default values.  The height and width will default to
10222 // the element's existing values if passed as null.
10223 el.scale(
10224     [element's width],
10225     [element's height], {
10226     easing: 'easeOut',
10227     duration: .35
10228 });
10229 </code></pre>
10230     * @param {Number} width  The new width (pass undefined to keep the original width)
10231     * @param {Number} height  The new height (pass undefined to keep the original height)
10232     * @param {Object} options (optional) Object literal with any of the Fx config options
10233     * @return {Roo.Element} The Element
10234     */
10235     scale : function(w, h, o){
10236         this.shift(Roo.apply({}, o, {
10237             width: w,
10238             height: h
10239         }));
10240         return this;
10241     },
10242
10243    /**
10244     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10245     * Any of these properties not specified in the config object will not be changed.  This effect 
10246     * requires that at least one new dimension, position or opacity setting must be passed in on
10247     * the config object in order for the function to have any effect.
10248     * Usage:
10249 <pre><code>
10250 // slide the element horizontally to x position 200 while changing the height and opacity
10251 el.shift({ x: 200, height: 50, opacity: .8 });
10252
10253 // common config options shown with default values.
10254 el.shift({
10255     width: [element's width],
10256     height: [element's height],
10257     x: [element's x position],
10258     y: [element's y position],
10259     opacity: [element's opacity],
10260     easing: 'easeOut',
10261     duration: .35
10262 });
10263 </code></pre>
10264     * @param {Object} options  Object literal with any of the Fx config options
10265     * @return {Roo.Element} The Element
10266     */
10267     shift : function(o){
10268         var el = this.getFxEl();
10269         o = o || {};
10270         el.queueFx(o, function(){
10271             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10272             if(w !== undefined){
10273                 a.width = {to: this.adjustWidth(w)};
10274             }
10275             if(h !== undefined){
10276                 a.height = {to: this.adjustHeight(h)};
10277             }
10278             if(x !== undefined || y !== undefined){
10279                 a.points = {to: [
10280                     x !== undefined ? x : this.getX(),
10281                     y !== undefined ? y : this.getY()
10282                 ]};
10283             }
10284             if(op !== undefined){
10285                 a.opacity = {to: op};
10286             }
10287             if(o.xy !== undefined){
10288                 a.points = {to: o.xy};
10289             }
10290             arguments.callee.anim = this.fxanim(a,
10291                 o, 'motion', .35, "easeOut", function(){
10292                 el.afterFx(o);
10293             });
10294         });
10295         return this;
10296     },
10297
10298         /**
10299          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10300          * ending point of the effect.
10301          * Usage:
10302          *<pre><code>
10303 // default: slide the element downward while fading out
10304 el.ghost();
10305
10306 // custom: slide the element out to the right with a 2-second duration
10307 el.ghost('r', { duration: 2 });
10308
10309 // common config options shown with default values
10310 el.ghost('b', {
10311     easing: 'easeOut',
10312     duration: .5
10313     remove: false,
10314     useDisplay: false
10315 });
10316 </code></pre>
10317          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10318          * @param {Object} options (optional) Object literal with any of the Fx config options
10319          * @return {Roo.Element} The Element
10320          */
10321     ghost : function(anchor, o){
10322         var el = this.getFxEl();
10323         o = o || {};
10324
10325         el.queueFx(o, function(){
10326             anchor = anchor || "b";
10327
10328             // restore values after effect
10329             var r = this.getFxRestore();
10330             var w = this.getWidth(),
10331                 h = this.getHeight();
10332
10333             var st = this.dom.style;
10334
10335             var after = function(){
10336                 if(o.useDisplay){
10337                     el.setDisplayed(false);
10338                 }else{
10339                     el.hide();
10340                 }
10341
10342                 el.clearOpacity();
10343                 el.setPositioning(r.pos);
10344                 st.width = r.width;
10345                 st.height = r.height;
10346
10347                 el.afterFx(o);
10348             };
10349
10350             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10351             switch(anchor.toLowerCase()){
10352                 case "t":
10353                     pt.by = [0, -h];
10354                 break;
10355                 case "l":
10356                     pt.by = [-w, 0];
10357                 break;
10358                 case "r":
10359                     pt.by = [w, 0];
10360                 break;
10361                 case "b":
10362                     pt.by = [0, h];
10363                 break;
10364                 case "tl":
10365                     pt.by = [-w, -h];
10366                 break;
10367                 case "bl":
10368                     pt.by = [-w, h];
10369                 break;
10370                 case "br":
10371                     pt.by = [w, h];
10372                 break;
10373                 case "tr":
10374                     pt.by = [w, -h];
10375                 break;
10376             }
10377
10378             arguments.callee.anim = this.fxanim(a,
10379                 o,
10380                 'motion',
10381                 .5,
10382                 "easeOut", after);
10383         });
10384         return this;
10385     },
10386
10387         /**
10388          * Ensures that all effects queued after syncFx is called on the element are
10389          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10390          * @return {Roo.Element} The Element
10391          */
10392     syncFx : function(){
10393         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10394             block : false,
10395             concurrent : true,
10396             stopFx : false
10397         });
10398         return this;
10399     },
10400
10401         /**
10402          * Ensures that all effects queued after sequenceFx is called on the element are
10403          * run in sequence.  This is the opposite of {@link #syncFx}.
10404          * @return {Roo.Element} The Element
10405          */
10406     sequenceFx : function(){
10407         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10408             block : false,
10409             concurrent : false,
10410             stopFx : false
10411         });
10412         return this;
10413     },
10414
10415         /* @private */
10416     nextFx : function(){
10417         var ef = this.fxQueue[0];
10418         if(ef){
10419             ef.call(this);
10420         }
10421     },
10422
10423         /**
10424          * Returns true if the element has any effects actively running or queued, else returns false.
10425          * @return {Boolean} True if element has active effects, else false
10426          */
10427     hasActiveFx : function(){
10428         return this.fxQueue && this.fxQueue[0];
10429     },
10430
10431         /**
10432          * Stops any running effects and clears the element's internal effects queue if it contains
10433          * any additional effects that haven't started yet.
10434          * @return {Roo.Element} The Element
10435          */
10436     stopFx : function(){
10437         if(this.hasActiveFx()){
10438             var cur = this.fxQueue[0];
10439             if(cur && cur.anim && cur.anim.isAnimated()){
10440                 this.fxQueue = [cur]; // clear out others
10441                 cur.anim.stop(true);
10442             }
10443         }
10444         return this;
10445     },
10446
10447         /* @private */
10448     beforeFx : function(o){
10449         if(this.hasActiveFx() && !o.concurrent){
10450            if(o.stopFx){
10451                this.stopFx();
10452                return true;
10453            }
10454            return false;
10455         }
10456         return true;
10457     },
10458
10459         /**
10460          * Returns true if the element is currently blocking so that no other effect can be queued
10461          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10462          * used to ensure that an effect initiated by a user action runs to completion prior to the
10463          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10464          * @return {Boolean} True if blocking, else false
10465          */
10466     hasFxBlock : function(){
10467         var q = this.fxQueue;
10468         return q && q[0] && q[0].block;
10469     },
10470
10471         /* @private */
10472     queueFx : function(o, fn){
10473         if(!this.fxQueue){
10474             this.fxQueue = [];
10475         }
10476         if(!this.hasFxBlock()){
10477             Roo.applyIf(o, this.fxDefaults);
10478             if(!o.concurrent){
10479                 var run = this.beforeFx(o);
10480                 fn.block = o.block;
10481                 this.fxQueue.push(fn);
10482                 if(run){
10483                     this.nextFx();
10484                 }
10485             }else{
10486                 fn.call(this);
10487             }
10488         }
10489         return this;
10490     },
10491
10492         /* @private */
10493     fxWrap : function(pos, o, vis){
10494         var wrap;
10495         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10496             var wrapXY;
10497             if(o.fixPosition){
10498                 wrapXY = this.getXY();
10499             }
10500             var div = document.createElement("div");
10501             div.style.visibility = vis;
10502             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10503             wrap.setPositioning(pos);
10504             if(wrap.getStyle("position") == "static"){
10505                 wrap.position("relative");
10506             }
10507             this.clearPositioning('auto');
10508             wrap.clip();
10509             wrap.dom.appendChild(this.dom);
10510             if(wrapXY){
10511                 wrap.setXY(wrapXY);
10512             }
10513         }
10514         return wrap;
10515     },
10516
10517         /* @private */
10518     fxUnwrap : function(wrap, pos, o){
10519         this.clearPositioning();
10520         this.setPositioning(pos);
10521         if(!o.wrap){
10522             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10523             wrap.remove();
10524         }
10525     },
10526
10527         /* @private */
10528     getFxRestore : function(){
10529         var st = this.dom.style;
10530         return {pos: this.getPositioning(), width: st.width, height : st.height};
10531     },
10532
10533         /* @private */
10534     afterFx : function(o){
10535         if(o.afterStyle){
10536             this.applyStyles(o.afterStyle);
10537         }
10538         if(o.afterCls){
10539             this.addClass(o.afterCls);
10540         }
10541         if(o.remove === true){
10542             this.remove();
10543         }
10544         Roo.callback(o.callback, o.scope, [this]);
10545         if(!o.concurrent){
10546             this.fxQueue.shift();
10547             this.nextFx();
10548         }
10549     },
10550
10551         /* @private */
10552     getFxEl : function(){ // support for composite element fx
10553         return Roo.get(this.dom);
10554     },
10555
10556         /* @private */
10557     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10558         animType = animType || 'run';
10559         opt = opt || {};
10560         var anim = Roo.lib.Anim[animType](
10561             this.dom, args,
10562             (opt.duration || defaultDur) || .35,
10563             (opt.easing || defaultEase) || 'easeOut',
10564             function(){
10565                 Roo.callback(cb, this);
10566             },
10567             this
10568         );
10569         opt.anim = anim;
10570         return anim;
10571     }
10572 };
10573
10574 // backwords compat
10575 Roo.Fx.resize = Roo.Fx.scale;
10576
10577 //When included, Roo.Fx is automatically applied to Element so that all basic
10578 //effects are available directly via the Element API
10579 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589
10590
10591 /**
10592  * @class Roo.CompositeElement
10593  * Standard composite class. Creates a Roo.Element for every element in the collection.
10594  * <br><br>
10595  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10596  * actions will be performed on all the elements in this collection.</b>
10597  * <br><br>
10598  * All methods return <i>this</i> and can be chained.
10599  <pre><code>
10600  var els = Roo.select("#some-el div.some-class", true);
10601  // or select directly from an existing element
10602  var el = Roo.get('some-el');
10603  el.select('div.some-class', true);
10604
10605  els.setWidth(100); // all elements become 100 width
10606  els.hide(true); // all elements fade out and hide
10607  // or
10608  els.setWidth(100).hide(true);
10609  </code></pre>
10610  */
10611 Roo.CompositeElement = function(els){
10612     this.elements = [];
10613     this.addElements(els);
10614 };
10615 Roo.CompositeElement.prototype = {
10616     isComposite: true,
10617     addElements : function(els){
10618         if(!els) return this;
10619         if(typeof els == "string"){
10620             els = Roo.Element.selectorFunction(els);
10621         }
10622         var yels = this.elements;
10623         var index = yels.length-1;
10624         for(var i = 0, len = els.length; i < len; i++) {
10625                 yels[++index] = Roo.get(els[i]);
10626         }
10627         return this;
10628     },
10629
10630     /**
10631     * Clears this composite and adds the elements returned by the passed selector.
10632     * @param {String/Array} els A string CSS selector, an array of elements or an element
10633     * @return {CompositeElement} this
10634     */
10635     fill : function(els){
10636         this.elements = [];
10637         this.add(els);
10638         return this;
10639     },
10640
10641     /**
10642     * Filters this composite to only elements that match the passed selector.
10643     * @param {String} selector A string CSS selector
10644     * @return {CompositeElement} this
10645     */
10646     filter : function(selector){
10647         var els = [];
10648         this.each(function(el){
10649             if(el.is(selector)){
10650                 els[els.length] = el.dom;
10651             }
10652         });
10653         this.fill(els);
10654         return this;
10655     },
10656
10657     invoke : function(fn, args){
10658         var els = this.elements;
10659         for(var i = 0, len = els.length; i < len; i++) {
10660                 Roo.Element.prototype[fn].apply(els[i], args);
10661         }
10662         return this;
10663     },
10664     /**
10665     * Adds elements to this composite.
10666     * @param {String/Array} els A string CSS selector, an array of elements or an element
10667     * @return {CompositeElement} this
10668     */
10669     add : function(els){
10670         if(typeof els == "string"){
10671             this.addElements(Roo.Element.selectorFunction(els));
10672         }else if(els.length !== undefined){
10673             this.addElements(els);
10674         }else{
10675             this.addElements([els]);
10676         }
10677         return this;
10678     },
10679     /**
10680     * Calls the passed function passing (el, this, index) for each element in this composite.
10681     * @param {Function} fn The function to call
10682     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10683     * @return {CompositeElement} this
10684     */
10685     each : function(fn, scope){
10686         var els = this.elements;
10687         for(var i = 0, len = els.length; i < len; i++){
10688             if(fn.call(scope || els[i], els[i], this, i) === false) {
10689                 break;
10690             }
10691         }
10692         return this;
10693     },
10694
10695     /**
10696      * Returns the Element object at the specified index
10697      * @param {Number} index
10698      * @return {Roo.Element}
10699      */
10700     item : function(index){
10701         return this.elements[index] || null;
10702     },
10703
10704     /**
10705      * Returns the first Element
10706      * @return {Roo.Element}
10707      */
10708     first : function(){
10709         return this.item(0);
10710     },
10711
10712     /**
10713      * Returns the last Element
10714      * @return {Roo.Element}
10715      */
10716     last : function(){
10717         return this.item(this.elements.length-1);
10718     },
10719
10720     /**
10721      * Returns the number of elements in this composite
10722      * @return Number
10723      */
10724     getCount : function(){
10725         return this.elements.length;
10726     },
10727
10728     /**
10729      * Returns true if this composite contains the passed element
10730      * @return Boolean
10731      */
10732     contains : function(el){
10733         return this.indexOf(el) !== -1;
10734     },
10735
10736     /**
10737      * Returns true if this composite contains the passed element
10738      * @return Boolean
10739      */
10740     indexOf : function(el){
10741         return this.elements.indexOf(Roo.get(el));
10742     },
10743
10744
10745     /**
10746     * Removes the specified element(s).
10747     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10748     * or an array of any of those.
10749     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10750     * @return {CompositeElement} this
10751     */
10752     removeElement : function(el, removeDom){
10753         if(el instanceof Array){
10754             for(var i = 0, len = el.length; i < len; i++){
10755                 this.removeElement(el[i]);
10756             }
10757             return this;
10758         }
10759         var index = typeof el == 'number' ? el : this.indexOf(el);
10760         if(index !== -1){
10761             if(removeDom){
10762                 var d = this.elements[index];
10763                 if(d.dom){
10764                     d.remove();
10765                 }else{
10766                     d.parentNode.removeChild(d);
10767                 }
10768             }
10769             this.elements.splice(index, 1);
10770         }
10771         return this;
10772     },
10773
10774     /**
10775     * Replaces the specified element with the passed element.
10776     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10777     * to replace.
10778     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10779     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10780     * @return {CompositeElement} this
10781     */
10782     replaceElement : function(el, replacement, domReplace){
10783         var index = typeof el == 'number' ? el : this.indexOf(el);
10784         if(index !== -1){
10785             if(domReplace){
10786                 this.elements[index].replaceWith(replacement);
10787             }else{
10788                 this.elements.splice(index, 1, Roo.get(replacement))
10789             }
10790         }
10791         return this;
10792     },
10793
10794     /**
10795      * Removes all elements.
10796      */
10797     clear : function(){
10798         this.elements = [];
10799     }
10800 };
10801 (function(){
10802     Roo.CompositeElement.createCall = function(proto, fnName){
10803         if(!proto[fnName]){
10804             proto[fnName] = function(){
10805                 return this.invoke(fnName, arguments);
10806             };
10807         }
10808     };
10809     for(var fnName in Roo.Element.prototype){
10810         if(typeof Roo.Element.prototype[fnName] == "function"){
10811             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10812         }
10813     };
10814 })();
10815 /*
10816  * Based on:
10817  * Ext JS Library 1.1.1
10818  * Copyright(c) 2006-2007, Ext JS, LLC.
10819  *
10820  * Originally Released Under LGPL - original licence link has changed is not relivant.
10821  *
10822  * Fork - LGPL
10823  * <script type="text/javascript">
10824  */
10825
10826 /**
10827  * @class Roo.CompositeElementLite
10828  * @extends Roo.CompositeElement
10829  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10830  <pre><code>
10831  var els = Roo.select("#some-el div.some-class");
10832  // or select directly from an existing element
10833  var el = Roo.get('some-el');
10834  el.select('div.some-class');
10835
10836  els.setWidth(100); // all elements become 100 width
10837  els.hide(true); // all elements fade out and hide
10838  // or
10839  els.setWidth(100).hide(true);
10840  </code></pre><br><br>
10841  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10842  * actions will be performed on all the elements in this collection.</b>
10843  */
10844 Roo.CompositeElementLite = function(els){
10845     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10846     this.el = new Roo.Element.Flyweight();
10847 };
10848 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10849     addElements : function(els){
10850         if(els){
10851             if(els instanceof Array){
10852                 this.elements = this.elements.concat(els);
10853             }else{
10854                 var yels = this.elements;
10855                 var index = yels.length-1;
10856                 for(var i = 0, len = els.length; i < len; i++) {
10857                     yels[++index] = els[i];
10858                 }
10859             }
10860         }
10861         return this;
10862     },
10863     invoke : function(fn, args){
10864         var els = this.elements;
10865         var el = this.el;
10866         for(var i = 0, len = els.length; i < len; i++) {
10867             el.dom = els[i];
10868                 Roo.Element.prototype[fn].apply(el, args);
10869         }
10870         return this;
10871     },
10872     /**
10873      * Returns a flyweight Element of the dom element object at the specified index
10874      * @param {Number} index
10875      * @return {Roo.Element}
10876      */
10877     item : function(index){
10878         if(!this.elements[index]){
10879             return null;
10880         }
10881         this.el.dom = this.elements[index];
10882         return this.el;
10883     },
10884
10885     // fixes scope with flyweight
10886     addListener : function(eventName, handler, scope, opt){
10887         var els = this.elements;
10888         for(var i = 0, len = els.length; i < len; i++) {
10889             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10890         }
10891         return this;
10892     },
10893
10894     /**
10895     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10896     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10897     * a reference to the dom node, use el.dom.</b>
10898     * @param {Function} fn The function to call
10899     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10900     * @return {CompositeElement} this
10901     */
10902     each : function(fn, scope){
10903         var els = this.elements;
10904         var el = this.el;
10905         for(var i = 0, len = els.length; i < len; i++){
10906             el.dom = els[i];
10907                 if(fn.call(scope || el, el, this, i) === false){
10908                 break;
10909             }
10910         }
10911         return this;
10912     },
10913
10914     indexOf : function(el){
10915         return this.elements.indexOf(Roo.getDom(el));
10916     },
10917
10918     replaceElement : function(el, replacement, domReplace){
10919         var index = typeof el == 'number' ? el : this.indexOf(el);
10920         if(index !== -1){
10921             replacement = Roo.getDom(replacement);
10922             if(domReplace){
10923                 var d = this.elements[index];
10924                 d.parentNode.insertBefore(replacement, d);
10925                 d.parentNode.removeChild(d);
10926             }
10927             this.elements.splice(index, 1, replacement);
10928         }
10929         return this;
10930     }
10931 });
10932 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10933
10934 /*
10935  * Based on:
10936  * Ext JS Library 1.1.1
10937  * Copyright(c) 2006-2007, Ext JS, LLC.
10938  *
10939  * Originally Released Under LGPL - original licence link has changed is not relivant.
10940  *
10941  * Fork - LGPL
10942  * <script type="text/javascript">
10943  */
10944
10945  
10946
10947 /**
10948  * @class Roo.data.Connection
10949  * @extends Roo.util.Observable
10950  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10951  * either to a configured URL, or to a URL specified at request time.<br><br>
10952  * <p>
10953  * Requests made by this class are asynchronous, and will return immediately. No data from
10954  * the server will be available to the statement immediately following the {@link #request} call.
10955  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10956  * <p>
10957  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10958  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10959  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10960  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10961  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10962  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10963  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10964  * standard DOM methods.
10965  * @constructor
10966  * @param {Object} config a configuration object.
10967  */
10968 Roo.data.Connection = function(config){
10969     Roo.apply(this, config);
10970     this.addEvents({
10971         /**
10972          * @event beforerequest
10973          * Fires before a network request is made to retrieve a data object.
10974          * @param {Connection} conn This Connection object.
10975          * @param {Object} options The options config object passed to the {@link #request} method.
10976          */
10977         "beforerequest" : true,
10978         /**
10979          * @event requestcomplete
10980          * Fires if the request was successfully completed.
10981          * @param {Connection} conn This Connection object.
10982          * @param {Object} response The XHR object containing the response data.
10983          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10984          * @param {Object} options The options config object passed to the {@link #request} method.
10985          */
10986         "requestcomplete" : true,
10987         /**
10988          * @event requestexception
10989          * Fires if an error HTTP status was returned from the server.
10990          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10991          * @param {Connection} conn This Connection object.
10992          * @param {Object} response The XHR object containing the response data.
10993          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10994          * @param {Object} options The options config object passed to the {@link #request} method.
10995          */
10996         "requestexception" : true
10997     });
10998     Roo.data.Connection.superclass.constructor.call(this);
10999 };
11000
11001 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11002     /**
11003      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11004      */
11005     /**
11006      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11007      * extra parameters to each request made by this object. (defaults to undefined)
11008      */
11009     /**
11010      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11011      *  to each request made by this object. (defaults to undefined)
11012      */
11013     /**
11014      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11015      */
11016     /**
11017      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11018      */
11019     timeout : 30000,
11020     /**
11021      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11022      * @type Boolean
11023      */
11024     autoAbort:false,
11025
11026     /**
11027      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11028      * @type Boolean
11029      */
11030     disableCaching: true,
11031
11032     /**
11033      * Sends an HTTP request to a remote server.
11034      * @param {Object} options An object which may contain the following properties:<ul>
11035      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11036      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11037      * request, a url encoded string or a function to call to get either.</li>
11038      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11039      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11040      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11041      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11042      * <li>options {Object} The parameter to the request call.</li>
11043      * <li>success {Boolean} True if the request succeeded.</li>
11044      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11045      * </ul></li>
11046      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11047      * The callback is passed the following parameters:<ul>
11048      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11049      * <li>options {Object} The parameter to the request call.</li>
11050      * </ul></li>
11051      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11052      * The callback is passed the following parameters:<ul>
11053      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11054      * <li>options {Object} The parameter to the request call.</li>
11055      * </ul></li>
11056      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11057      * for the callback function. Defaults to the browser window.</li>
11058      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11059      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11060      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11061      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11062      * params for the post data. Any params will be appended to the URL.</li>
11063      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11064      * </ul>
11065      * @return {Number} transactionId
11066      */
11067     request : function(o){
11068         if(this.fireEvent("beforerequest", this, o) !== false){
11069             var p = o.params;
11070
11071             if(typeof p == "function"){
11072                 p = p.call(o.scope||window, o);
11073             }
11074             if(typeof p == "object"){
11075                 p = Roo.urlEncode(o.params);
11076             }
11077             if(this.extraParams){
11078                 var extras = Roo.urlEncode(this.extraParams);
11079                 p = p ? (p + '&' + extras) : extras;
11080             }
11081
11082             var url = o.url || this.url;
11083             if(typeof url == 'function'){
11084                 url = url.call(o.scope||window, o);
11085             }
11086
11087             if(o.form){
11088                 var form = Roo.getDom(o.form);
11089                 url = url || form.action;
11090
11091                 var enctype = form.getAttribute("enctype");
11092                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11093                     return this.doFormUpload(o, p, url);
11094                 }
11095                 var f = Roo.lib.Ajax.serializeForm(form);
11096                 p = p ? (p + '&' + f) : f;
11097             }
11098
11099             var hs = o.headers;
11100             if(this.defaultHeaders){
11101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11102                 if(!o.headers){
11103                     o.headers = hs;
11104                 }
11105             }
11106
11107             var cb = {
11108                 success: this.handleResponse,
11109                 failure: this.handleFailure,
11110                 scope: this,
11111                 argument: {options: o},
11112                 timeout : this.timeout
11113             };
11114
11115             var method = o.method||this.method||(p ? "POST" : "GET");
11116
11117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11119             }
11120
11121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11122                 if(o.autoAbort){
11123                     this.abort();
11124                 }
11125             }else if(this.autoAbort !== false){
11126                 this.abort();
11127             }
11128
11129             if((method == 'GET' && p) || o.xmlData){
11130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11131                 p = '';
11132             }
11133             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11134             return this.transId;
11135         }else{
11136             Roo.callback(o.callback, o.scope, [o, null, null]);
11137             return null;
11138         }
11139     },
11140
11141     /**
11142      * Determine whether this object has a request outstanding.
11143      * @param {Number} transactionId (Optional) defaults to the last transaction
11144      * @return {Boolean} True if there is an outstanding request.
11145      */
11146     isLoading : function(transId){
11147         if(transId){
11148             return Roo.lib.Ajax.isCallInProgress(transId);
11149         }else{
11150             return this.transId ? true : false;
11151         }
11152     },
11153
11154     /**
11155      * Aborts any outstanding request.
11156      * @param {Number} transactionId (Optional) defaults to the last transaction
11157      */
11158     abort : function(transId){
11159         if(transId || this.isLoading()){
11160             Roo.lib.Ajax.abort(transId || this.transId);
11161         }
11162     },
11163
11164     // private
11165     handleResponse : function(response){
11166         this.transId = false;
11167         var options = response.argument.options;
11168         response.argument = options ? options.argument : null;
11169         this.fireEvent("requestcomplete", this, response, options);
11170         Roo.callback(options.success, options.scope, [response, options]);
11171         Roo.callback(options.callback, options.scope, [options, true, response]);
11172     },
11173
11174     // private
11175     handleFailure : function(response, e){
11176         this.transId = false;
11177         var options = response.argument.options;
11178         response.argument = options ? options.argument : null;
11179         this.fireEvent("requestexception", this, response, options, e);
11180         Roo.callback(options.failure, options.scope, [response, options]);
11181         Roo.callback(options.callback, options.scope, [options, false, response]);
11182     },
11183
11184     // private
11185     doFormUpload : function(o, ps, url){
11186         var id = Roo.id();
11187         var frame = document.createElement('iframe');
11188         frame.id = id;
11189         frame.name = id;
11190         frame.className = 'x-hidden';
11191         if(Roo.isIE){
11192             frame.src = Roo.SSL_SECURE_URL;
11193         }
11194         document.body.appendChild(frame);
11195
11196         if(Roo.isIE){
11197            document.frames[id].name = id;
11198         }
11199
11200         var form = Roo.getDom(o.form);
11201         form.target = id;
11202         form.method = 'POST';
11203         form.enctype = form.encoding = 'multipart/form-data';
11204         if(url){
11205             form.action = url;
11206         }
11207
11208         var hiddens, hd;
11209         if(ps){ // add dynamic params
11210             hiddens = [];
11211             ps = Roo.urlDecode(ps, false);
11212             for(var k in ps){
11213                 if(ps.hasOwnProperty(k)){
11214                     hd = document.createElement('input');
11215                     hd.type = 'hidden';
11216                     hd.name = k;
11217                     hd.value = ps[k];
11218                     form.appendChild(hd);
11219                     hiddens.push(hd);
11220                 }
11221             }
11222         }
11223
11224         function cb(){
11225             var r = {  // bogus response object
11226                 responseText : '',
11227                 responseXML : null
11228             };
11229
11230             r.argument = o ? o.argument : null;
11231
11232             try { //
11233                 var doc;
11234                 if(Roo.isIE){
11235                     doc = frame.contentWindow.document;
11236                 }else {
11237                     doc = (frame.contentDocument || window.frames[id].document);
11238                 }
11239                 if(doc && doc.body){
11240                     r.responseText = doc.body.innerHTML;
11241                 }
11242                 if(doc && doc.XMLDocument){
11243                     r.responseXML = doc.XMLDocument;
11244                 }else {
11245                     r.responseXML = doc;
11246                 }
11247             }
11248             catch(e) {
11249                 // ignore
11250             }
11251
11252             Roo.EventManager.removeListener(frame, 'load', cb, this);
11253
11254             this.fireEvent("requestcomplete", this, r, o);
11255             Roo.callback(o.success, o.scope, [r, o]);
11256             Roo.callback(o.callback, o.scope, [o, true, r]);
11257
11258             setTimeout(function(){document.body.removeChild(frame);}, 100);
11259         }
11260
11261         Roo.EventManager.on(frame, 'load', cb, this);
11262         form.submit();
11263
11264         if(hiddens){ // remove dynamic params
11265             for(var i = 0, len = hiddens.length; i < len; i++){
11266                 form.removeChild(hiddens[i]);
11267             }
11268         }
11269     }
11270 });
11271
11272 /**
11273  * @class Roo.Ajax
11274  * @extends Roo.data.Connection
11275  * Global Ajax request class.
11276  *
11277  * @singleton
11278  */
11279 Roo.Ajax = new Roo.data.Connection({
11280     // fix up the docs
11281    /**
11282      * @cfg {String} url @hide
11283      */
11284     /**
11285      * @cfg {Object} extraParams @hide
11286      */
11287     /**
11288      * @cfg {Object} defaultHeaders @hide
11289      */
11290     /**
11291      * @cfg {String} method (Optional) @hide
11292      */
11293     /**
11294      * @cfg {Number} timeout (Optional) @hide
11295      */
11296     /**
11297      * @cfg {Boolean} autoAbort (Optional) @hide
11298      */
11299
11300     /**
11301      * @cfg {Boolean} disableCaching (Optional) @hide
11302      */
11303
11304     /**
11305      * @property  disableCaching
11306      * True to add a unique cache-buster param to GET requests. (defaults to true)
11307      * @type Boolean
11308      */
11309     /**
11310      * @property  url
11311      * The default URL to be used for requests to the server. (defaults to undefined)
11312      * @type String
11313      */
11314     /**
11315      * @property  extraParams
11316      * An object containing properties which are used as
11317      * extra parameters to each request made by this object. (defaults to undefined)
11318      * @type Object
11319      */
11320     /**
11321      * @property  defaultHeaders
11322      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11323      * @type Object
11324      */
11325     /**
11326      * @property  method
11327      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11328      * @type String
11329      */
11330     /**
11331      * @property  timeout
11332      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11333      * @type Number
11334      */
11335
11336     /**
11337      * @property  autoAbort
11338      * Whether a new request should abort any pending requests. (defaults to false)
11339      * @type Boolean
11340      */
11341     autoAbort : false,
11342
11343     /**
11344      * Serialize the passed form into a url encoded string
11345      * @param {String/HTMLElement} form
11346      * @return {String}
11347      */
11348     serializeForm : function(form){
11349         return Roo.lib.Ajax.serializeForm(form);
11350     }
11351 });/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361  
11362 /**
11363  * @class Roo.Ajax
11364  * @extends Roo.data.Connection
11365  * Global Ajax request class.
11366  *
11367  * @instanceOf  Roo.data.Connection
11368  */
11369 Roo.Ajax = new Roo.data.Connection({
11370     // fix up the docs
11371     
11372     /**
11373      * fix up scoping
11374      * @scope Roo.Ajax
11375      */
11376     
11377    /**
11378      * @cfg {String} url @hide
11379      */
11380     /**
11381      * @cfg {Object} extraParams @hide
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders @hide
11385      */
11386     /**
11387      * @cfg {String} method (Optional) @hide
11388      */
11389     /**
11390      * @cfg {Number} timeout (Optional) @hide
11391      */
11392     /**
11393      * @cfg {Boolean} autoAbort (Optional) @hide
11394      */
11395
11396     /**
11397      * @cfg {Boolean} disableCaching (Optional) @hide
11398      */
11399
11400     /**
11401      * @property  disableCaching
11402      * True to add a unique cache-buster param to GET requests. (defaults to true)
11403      * @type Boolean
11404      */
11405     /**
11406      * @property  url
11407      * The default URL to be used for requests to the server. (defaults to undefined)
11408      * @type String
11409      */
11410     /**
11411      * @property  extraParams
11412      * An object containing properties which are used as
11413      * extra parameters to each request made by this object. (defaults to undefined)
11414      * @type Object
11415      */
11416     /**
11417      * @property  defaultHeaders
11418      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11419      * @type Object
11420      */
11421     /**
11422      * @property  method
11423      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11424      * @type String
11425      */
11426     /**
11427      * @property  timeout
11428      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11429      * @type Number
11430      */
11431
11432     /**
11433      * @property  autoAbort
11434      * Whether a new request should abort any pending requests. (defaults to false)
11435      * @type Boolean
11436      */
11437     autoAbort : false,
11438
11439     /**
11440      * Serialize the passed form into a url encoded string
11441      * @param {String/HTMLElement} form
11442      * @return {String}
11443      */
11444     serializeForm : function(form){
11445         return Roo.lib.Ajax.serializeForm(form);
11446     }
11447 });/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458  
11459 /**
11460  * @class Roo.UpdateManager
11461  * @extends Roo.util.Observable
11462  * Provides AJAX-style update for Element object.<br><br>
11463  * Usage:<br>
11464  * <pre><code>
11465  * // Get it from a Roo.Element object
11466  * var el = Roo.get("foo");
11467  * var mgr = el.getUpdateManager();
11468  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11469  * ...
11470  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11471  * <br>
11472  * // or directly (returns the same UpdateManager instance)
11473  * var mgr = new Roo.UpdateManager("myElementId");
11474  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11475  * mgr.on("update", myFcnNeedsToKnow);
11476  * <br>
11477    // short handed call directly from the element object
11478    Roo.get("foo").load({
11479         url: "bar.php",
11480         scripts:true,
11481         params: "for=bar",
11482         text: "Loading Foo..."
11483    });
11484  * </code></pre>
11485  * @constructor
11486  * Create new UpdateManager directly.
11487  * @param {String/HTMLElement/Roo.Element} el The element to update
11488  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11489  */
11490 Roo.UpdateManager = function(el, forceNew){
11491     el = Roo.get(el);
11492     if(!forceNew && el.updateManager){
11493         return el.updateManager;
11494     }
11495     /**
11496      * The Element object
11497      * @type Roo.Element
11498      */
11499     this.el = el;
11500     /**
11501      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11502      * @type String
11503      */
11504     this.defaultUrl = null;
11505
11506     this.addEvents({
11507         /**
11508          * @event beforeupdate
11509          * Fired before an update is made, return false from your handler and the update is cancelled.
11510          * @param {Roo.Element} el
11511          * @param {String/Object/Function} url
11512          * @param {String/Object} params
11513          */
11514         "beforeupdate": true,
11515         /**
11516          * @event update
11517          * Fired after successful update is made.
11518          * @param {Roo.Element} el
11519          * @param {Object} oResponseObject The response Object
11520          */
11521         "update": true,
11522         /**
11523          * @event failure
11524          * Fired on update failure.
11525          * @param {Roo.Element} el
11526          * @param {Object} oResponseObject The response Object
11527          */
11528         "failure": true
11529     });
11530     var d = Roo.UpdateManager.defaults;
11531     /**
11532      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11533      * @type String
11534      */
11535     this.sslBlankUrl = d.sslBlankUrl;
11536     /**
11537      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11538      * @type Boolean
11539      */
11540     this.disableCaching = d.disableCaching;
11541     /**
11542      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11543      * @type String
11544      */
11545     this.indicatorText = d.indicatorText;
11546     /**
11547      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11548      * @type String
11549      */
11550     this.showLoadIndicator = d.showLoadIndicator;
11551     /**
11552      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11553      * @type Number
11554      */
11555     this.timeout = d.timeout;
11556
11557     /**
11558      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11559      * @type Boolean
11560      */
11561     this.loadScripts = d.loadScripts;
11562
11563     /**
11564      * Transaction object of current executing transaction
11565      */
11566     this.transaction = null;
11567
11568     /**
11569      * @private
11570      */
11571     this.autoRefreshProcId = null;
11572     /**
11573      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11574      * @type Function
11575      */
11576     this.refreshDelegate = this.refresh.createDelegate(this);
11577     /**
11578      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11579      * @type Function
11580      */
11581     this.updateDelegate = this.update.createDelegate(this);
11582     /**
11583      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11584      * @type Function
11585      */
11586     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11587     /**
11588      * @private
11589      */
11590     this.successDelegate = this.processSuccess.createDelegate(this);
11591     /**
11592      * @private
11593      */
11594     this.failureDelegate = this.processFailure.createDelegate(this);
11595
11596     if(!this.renderer){
11597      /**
11598       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11599       */
11600     this.renderer = new Roo.UpdateManager.BasicRenderer();
11601     }
11602     
11603     Roo.UpdateManager.superclass.constructor.call(this);
11604 };
11605
11606 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11607     /**
11608      * Get the Element this UpdateManager is bound to
11609      * @return {Roo.Element} The element
11610      */
11611     getEl : function(){
11612         return this.el;
11613     },
11614     /**
11615      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11616      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11617 <pre><code>
11618 um.update({<br/>
11619     url: "your-url.php",<br/>
11620     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11621     callback: yourFunction,<br/>
11622     scope: yourObject, //(optional scope)  <br/>
11623     discardUrl: false, <br/>
11624     nocache: false,<br/>
11625     text: "Loading...",<br/>
11626     timeout: 30,<br/>
11627     scripts: false<br/>
11628 });
11629 </code></pre>
11630      * The only required property is url. The optional properties nocache, text and scripts
11631      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11632      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11634      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11635      */
11636     update : function(url, params, callback, discardUrl){
11637         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11638             var method = this.method, cfg;
11639             if(typeof url == "object"){ // must be config object
11640                 cfg = url;
11641                 url = cfg.url;
11642                 params = params || cfg.params;
11643                 callback = callback || cfg.callback;
11644                 discardUrl = discardUrl || cfg.discardUrl;
11645                 if(callback && cfg.scope){
11646                     callback = callback.createDelegate(cfg.scope);
11647                 }
11648                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11649                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11650                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11651                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11652                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11653             }
11654             this.showLoading();
11655             if(!discardUrl){
11656                 this.defaultUrl = url;
11657             }
11658             if(typeof url == "function"){
11659                 url = url.call(this);
11660             }
11661
11662             method = method || (params ? "POST" : "GET");
11663             if(method == "GET"){
11664                 url = this.prepareUrl(url);
11665             }
11666
11667             var o = Roo.apply(cfg ||{}, {
11668                 url : url,
11669                 params: params,
11670                 success: this.successDelegate,
11671                 failure: this.failureDelegate,
11672                 callback: undefined,
11673                 timeout: (this.timeout*1000),
11674                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11675             });
11676
11677             this.transaction = Roo.Ajax.request(o);
11678         }
11679     },
11680
11681     /**
11682      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11683      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11684      * @param {String/HTMLElement} form The form Id or form element
11685      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11686      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11687      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11688      */
11689     formUpdate : function(form, url, reset, callback){
11690         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11691             if(typeof url == "function"){
11692                 url = url.call(this);
11693             }
11694             form = Roo.getDom(form);
11695             this.transaction = Roo.Ajax.request({
11696                 form: form,
11697                 url:url,
11698                 success: this.successDelegate,
11699                 failure: this.failureDelegate,
11700                 timeout: (this.timeout*1000),
11701                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11702             });
11703             this.showLoading.defer(1, this);
11704         }
11705     },
11706
11707     /**
11708      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11709      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11710      */
11711     refresh : function(callback){
11712         if(this.defaultUrl == null){
11713             return;
11714         }
11715         this.update(this.defaultUrl, null, callback, true);
11716     },
11717
11718     /**
11719      * Set this element to auto refresh.
11720      * @param {Number} interval How often to update (in seconds).
11721      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11722      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11723      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11724      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11725      */
11726     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11727         if(refreshNow){
11728             this.update(url || this.defaultUrl, params, callback, true);
11729         }
11730         if(this.autoRefreshProcId){
11731             clearInterval(this.autoRefreshProcId);
11732         }
11733         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11734     },
11735
11736     /**
11737      * Stop auto refresh on this element.
11738      */
11739      stopAutoRefresh : function(){
11740         if(this.autoRefreshProcId){
11741             clearInterval(this.autoRefreshProcId);
11742             delete this.autoRefreshProcId;
11743         }
11744     },
11745
11746     isAutoRefreshing : function(){
11747        return this.autoRefreshProcId ? true : false;
11748     },
11749     /**
11750      * Called to update the element to "Loading" state. Override to perform custom action.
11751      */
11752     showLoading : function(){
11753         if(this.showLoadIndicator){
11754             this.el.update(this.indicatorText);
11755         }
11756     },
11757
11758     /**
11759      * Adds unique parameter to query string if disableCaching = true
11760      * @private
11761      */
11762     prepareUrl : function(url){
11763         if(this.disableCaching){
11764             var append = "_dc=" + (new Date().getTime());
11765             if(url.indexOf("?") !== -1){
11766                 url += "&" + append;
11767             }else{
11768                 url += "?" + append;
11769             }
11770         }
11771         return url;
11772     },
11773
11774     /**
11775      * @private
11776      */
11777     processSuccess : function(response){
11778         this.transaction = null;
11779         if(response.argument.form && response.argument.reset){
11780             try{ // put in try/catch since some older FF releases had problems with this
11781                 response.argument.form.reset();
11782             }catch(e){}
11783         }
11784         if(this.loadScripts){
11785             this.renderer.render(this.el, response, this,
11786                 this.updateComplete.createDelegate(this, [response]));
11787         }else{
11788             this.renderer.render(this.el, response, this);
11789             this.updateComplete(response);
11790         }
11791     },
11792
11793     updateComplete : function(response){
11794         this.fireEvent("update", this.el, response);
11795         if(typeof response.argument.callback == "function"){
11796             response.argument.callback(this.el, true, response);
11797         }
11798     },
11799
11800     /**
11801      * @private
11802      */
11803     processFailure : function(response){
11804         this.transaction = null;
11805         this.fireEvent("failure", this.el, response);
11806         if(typeof response.argument.callback == "function"){
11807             response.argument.callback(this.el, false, response);
11808         }
11809     },
11810
11811     /**
11812      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11813      * @param {Object} renderer The object implementing the render() method
11814      */
11815     setRenderer : function(renderer){
11816         this.renderer = renderer;
11817     },
11818
11819     getRenderer : function(){
11820        return this.renderer;
11821     },
11822
11823     /**
11824      * Set the defaultUrl used for updates
11825      * @param {String/Function} defaultUrl The url or a function to call to get the url
11826      */
11827     setDefaultUrl : function(defaultUrl){
11828         this.defaultUrl = defaultUrl;
11829     },
11830
11831     /**
11832      * Aborts the executing transaction
11833      */
11834     abort : function(){
11835         if(this.transaction){
11836             Roo.Ajax.abort(this.transaction);
11837         }
11838     },
11839
11840     /**
11841      * Returns true if an update is in progress
11842      * @return {Boolean}
11843      */
11844     isUpdating : function(){
11845         if(this.transaction){
11846             return Roo.Ajax.isLoading(this.transaction);
11847         }
11848         return false;
11849     }
11850 });
11851
11852 /**
11853  * @class Roo.UpdateManager.defaults
11854  * @static (not really - but it helps the doc tool)
11855  * The defaults collection enables customizing the default properties of UpdateManager
11856  */
11857    Roo.UpdateManager.defaults = {
11858        /**
11859          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11860          * @type Number
11861          */
11862          timeout : 30,
11863
11864          /**
11865          * True to process scripts by default (Defaults to false).
11866          * @type Boolean
11867          */
11868         loadScripts : false,
11869
11870         /**
11871         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11872         * @type String
11873         */
11874         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11875         /**
11876          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11877          * @type Boolean
11878          */
11879         disableCaching : false,
11880         /**
11881          * Whether to show indicatorText when loading (Defaults to true).
11882          * @type Boolean
11883          */
11884         showLoadIndicator : true,
11885         /**
11886          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11887          * @type String
11888          */
11889         indicatorText : '<div class="loading-indicator">Loading...</div>'
11890    };
11891
11892 /**
11893  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11894  *Usage:
11895  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11896  * @param {String/HTMLElement/Roo.Element} el The element to update
11897  * @param {String} url The url
11898  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11899  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11900  * @static
11901  * @deprecated
11902  * @member Roo.UpdateManager
11903  */
11904 Roo.UpdateManager.updateElement = function(el, url, params, options){
11905     var um = Roo.get(el, true).getUpdateManager();
11906     Roo.apply(um, options);
11907     um.update(url, params, options ? options.callback : null);
11908 };
11909 // alias for backwards compat
11910 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11911 /**
11912  * @class Roo.UpdateManager.BasicRenderer
11913  * Default Content renderer. Updates the elements innerHTML with the responseText.
11914  */
11915 Roo.UpdateManager.BasicRenderer = function(){};
11916
11917 Roo.UpdateManager.BasicRenderer.prototype = {
11918     /**
11919      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11920      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11921      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11922      * @param {Roo.Element} el The element being rendered
11923      * @param {Object} response The YUI Connect response object
11924      * @param {UpdateManager} updateManager The calling update manager
11925      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11926      */
11927      render : function(el, response, updateManager, callback){
11928         el.update(response.responseText, updateManager.loadScripts, callback);
11929     }
11930 };
11931 /*
11932  * Based on:
11933  * Ext JS Library 1.1.1
11934  * Copyright(c) 2006-2007, Ext JS, LLC.
11935  *
11936  * Originally Released Under LGPL - original licence link has changed is not relivant.
11937  *
11938  * Fork - LGPL
11939  * <script type="text/javascript">
11940  */
11941
11942 /**
11943  * @class Roo.util.DelayedTask
11944  * Provides a convenient method of performing setTimeout where a new
11945  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11946  * You can use this class to buffer
11947  * the keypress events for a certain number of milliseconds, and perform only if they stop
11948  * for that amount of time.
11949  * @constructor The parameters to this constructor serve as defaults and are not required.
11950  * @param {Function} fn (optional) The default function to timeout
11951  * @param {Object} scope (optional) The default scope of that timeout
11952  * @param {Array} args (optional) The default Array of arguments
11953  */
11954 Roo.util.DelayedTask = function(fn, scope, args){
11955     var id = null, d, t;
11956
11957     var call = function(){
11958         var now = new Date().getTime();
11959         if(now - t >= d){
11960             clearInterval(id);
11961             id = null;
11962             fn.apply(scope, args || []);
11963         }
11964     };
11965     /**
11966      * Cancels any pending timeout and queues a new one
11967      * @param {Number} delay The milliseconds to delay
11968      * @param {Function} newFn (optional) Overrides function passed to constructor
11969      * @param {Object} newScope (optional) Overrides scope passed to constructor
11970      * @param {Array} newArgs (optional) Overrides args passed to constructor
11971      */
11972     this.delay = function(delay, newFn, newScope, newArgs){
11973         if(id && delay != d){
11974             this.cancel();
11975         }
11976         d = delay;
11977         t = new Date().getTime();
11978         fn = newFn || fn;
11979         scope = newScope || scope;
11980         args = newArgs || args;
11981         if(!id){
11982             id = setInterval(call, d);
11983         }
11984     };
11985
11986     /**
11987      * Cancel the last queued timeout
11988      */
11989     this.cancel = function(){
11990         if(id){
11991             clearInterval(id);
11992             id = null;
11993         }
11994     };
11995 };/*
11996  * Based on:
11997  * Ext JS Library 1.1.1
11998  * Copyright(c) 2006-2007, Ext JS, LLC.
11999  *
12000  * Originally Released Under LGPL - original licence link has changed is not relivant.
12001  *
12002  * Fork - LGPL
12003  * <script type="text/javascript">
12004  */
12005  
12006  
12007 Roo.util.TaskRunner = function(interval){
12008     interval = interval || 10;
12009     var tasks = [], removeQueue = [];
12010     var id = 0;
12011     var running = false;
12012
12013     var stopThread = function(){
12014         running = false;
12015         clearInterval(id);
12016         id = 0;
12017     };
12018
12019     var startThread = function(){
12020         if(!running){
12021             running = true;
12022             id = setInterval(runTasks, interval);
12023         }
12024     };
12025
12026     var removeTask = function(task){
12027         removeQueue.push(task);
12028         if(task.onStop){
12029             task.onStop();
12030         }
12031     };
12032
12033     var runTasks = function(){
12034         if(removeQueue.length > 0){
12035             for(var i = 0, len = removeQueue.length; i < len; i++){
12036                 tasks.remove(removeQueue[i]);
12037             }
12038             removeQueue = [];
12039             if(tasks.length < 1){
12040                 stopThread();
12041                 return;
12042             }
12043         }
12044         var now = new Date().getTime();
12045         for(var i = 0, len = tasks.length; i < len; ++i){
12046             var t = tasks[i];
12047             var itime = now - t.taskRunTime;
12048             if(t.interval <= itime){
12049                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12050                 t.taskRunTime = now;
12051                 if(rt === false || t.taskRunCount === t.repeat){
12052                     removeTask(t);
12053                     return;
12054                 }
12055             }
12056             if(t.duration && t.duration <= (now - t.taskStartTime)){
12057                 removeTask(t);
12058             }
12059         }
12060     };
12061
12062     /**
12063      * Queues a new task.
12064      * @param {Object} task
12065      */
12066     this.start = function(task){
12067         tasks.push(task);
12068         task.taskStartTime = new Date().getTime();
12069         task.taskRunTime = 0;
12070         task.taskRunCount = 0;
12071         startThread();
12072         return task;
12073     };
12074
12075     this.stop = function(task){
12076         removeTask(task);
12077         return task;
12078     };
12079
12080     this.stopAll = function(){
12081         stopThread();
12082         for(var i = 0, len = tasks.length; i < len; i++){
12083             if(tasks[i].onStop){
12084                 tasks[i].onStop();
12085             }
12086         }
12087         tasks = [];
12088         removeQueue = [];
12089     };
12090 };
12091
12092 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103  
12104 /**
12105  * @class Roo.util.MixedCollection
12106  * @extends Roo.util.Observable
12107  * A Collection class that maintains both numeric indexes and keys and exposes events.
12108  * @constructor
12109  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12110  * collection (defaults to false)
12111  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12112  * and return the key value for that item.  This is used when available to look up the key on items that
12113  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12114  * equivalent to providing an implementation for the {@link #getKey} method.
12115  */
12116 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12117     this.items = [];
12118     this.map = {};
12119     this.keys = [];
12120     this.length = 0;
12121     this.addEvents({
12122         /**
12123          * @event clear
12124          * Fires when the collection is cleared.
12125          */
12126         "clear" : true,
12127         /**
12128          * @event add
12129          * Fires when an item is added to the collection.
12130          * @param {Number} index The index at which the item was added.
12131          * @param {Object} o The item added.
12132          * @param {String} key The key associated with the added item.
12133          */
12134         "add" : true,
12135         /**
12136          * @event replace
12137          * Fires when an item is replaced in the collection.
12138          * @param {String} key he key associated with the new added.
12139          * @param {Object} old The item being replaced.
12140          * @param {Object} new The new item.
12141          */
12142         "replace" : true,
12143         /**
12144          * @event remove
12145          * Fires when an item is removed from the collection.
12146          * @param {Object} o The item being removed.
12147          * @param {String} key (optional) The key associated with the removed item.
12148          */
12149         "remove" : true,
12150         "sort" : true
12151     });
12152     this.allowFunctions = allowFunctions === true;
12153     if(keyFn){
12154         this.getKey = keyFn;
12155     }
12156     Roo.util.MixedCollection.superclass.constructor.call(this);
12157 };
12158
12159 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12160     allowFunctions : false,
12161     
12162 /**
12163  * Adds an item to the collection.
12164  * @param {String} key The key to associate with the item
12165  * @param {Object} o The item to add.
12166  * @return {Object} The item added.
12167  */
12168     add : function(key, o){
12169         if(arguments.length == 1){
12170             o = arguments[0];
12171             key = this.getKey(o);
12172         }
12173         if(typeof key == "undefined" || key === null){
12174             this.length++;
12175             this.items.push(o);
12176             this.keys.push(null);
12177         }else{
12178             var old = this.map[key];
12179             if(old){
12180                 return this.replace(key, o);
12181             }
12182             this.length++;
12183             this.items.push(o);
12184             this.map[key] = o;
12185             this.keys.push(key);
12186         }
12187         this.fireEvent("add", this.length-1, o, key);
12188         return o;
12189     },
12190    
12191 /**
12192   * MixedCollection has a generic way to fetch keys if you implement getKey.
12193 <pre><code>
12194 // normal way
12195 var mc = new Roo.util.MixedCollection();
12196 mc.add(someEl.dom.id, someEl);
12197 mc.add(otherEl.dom.id, otherEl);
12198 //and so on
12199
12200 // using getKey
12201 var mc = new Roo.util.MixedCollection();
12202 mc.getKey = function(el){
12203    return el.dom.id;
12204 };
12205 mc.add(someEl);
12206 mc.add(otherEl);
12207
12208 // or via the constructor
12209 var mc = new Roo.util.MixedCollection(false, function(el){
12210    return el.dom.id;
12211 });
12212 mc.add(someEl);
12213 mc.add(otherEl);
12214 </code></pre>
12215  * @param o {Object} The item for which to find the key.
12216  * @return {Object} The key for the passed item.
12217  */
12218     getKey : function(o){
12219          return o.id; 
12220     },
12221    
12222 /**
12223  * Replaces an item in the collection.
12224  * @param {String} key The key associated with the item to replace, or the item to replace.
12225  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12226  * @return {Object}  The new item.
12227  */
12228     replace : function(key, o){
12229         if(arguments.length == 1){
12230             o = arguments[0];
12231             key = this.getKey(o);
12232         }
12233         var old = this.item(key);
12234         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12235              return this.add(key, o);
12236         }
12237         var index = this.indexOfKey(key);
12238         this.items[index] = o;
12239         this.map[key] = o;
12240         this.fireEvent("replace", key, old, o);
12241         return o;
12242     },
12243    
12244 /**
12245  * Adds all elements of an Array or an Object to the collection.
12246  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12247  * an Array of values, each of which are added to the collection.
12248  */
12249     addAll : function(objs){
12250         if(arguments.length > 1 || objs instanceof Array){
12251             var args = arguments.length > 1 ? arguments : objs;
12252             for(var i = 0, len = args.length; i < len; i++){
12253                 this.add(args[i]);
12254             }
12255         }else{
12256             for(var key in objs){
12257                 if(this.allowFunctions || typeof objs[key] != "function"){
12258                     this.add(key, objs[key]);
12259                 }
12260             }
12261         }
12262     },
12263    
12264 /**
12265  * Executes the specified function once for every item in the collection, passing each
12266  * item as the first and only parameter. returning false from the function will stop the iteration.
12267  * @param {Function} fn The function to execute for each item.
12268  * @param {Object} scope (optional) The scope in which to execute the function.
12269  */
12270     each : function(fn, scope){
12271         var items = [].concat(this.items); // each safe for removal
12272         for(var i = 0, len = items.length; i < len; i++){
12273             if(fn.call(scope || items[i], items[i], i, len) === false){
12274                 break;
12275             }
12276         }
12277     },
12278    
12279 /**
12280  * Executes the specified function once for every key in the collection, passing each
12281  * key, and its associated item as the first two parameters.
12282  * @param {Function} fn The function to execute for each item.
12283  * @param {Object} scope (optional) The scope in which to execute the function.
12284  */
12285     eachKey : function(fn, scope){
12286         for(var i = 0, len = this.keys.length; i < len; i++){
12287             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12288         }
12289     },
12290    
12291 /**
12292  * Returns the first item in the collection which elicits a true return value from the
12293  * passed selection function.
12294  * @param {Function} fn The selection function to execute for each item.
12295  * @param {Object} scope (optional) The scope in which to execute the function.
12296  * @return {Object} The first item in the collection which returned true from the selection function.
12297  */
12298     find : function(fn, scope){
12299         for(var i = 0, len = this.items.length; i < len; i++){
12300             if(fn.call(scope || window, this.items[i], this.keys[i])){
12301                 return this.items[i];
12302             }
12303         }
12304         return null;
12305     },
12306    
12307 /**
12308  * Inserts an item at the specified index in the collection.
12309  * @param {Number} index The index to insert the item at.
12310  * @param {String} key The key to associate with the new item, or the item itself.
12311  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12312  * @return {Object} The item inserted.
12313  */
12314     insert : function(index, key, o){
12315         if(arguments.length == 2){
12316             o = arguments[1];
12317             key = this.getKey(o);
12318         }
12319         if(index >= this.length){
12320             return this.add(key, o);
12321         }
12322         this.length++;
12323         this.items.splice(index, 0, o);
12324         if(typeof key != "undefined" && key != null){
12325             this.map[key] = o;
12326         }
12327         this.keys.splice(index, 0, key);
12328         this.fireEvent("add", index, o, key);
12329         return o;
12330     },
12331    
12332 /**
12333  * Removed an item from the collection.
12334  * @param {Object} o The item to remove.
12335  * @return {Object} The item removed.
12336  */
12337     remove : function(o){
12338         return this.removeAt(this.indexOf(o));
12339     },
12340    
12341 /**
12342  * Remove an item from a specified index in the collection.
12343  * @param {Number} index The index within the collection of the item to remove.
12344  */
12345     removeAt : function(index){
12346         if(index < this.length && index >= 0){
12347             this.length--;
12348             var o = this.items[index];
12349             this.items.splice(index, 1);
12350             var key = this.keys[index];
12351             if(typeof key != "undefined"){
12352                 delete this.map[key];
12353             }
12354             this.keys.splice(index, 1);
12355             this.fireEvent("remove", o, key);
12356         }
12357     },
12358    
12359 /**
12360  * Removed an item associated with the passed key fom the collection.
12361  * @param {String} key The key of the item to remove.
12362  */
12363     removeKey : function(key){
12364         return this.removeAt(this.indexOfKey(key));
12365     },
12366    
12367 /**
12368  * Returns the number of items in the collection.
12369  * @return {Number} the number of items in the collection.
12370  */
12371     getCount : function(){
12372         return this.length; 
12373     },
12374    
12375 /**
12376  * Returns index within the collection of the passed Object.
12377  * @param {Object} o The item to find the index of.
12378  * @return {Number} index of the item.
12379  */
12380     indexOf : function(o){
12381         if(!this.items.indexOf){
12382             for(var i = 0, len = this.items.length; i < len; i++){
12383                 if(this.items[i] == o) return i;
12384             }
12385             return -1;
12386         }else{
12387             return this.items.indexOf(o);
12388         }
12389     },
12390    
12391 /**
12392  * Returns index within the collection of the passed key.
12393  * @param {String} key The key to find the index of.
12394  * @return {Number} index of the key.
12395  */
12396     indexOfKey : function(key){
12397         if(!this.keys.indexOf){
12398             for(var i = 0, len = this.keys.length; i < len; i++){
12399                 if(this.keys[i] == key) return i;
12400             }
12401             return -1;
12402         }else{
12403             return this.keys.indexOf(key);
12404         }
12405     },
12406    
12407 /**
12408  * Returns the item associated with the passed key OR index. Key has priority over index.
12409  * @param {String/Number} key The key or index of the item.
12410  * @return {Object} The item associated with the passed key.
12411  */
12412     item : function(key){
12413         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12414         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12415     },
12416     
12417 /**
12418  * Returns the item at the specified index.
12419  * @param {Number} index The index of the item.
12420  * @return {Object}
12421  */
12422     itemAt : function(index){
12423         return this.items[index];
12424     },
12425     
12426 /**
12427  * Returns the item associated with the passed key.
12428  * @param {String/Number} key The key of the item.
12429  * @return {Object} The item associated with the passed key.
12430  */
12431     key : function(key){
12432         return this.map[key];
12433     },
12434    
12435 /**
12436  * Returns true if the collection contains the passed Object as an item.
12437  * @param {Object} o  The Object to look for in the collection.
12438  * @return {Boolean} True if the collection contains the Object as an item.
12439  */
12440     contains : function(o){
12441         return this.indexOf(o) != -1;
12442     },
12443    
12444 /**
12445  * Returns true if the collection contains the passed Object as a key.
12446  * @param {String} key The key to look for in the collection.
12447  * @return {Boolean} True if the collection contains the Object as a key.
12448  */
12449     containsKey : function(key){
12450         return typeof this.map[key] != "undefined";
12451     },
12452    
12453 /**
12454  * Removes all items from the collection.
12455  */
12456     clear : function(){
12457         this.length = 0;
12458         this.items = [];
12459         this.keys = [];
12460         this.map = {};
12461         this.fireEvent("clear");
12462     },
12463    
12464 /**
12465  * Returns the first item in the collection.
12466  * @return {Object} the first item in the collection..
12467  */
12468     first : function(){
12469         return this.items[0]; 
12470     },
12471    
12472 /**
12473  * Returns the last item in the collection.
12474  * @return {Object} the last item in the collection..
12475  */
12476     last : function(){
12477         return this.items[this.length-1];   
12478     },
12479     
12480     _sort : function(property, dir, fn){
12481         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12482         fn = fn || function(a, b){
12483             return a-b;
12484         };
12485         var c = [], k = this.keys, items = this.items;
12486         for(var i = 0, len = items.length; i < len; i++){
12487             c[c.length] = {key: k[i], value: items[i], index: i};
12488         }
12489         c.sort(function(a, b){
12490             var v = fn(a[property], b[property]) * dsc;
12491             if(v == 0){
12492                 v = (a.index < b.index ? -1 : 1);
12493             }
12494             return v;
12495         });
12496         for(var i = 0, len = c.length; i < len; i++){
12497             items[i] = c[i].value;
12498             k[i] = c[i].key;
12499         }
12500         this.fireEvent("sort", this);
12501     },
12502     
12503     /**
12504      * Sorts this collection with the passed comparison function
12505      * @param {String} direction (optional) "ASC" or "DESC"
12506      * @param {Function} fn (optional) comparison function
12507      */
12508     sort : function(dir, fn){
12509         this._sort("value", dir, fn);
12510     },
12511     
12512     /**
12513      * Sorts this collection by keys
12514      * @param {String} direction (optional) "ASC" or "DESC"
12515      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12516      */
12517     keySort : function(dir, fn){
12518         this._sort("key", dir, fn || function(a, b){
12519             return String(a).toUpperCase()-String(b).toUpperCase();
12520         });
12521     },
12522     
12523     /**
12524      * Returns a range of items in this collection
12525      * @param {Number} startIndex (optional) defaults to 0
12526      * @param {Number} endIndex (optional) default to the last item
12527      * @return {Array} An array of items
12528      */
12529     getRange : function(start, end){
12530         var items = this.items;
12531         if(items.length < 1){
12532             return [];
12533         }
12534         start = start || 0;
12535         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12536         var r = [];
12537         if(start <= end){
12538             for(var i = start; i <= end; i++) {
12539                     r[r.length] = items[i];
12540             }
12541         }else{
12542             for(var i = start; i >= end; i--) {
12543                     r[r.length] = items[i];
12544             }
12545         }
12546         return r;
12547     },
12548         
12549     /**
12550      * Filter the <i>objects</i> in this collection by a specific property. 
12551      * Returns a new collection that has been filtered.
12552      * @param {String} property A property on your objects
12553      * @param {String/RegExp} value Either string that the property values 
12554      * should start with or a RegExp to test against the property
12555      * @return {MixedCollection} The new filtered collection
12556      */
12557     filter : function(property, value){
12558         if(!value.exec){ // not a regex
12559             value = String(value);
12560             if(value.length == 0){
12561                 return this.clone();
12562             }
12563             value = new RegExp("^" + Roo.escapeRe(value), "i");
12564         }
12565         return this.filterBy(function(o){
12566             return o && value.test(o[property]);
12567         });
12568         },
12569     
12570     /**
12571      * Filter by a function. * Returns a new collection that has been filtered.
12572      * The passed function will be called with each 
12573      * object in the collection. If the function returns true, the value is included 
12574      * otherwise it is filtered.
12575      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12576      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12577      * @return {MixedCollection} The new filtered collection
12578      */
12579     filterBy : function(fn, scope){
12580         var r = new Roo.util.MixedCollection();
12581         r.getKey = this.getKey;
12582         var k = this.keys, it = this.items;
12583         for(var i = 0, len = it.length; i < len; i++){
12584             if(fn.call(scope||this, it[i], k[i])){
12585                                 r.add(k[i], it[i]);
12586                         }
12587         }
12588         return r;
12589     },
12590     
12591     /**
12592      * Creates a duplicate of this collection
12593      * @return {MixedCollection}
12594      */
12595     clone : function(){
12596         var r = new Roo.util.MixedCollection();
12597         var k = this.keys, it = this.items;
12598         for(var i = 0, len = it.length; i < len; i++){
12599             r.add(k[i], it[i]);
12600         }
12601         r.getKey = this.getKey;
12602         return r;
12603     }
12604 });
12605 /**
12606  * Returns the item associated with the passed key or index.
12607  * @method
12608  * @param {String/Number} key The key or index of the item.
12609  * @return {Object} The item associated with the passed key.
12610  */
12611 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12612  * Based on:
12613  * Ext JS Library 1.1.1
12614  * Copyright(c) 2006-2007, Ext JS, LLC.
12615  *
12616  * Originally Released Under LGPL - original licence link has changed is not relivant.
12617  *
12618  * Fork - LGPL
12619  * <script type="text/javascript">
12620  */
12621 /**
12622  * @class Roo.util.JSON
12623  * Modified version of Douglas Crockford"s json.js that doesn"t
12624  * mess with the Object prototype 
12625  * http://www.json.org/js.html
12626  * @singleton
12627  */
12628 Roo.util.JSON = new (function(){
12629     var useHasOwn = {}.hasOwnProperty ? true : false;
12630     
12631     // crashes Safari in some instances
12632     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12633     
12634     var pad = function(n) {
12635         return n < 10 ? "0" + n : n;
12636     };
12637     
12638     var m = {
12639         "\b": '\\b',
12640         "\t": '\\t',
12641         "\n": '\\n',
12642         "\f": '\\f',
12643         "\r": '\\r',
12644         '"' : '\\"',
12645         "\\": '\\\\'
12646     };
12647
12648     var encodeString = function(s){
12649         if (/["\\\x00-\x1f]/.test(s)) {
12650             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12651                 var c = m[b];
12652                 if(c){
12653                     return c;
12654                 }
12655                 c = b.charCodeAt();
12656                 return "\\u00" +
12657                     Math.floor(c / 16).toString(16) +
12658                     (c % 16).toString(16);
12659             }) + '"';
12660         }
12661         return '"' + s + '"';
12662     };
12663     
12664     var encodeArray = function(o){
12665         var a = ["["], b, i, l = o.length, v;
12666             for (i = 0; i < l; i += 1) {
12667                 v = o[i];
12668                 switch (typeof v) {
12669                     case "undefined":
12670                     case "function":
12671                     case "unknown":
12672                         break;
12673                     default:
12674                         if (b) {
12675                             a.push(',');
12676                         }
12677                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12678                         b = true;
12679                 }
12680             }
12681             a.push("]");
12682             return a.join("");
12683     };
12684     
12685     var encodeDate = function(o){
12686         return '"' + o.getFullYear() + "-" +
12687                 pad(o.getMonth() + 1) + "-" +
12688                 pad(o.getDate()) + "T" +
12689                 pad(o.getHours()) + ":" +
12690                 pad(o.getMinutes()) + ":" +
12691                 pad(o.getSeconds()) + '"';
12692     };
12693     
12694     /**
12695      * Encodes an Object, Array or other value
12696      * @param {Mixed} o The variable to encode
12697      * @return {String} The JSON string
12698      */
12699     this.encode = function(o){
12700         if(typeof o == "undefined" || o === null){
12701             return "null";
12702         }else if(o instanceof Array){
12703             return encodeArray(o);
12704         }else if(o instanceof Date){
12705             return encodeDate(o);
12706         }else if(typeof o == "string"){
12707             return encodeString(o);
12708         }else if(typeof o == "number"){
12709             return isFinite(o) ? String(o) : "null";
12710         }else if(typeof o == "boolean"){
12711             return String(o);
12712         }else {
12713             var a = ["{"], b, i, v;
12714             for (i in o) {
12715                 if(!useHasOwn || o.hasOwnProperty(i)) {
12716                     v = o[i];
12717                     switch (typeof v) {
12718                     case "undefined":
12719                     case "function":
12720                     case "unknown":
12721                         break;
12722                     default:
12723                         if(b){
12724                             a.push(',');
12725                         }
12726                         a.push(this.encode(i), ":",
12727                                 v === null ? "null" : this.encode(v));
12728                         b = true;
12729                     }
12730                 }
12731             }
12732             a.push("}");
12733             return a.join("");
12734         }
12735     };
12736     
12737     /**
12738      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12739      * @param {String} json The JSON string
12740      * @return {Object} The resulting object
12741      */
12742     this.decode = function(json){
12743         /**
12744          * eval:var:json
12745          */
12746         return eval("(" + json + ')');
12747     };
12748 })();
12749 /** 
12750  * Shorthand for {@link Roo.util.JSON#encode}
12751  * @member Roo encode 
12752  * @method */
12753 Roo.encode = Roo.util.JSON.encode;
12754 /** 
12755  * Shorthand for {@link Roo.util.JSON#decode}
12756  * @member Roo decode 
12757  * @method */
12758 Roo.decode = Roo.util.JSON.decode;
12759 /*
12760  * Based on:
12761  * Ext JS Library 1.1.1
12762  * Copyright(c) 2006-2007, Ext JS, LLC.
12763  *
12764  * Originally Released Under LGPL - original licence link has changed is not relivant.
12765  *
12766  * Fork - LGPL
12767  * <script type="text/javascript">
12768  */
12769  
12770 /**
12771  * @class Roo.util.Format
12772  * Reusable data formatting functions
12773  * @singleton
12774  */
12775 Roo.util.Format = function(){
12776     var trimRe = /^\s+|\s+$/g;
12777     return {
12778         /**
12779          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12780          * @param {String} value The string to truncate
12781          * @param {Number} length The maximum length to allow before truncating
12782          * @return {String} The converted text
12783          */
12784         ellipsis : function(value, len){
12785             if(value && value.length > len){
12786                 return value.substr(0, len-3)+"...";
12787             }
12788             return value;
12789         },
12790
12791         /**
12792          * Checks a reference and converts it to empty string if it is undefined
12793          * @param {Mixed} value Reference to check
12794          * @return {Mixed} Empty string if converted, otherwise the original value
12795          */
12796         undef : function(value){
12797             return typeof value != "undefined" ? value : "";
12798         },
12799
12800         /**
12801          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12802          * @param {String} value The string to encode
12803          * @return {String} The encoded text
12804          */
12805         htmlEncode : function(value){
12806             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12807         },
12808
12809         /**
12810          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12811          * @param {String} value The string to decode
12812          * @return {String} The decoded text
12813          */
12814         htmlDecode : function(value){
12815             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12816         },
12817
12818         /**
12819          * Trims any whitespace from either side of a string
12820          * @param {String} value The text to trim
12821          * @return {String} The trimmed text
12822          */
12823         trim : function(value){
12824             return String(value).replace(trimRe, "");
12825         },
12826
12827         /**
12828          * Returns a substring from within an original string
12829          * @param {String} value The original text
12830          * @param {Number} start The start index of the substring
12831          * @param {Number} length The length of the substring
12832          * @return {String} The substring
12833          */
12834         substr : function(value, start, length){
12835             return String(value).substr(start, length);
12836         },
12837
12838         /**
12839          * Converts a string to all lower case letters
12840          * @param {String} value The text to convert
12841          * @return {String} The converted text
12842          */
12843         lowercase : function(value){
12844             return String(value).toLowerCase();
12845         },
12846
12847         /**
12848          * Converts a string to all upper case letters
12849          * @param {String} value The text to convert
12850          * @return {String} The converted text
12851          */
12852         uppercase : function(value){
12853             return String(value).toUpperCase();
12854         },
12855
12856         /**
12857          * Converts the first character only of a string to upper case
12858          * @param {String} value The text to convert
12859          * @return {String} The converted text
12860          */
12861         capitalize : function(value){
12862             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12863         },
12864
12865         // private
12866         call : function(value, fn){
12867             if(arguments.length > 2){
12868                 var args = Array.prototype.slice.call(arguments, 2);
12869                 args.unshift(value);
12870                  
12871                 return /** eval:var:value */  eval(fn).apply(window, args);
12872             }else{
12873                 /** eval:var:value */
12874                 return /** eval:var:value */ eval(fn).call(window, value);
12875             }
12876         },
12877
12878         /**
12879          * Format a number as US currency
12880          * @param {Number/String} value The numeric value to format
12881          * @return {String} The formatted currency string
12882          */
12883         usMoney : function(v){
12884             v = (Math.round((v-0)*100))/100;
12885             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12886             v = String(v);
12887             var ps = v.split('.');
12888             var whole = ps[0];
12889             var sub = ps[1] ? '.'+ ps[1] : '.00';
12890             var r = /(\d+)(\d{3})/;
12891             while (r.test(whole)) {
12892                 whole = whole.replace(r, '$1' + ',' + '$2');
12893             }
12894             return "$" + whole + sub ;
12895         },
12896
12897         /**
12898          * Parse a value into a formatted date using the specified format pattern.
12899          * @param {Mixed} value The value to format
12900          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12901          * @return {String} The formatted date string
12902          */
12903         date : function(v, format){
12904             if(!v){
12905                 return "";
12906             }
12907             if(!(v instanceof Date)){
12908                 v = new Date(Date.parse(v));
12909             }
12910             return v.dateFormat(format || "m/d/Y");
12911         },
12912
12913         /**
12914          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12915          * @param {String} format Any valid date format string
12916          * @return {Function} The date formatting function
12917          */
12918         dateRenderer : function(format){
12919             return function(v){
12920                 return Roo.util.Format.date(v, format);  
12921             };
12922         },
12923
12924         // private
12925         stripTagsRE : /<\/?[^>]+>/gi,
12926         
12927         /**
12928          * Strips all HTML tags
12929          * @param {Mixed} value The text from which to strip tags
12930          * @return {String} The stripped text
12931          */
12932         stripTags : function(v){
12933             return !v ? v : String(v).replace(this.stripTagsRE, "");
12934         }
12935     };
12936 }();/*
12937  * Based on:
12938  * Ext JS Library 1.1.1
12939  * Copyright(c) 2006-2007, Ext JS, LLC.
12940  *
12941  * Originally Released Under LGPL - original licence link has changed is not relivant.
12942  *
12943  * Fork - LGPL
12944  * <script type="text/javascript">
12945  */
12946
12947
12948  
12949
12950 /**
12951  * @class Roo.MasterTemplate
12952  * @extends Roo.Template
12953  * Provides a template that can have child templates. The syntax is:
12954 <pre><code>
12955 var t = new Roo.MasterTemplate(
12956         '&lt;select name="{name}"&gt;',
12957                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12958         '&lt;/select&gt;'
12959 );
12960 t.add('options', {value: 'foo', text: 'bar'});
12961 // or you can add multiple child elements in one shot
12962 t.addAll('options', [
12963     {value: 'foo', text: 'bar'},
12964     {value: 'foo2', text: 'bar2'},
12965     {value: 'foo3', text: 'bar3'}
12966 ]);
12967 // then append, applying the master template values
12968 t.append('my-form', {name: 'my-select'});
12969 </code></pre>
12970 * A name attribute for the child template is not required if you have only one child
12971 * template or you want to refer to them by index.
12972  */
12973 Roo.MasterTemplate = function(){
12974     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12975     this.originalHtml = this.html;
12976     var st = {};
12977     var m, re = this.subTemplateRe;
12978     re.lastIndex = 0;
12979     var subIndex = 0;
12980     while(m = re.exec(this.html)){
12981         var name = m[1], content = m[2];
12982         st[subIndex] = {
12983             name: name,
12984             index: subIndex,
12985             buffer: [],
12986             tpl : new Roo.Template(content)
12987         };
12988         if(name){
12989             st[name] = st[subIndex];
12990         }
12991         st[subIndex].tpl.compile();
12992         st[subIndex].tpl.call = this.call.createDelegate(this);
12993         subIndex++;
12994     }
12995     this.subCount = subIndex;
12996     this.subs = st;
12997 };
12998 Roo.extend(Roo.MasterTemplate, Roo.Template, {
12999     /**
13000     * The regular expression used to match sub templates
13001     * @type RegExp
13002     * @property
13003     */
13004     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13005
13006     /**
13007      * Applies the passed values to a child template.
13008      * @param {String/Number} name (optional) The name or index of the child template
13009      * @param {Array/Object} values The values to be applied to the template
13010      * @return {MasterTemplate} this
13011      */
13012      add : function(name, values){
13013         if(arguments.length == 1){
13014             values = arguments[0];
13015             name = 0;
13016         }
13017         var s = this.subs[name];
13018         s.buffer[s.buffer.length] = s.tpl.apply(values);
13019         return this;
13020     },
13021
13022     /**
13023      * Applies all the passed values to a child template.
13024      * @param {String/Number} name (optional) The name or index of the child template
13025      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13026      * @param {Boolean} reset (optional) True to reset the template first
13027      * @return {MasterTemplate} this
13028      */
13029     fill : function(name, values, reset){
13030         var a = arguments;
13031         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13032             values = a[0];
13033             name = 0;
13034             reset = a[1];
13035         }
13036         if(reset){
13037             this.reset();
13038         }
13039         for(var i = 0, len = values.length; i < len; i++){
13040             this.add(name, values[i]);
13041         }
13042         return this;
13043     },
13044
13045     /**
13046      * Resets the template for reuse
13047      * @return {MasterTemplate} this
13048      */
13049      reset : function(){
13050         var s = this.subs;
13051         for(var i = 0; i < this.subCount; i++){
13052             s[i].buffer = [];
13053         }
13054         return this;
13055     },
13056
13057     applyTemplate : function(values){
13058         var s = this.subs;
13059         var replaceIndex = -1;
13060         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13061             return s[++replaceIndex].buffer.join("");
13062         });
13063         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13064     },
13065
13066     apply : function(){
13067         return this.applyTemplate.apply(this, arguments);
13068     },
13069
13070     compile : function(){return this;}
13071 });
13072
13073 /**
13074  * Alias for fill().
13075  * @method
13076  */
13077 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13078  /**
13079  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13080  * var tpl = Roo.MasterTemplate.from('element-id');
13081  * @param {String/HTMLElement} el
13082  * @param {Object} config
13083  * @static
13084  */
13085 Roo.MasterTemplate.from = function(el, config){
13086     el = Roo.getDom(el);
13087     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13088 };/*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098
13099  
13100 /**
13101  * @class Roo.util.CSS
13102  * Utility class for manipulating CSS rules
13103  * @singleton
13104  */
13105 Roo.util.CSS = function(){
13106         var rules = null;
13107         var doc = document;
13108
13109     var camelRe = /(-[a-z])/gi;
13110     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13111
13112    return {
13113    /**
13114     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13115     * tag and appended to the HEAD of the document.
13116     * @param {String} cssText The text containing the css rules
13117     * @param {String} id An id to add to the stylesheet for later removal
13118     * @return {StyleSheet}
13119     */
13120    createStyleSheet : function(cssText, id){
13121        var ss;
13122        var head = doc.getElementsByTagName("head")[0];
13123        var rules = doc.createElement("style");
13124        rules.setAttribute("type", "text/css");
13125        if(id){
13126            rules.setAttribute("id", id);
13127        }
13128        if(Roo.isIE){
13129            head.appendChild(rules);
13130            ss = rules.styleSheet;
13131            ss.cssText = cssText;
13132        }else{
13133            try{
13134                 rules.appendChild(doc.createTextNode(cssText));
13135            }catch(e){
13136                rules.cssText = cssText; 
13137            }
13138            head.appendChild(rules);
13139            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13140        }
13141        this.cacheStyleSheet(ss);
13142        return ss;
13143    },
13144
13145    /**
13146     * Removes a style or link tag by id
13147     * @param {String} id The id of the tag
13148     */
13149    removeStyleSheet : function(id){
13150        var existing = doc.getElementById(id);
13151        if(existing){
13152            existing.parentNode.removeChild(existing);
13153        }
13154    },
13155
13156    /**
13157     * Dynamically swaps an existing stylesheet reference for a new one
13158     * @param {String} id The id of an existing link tag to remove
13159     * @param {String} url The href of the new stylesheet to include
13160     */
13161    swapStyleSheet : function(id, url){
13162        this.removeStyleSheet(id);
13163        var ss = doc.createElement("link");
13164        ss.setAttribute("rel", "stylesheet");
13165        ss.setAttribute("type", "text/css");
13166        ss.setAttribute("id", id);
13167        ss.setAttribute("href", url);
13168        doc.getElementsByTagName("head")[0].appendChild(ss);
13169    },
13170    
13171    /**
13172     * Refresh the rule cache if you have dynamically added stylesheets
13173     * @return {Object} An object (hash) of rules indexed by selector
13174     */
13175    refreshCache : function(){
13176        return this.getRules(true);
13177    },
13178
13179    // private
13180    cacheStyleSheet : function(ss){
13181        if(!rules){
13182            rules = {};
13183        }
13184        try{// try catch for cross domain access issue
13185            var ssRules = ss.cssRules || ss.rules;
13186            for(var j = ssRules.length-1; j >= 0; --j){
13187                rules[ssRules[j].selectorText] = ssRules[j];
13188            }
13189        }catch(e){}
13190    },
13191    
13192    /**
13193     * Gets all css rules for the document
13194     * @param {Boolean} refreshCache true to refresh the internal cache
13195     * @return {Object} An object (hash) of rules indexed by selector
13196     */
13197    getRules : function(refreshCache){
13198                 if(rules == null || refreshCache){
13199                         rules = {};
13200                         var ds = doc.styleSheets;
13201                         for(var i =0, len = ds.length; i < len; i++){
13202                             try{
13203                         this.cacheStyleSheet(ds[i]);
13204                     }catch(e){} 
13205                 }
13206                 }
13207                 return rules;
13208         },
13209         
13210         /**
13211     * Gets an an individual CSS rule by selector(s)
13212     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13213     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13214     * @return {CSSRule} The CSS rule or null if one is not found
13215     */
13216    getRule : function(selector, refreshCache){
13217                 var rs = this.getRules(refreshCache);
13218                 if(!(selector instanceof Array)){
13219                     return rs[selector];
13220                 }
13221                 for(var i = 0; i < selector.length; i++){
13222                         if(rs[selector[i]]){
13223                                 return rs[selector[i]];
13224                         }
13225                 }
13226                 return null;
13227         },
13228         
13229         
13230         /**
13231     * Updates a rule property
13232     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13233     * @param {String} property The css property
13234     * @param {String} value The new value for the property
13235     * @return {Boolean} true If a rule was found and updated
13236     */
13237    updateRule : function(selector, property, value){
13238                 if(!(selector instanceof Array)){
13239                         var rule = this.getRule(selector);
13240                         if(rule){
13241                                 rule.style[property.replace(camelRe, camelFn)] = value;
13242                                 return true;
13243                         }
13244                 }else{
13245                         for(var i = 0; i < selector.length; i++){
13246                                 if(this.updateRule(selector[i], property, value)){
13247                                         return true;
13248                                 }
13249                         }
13250                 }
13251                 return false;
13252         }
13253    };   
13254 }();/*
13255  * Based on:
13256  * Ext JS Library 1.1.1
13257  * Copyright(c) 2006-2007, Ext JS, LLC.
13258  *
13259  * Originally Released Under LGPL - original licence link has changed is not relivant.
13260  *
13261  * Fork - LGPL
13262  * <script type="text/javascript">
13263  */
13264
13265  
13266
13267 /**
13268  * @class Roo.util.ClickRepeater
13269  * @extends Roo.util.Observable
13270  * 
13271  * A wrapper class which can be applied to any element. Fires a "click" event while the
13272  * mouse is pressed. The interval between firings may be specified in the config but
13273  * defaults to 10 milliseconds.
13274  * 
13275  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13276  * 
13277  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13278  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13279  * Similar to an autorepeat key delay.
13280  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13281  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13282  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13283  *           "interval" and "delay" are ignored. "immediate" is honored.
13284  * @cfg {Boolean} preventDefault True to prevent the default click event
13285  * @cfg {Boolean} stopDefault True to stop the default click event
13286  * 
13287  * @history
13288  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13289  *     2007-02-02 jvs Renamed to ClickRepeater
13290  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13291  *
13292  *  @constructor
13293  * @param {String/HTMLElement/Element} el The element to listen on
13294  * @param {Object} config
13295  **/
13296 Roo.util.ClickRepeater = function(el, config)
13297 {
13298     this.el = Roo.get(el);
13299     this.el.unselectable();
13300
13301     Roo.apply(this, config);
13302
13303     this.addEvents({
13304     /**
13305      * @event mousedown
13306      * Fires when the mouse button is depressed.
13307      * @param {Roo.util.ClickRepeater} this
13308      */
13309         "mousedown" : true,
13310     /**
13311      * @event click
13312      * Fires on a specified interval during the time the element is pressed.
13313      * @param {Roo.util.ClickRepeater} this
13314      */
13315         "click" : true,
13316     /**
13317      * @event mouseup
13318      * Fires when the mouse key is released.
13319      * @param {Roo.util.ClickRepeater} this
13320      */
13321         "mouseup" : true
13322     });
13323
13324     this.el.on("mousedown", this.handleMouseDown, this);
13325     if(this.preventDefault || this.stopDefault){
13326         this.el.on("click", function(e){
13327             if(this.preventDefault){
13328                 e.preventDefault();
13329             }
13330             if(this.stopDefault){
13331                 e.stopEvent();
13332             }
13333         }, this);
13334     }
13335
13336     // allow inline handler
13337     if(this.handler){
13338         this.on("click", this.handler,  this.scope || this);
13339     }
13340
13341     Roo.util.ClickRepeater.superclass.constructor.call(this);
13342 };
13343
13344 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13345     interval : 20,
13346     delay: 250,
13347     preventDefault : true,
13348     stopDefault : false,
13349     timer : 0,
13350
13351     // private
13352     handleMouseDown : function(){
13353         clearTimeout(this.timer);
13354         this.el.blur();
13355         if(this.pressClass){
13356             this.el.addClass(this.pressClass);
13357         }
13358         this.mousedownTime = new Date();
13359
13360         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13361         this.el.on("mouseout", this.handleMouseOut, this);
13362
13363         this.fireEvent("mousedown", this);
13364         this.fireEvent("click", this);
13365         
13366         this.timer = this.click.defer(this.delay || this.interval, this);
13367     },
13368
13369     // private
13370     click : function(){
13371         this.fireEvent("click", this);
13372         this.timer = this.click.defer(this.getInterval(), this);
13373     },
13374
13375     // private
13376     getInterval: function(){
13377         if(!this.accelerate){
13378             return this.interval;
13379         }
13380         var pressTime = this.mousedownTime.getElapsed();
13381         if(pressTime < 500){
13382             return 400;
13383         }else if(pressTime < 1700){
13384             return 320;
13385         }else if(pressTime < 2600){
13386             return 250;
13387         }else if(pressTime < 3500){
13388             return 180;
13389         }else if(pressTime < 4400){
13390             return 140;
13391         }else if(pressTime < 5300){
13392             return 80;
13393         }else if(pressTime < 6200){
13394             return 50;
13395         }else{
13396             return 10;
13397         }
13398     },
13399
13400     // private
13401     handleMouseOut : function(){
13402         clearTimeout(this.timer);
13403         if(this.pressClass){
13404             this.el.removeClass(this.pressClass);
13405         }
13406         this.el.on("mouseover", this.handleMouseReturn, this);
13407     },
13408
13409     // private
13410     handleMouseReturn : function(){
13411         this.el.un("mouseover", this.handleMouseReturn);
13412         if(this.pressClass){
13413             this.el.addClass(this.pressClass);
13414         }
13415         this.click();
13416     },
13417
13418     // private
13419     handleMouseUp : function(){
13420         clearTimeout(this.timer);
13421         this.el.un("mouseover", this.handleMouseReturn);
13422         this.el.un("mouseout", this.handleMouseOut);
13423         Roo.get(document).un("mouseup", this.handleMouseUp);
13424         this.el.removeClass(this.pressClass);
13425         this.fireEvent("mouseup", this);
13426     }
13427 });/*
13428  * Based on:
13429  * Ext JS Library 1.1.1
13430  * Copyright(c) 2006-2007, Ext JS, LLC.
13431  *
13432  * Originally Released Under LGPL - original licence link has changed is not relivant.
13433  *
13434  * Fork - LGPL
13435  * <script type="text/javascript">
13436  */
13437
13438  
13439 /**
13440  * @class Roo.KeyNav
13441  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13442  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13443  * way to implement custom navigation schemes for any UI component.</p>
13444  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13445  * pageUp, pageDown, del, home, end.  Usage:</p>
13446  <pre><code>
13447 var nav = new Roo.KeyNav("my-element", {
13448     "left" : function(e){
13449         this.moveLeft(e.ctrlKey);
13450     },
13451     "right" : function(e){
13452         this.moveRight(e.ctrlKey);
13453     },
13454     "enter" : function(e){
13455         this.save();
13456     },
13457     scope : this
13458 });
13459 </code></pre>
13460  * @constructor
13461  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13462  * @param {Object} config The config
13463  */
13464 Roo.KeyNav = function(el, config){
13465     this.el = Roo.get(el);
13466     Roo.apply(this, config);
13467     if(!this.disabled){
13468         this.disabled = true;
13469         this.enable();
13470     }
13471 };
13472
13473 Roo.KeyNav.prototype = {
13474     /**
13475      * @cfg {Boolean} disabled
13476      * True to disable this KeyNav instance (defaults to false)
13477      */
13478     disabled : false,
13479     /**
13480      * @cfg {String} defaultEventAction
13481      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13482      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13483      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13484      */
13485     defaultEventAction: "stopEvent",
13486     /**
13487      * @cfg {Boolean} forceKeyDown
13488      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13489      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13490      * handle keydown instead of keypress.
13491      */
13492     forceKeyDown : false,
13493
13494     // private
13495     prepareEvent : function(e){
13496         var k = e.getKey();
13497         var h = this.keyToHandler[k];
13498         //if(h && this[h]){
13499         //    e.stopPropagation();
13500         //}
13501         if(Roo.isSafari && h && k >= 37 && k <= 40){
13502             e.stopEvent();
13503         }
13504     },
13505
13506     // private
13507     relay : function(e){
13508         var k = e.getKey();
13509         var h = this.keyToHandler[k];
13510         if(h && this[h]){
13511             if(this.doRelay(e, this[h], h) !== true){
13512                 e[this.defaultEventAction]();
13513             }
13514         }
13515     },
13516
13517     // private
13518     doRelay : function(e, h, hname){
13519         return h.call(this.scope || this, e);
13520     },
13521
13522     // possible handlers
13523     enter : false,
13524     left : false,
13525     right : false,
13526     up : false,
13527     down : false,
13528     tab : false,
13529     esc : false,
13530     pageUp : false,
13531     pageDown : false,
13532     del : false,
13533     home : false,
13534     end : false,
13535
13536     // quick lookup hash
13537     keyToHandler : {
13538         37 : "left",
13539         39 : "right",
13540         38 : "up",
13541         40 : "down",
13542         33 : "pageUp",
13543         34 : "pageDown",
13544         46 : "del",
13545         36 : "home",
13546         35 : "end",
13547         13 : "enter",
13548         27 : "esc",
13549         9  : "tab"
13550     },
13551
13552         /**
13553          * Enable this KeyNav
13554          */
13555         enable: function(){
13556                 if(this.disabled){
13557             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13558             // the EventObject will normalize Safari automatically
13559             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13560                 this.el.on("keydown", this.relay,  this);
13561             }else{
13562                 this.el.on("keydown", this.prepareEvent,  this);
13563                 this.el.on("keypress", this.relay,  this);
13564             }
13565                     this.disabled = false;
13566                 }
13567         },
13568
13569         /**
13570          * Disable this KeyNav
13571          */
13572         disable: function(){
13573                 if(!this.disabled){
13574                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13575                 this.el.un("keydown", this.relay);
13576             }else{
13577                 this.el.un("keydown", this.prepareEvent);
13578                 this.el.un("keypress", this.relay);
13579             }
13580                     this.disabled = true;
13581                 }
13582         }
13583 };/*
13584  * Based on:
13585  * Ext JS Library 1.1.1
13586  * Copyright(c) 2006-2007, Ext JS, LLC.
13587  *
13588  * Originally Released Under LGPL - original licence link has changed is not relivant.
13589  *
13590  * Fork - LGPL
13591  * <script type="text/javascript">
13592  */
13593
13594  
13595 /**
13596  * @class Roo.KeyMap
13597  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13598  * The constructor accepts the same config object as defined by {@link #addBinding}.
13599  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13600  * combination it will call the function with this signature (if the match is a multi-key
13601  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13602  * A KeyMap can also handle a string representation of keys.<br />
13603  * Usage:
13604  <pre><code>
13605 // map one key by key code
13606 var map = new Roo.KeyMap("my-element", {
13607     key: 13, // or Roo.EventObject.ENTER
13608     fn: myHandler,
13609     scope: myObject
13610 });
13611
13612 // map multiple keys to one action by string
13613 var map = new Roo.KeyMap("my-element", {
13614     key: "a\r\n\t",
13615     fn: myHandler,
13616     scope: myObject
13617 });
13618
13619 // map multiple keys to multiple actions by strings and array of codes
13620 var map = new Roo.KeyMap("my-element", [
13621     {
13622         key: [10,13],
13623         fn: function(){ alert("Return was pressed"); }
13624     }, {
13625         key: "abc",
13626         fn: function(){ alert('a, b or c was pressed'); }
13627     }, {
13628         key: "\t",
13629         ctrl:true,
13630         shift:true,
13631         fn: function(){ alert('Control + shift + tab was pressed.'); }
13632     }
13633 ]);
13634 </code></pre>
13635  * <b>Note: A KeyMap starts enabled</b>
13636  * @constructor
13637  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13638  * @param {Object} config The config (see {@link #addBinding})
13639  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13640  */
13641 Roo.KeyMap = function(el, config, eventName){
13642     this.el  = Roo.get(el);
13643     this.eventName = eventName || "keydown";
13644     this.bindings = [];
13645     if(config){
13646         this.addBinding(config);
13647     }
13648     this.enable();
13649 };
13650
13651 Roo.KeyMap.prototype = {
13652     /**
13653      * True to stop the event from bubbling and prevent the default browser action if the
13654      * key was handled by the KeyMap (defaults to false)
13655      * @type Boolean
13656      */
13657     stopEvent : false,
13658
13659     /**
13660      * Add a new binding to this KeyMap. The following config object properties are supported:
13661      * <pre>
13662 Property    Type             Description
13663 ----------  ---------------  ----------------------------------------------------------------------
13664 key         String/Array     A single keycode or an array of keycodes to handle
13665 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13666 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13667 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13668 fn          Function         The function to call when KeyMap finds the expected key combination
13669 scope       Object           The scope of the callback function
13670 </pre>
13671      *
13672      * Usage:
13673      * <pre><code>
13674 // Create a KeyMap
13675 var map = new Roo.KeyMap(document, {
13676     key: Roo.EventObject.ENTER,
13677     fn: handleKey,
13678     scope: this
13679 });
13680
13681 //Add a new binding to the existing KeyMap later
13682 map.addBinding({
13683     key: 'abc',
13684     shift: true,
13685     fn: handleKey,
13686     scope: this
13687 });
13688 </code></pre>
13689      * @param {Object/Array} config A single KeyMap config or an array of configs
13690      */
13691         addBinding : function(config){
13692         if(config instanceof Array){
13693             for(var i = 0, len = config.length; i < len; i++){
13694                 this.addBinding(config[i]);
13695             }
13696             return;
13697         }
13698         var keyCode = config.key,
13699             shift = config.shift, 
13700             ctrl = config.ctrl, 
13701             alt = config.alt,
13702             fn = config.fn,
13703             scope = config.scope;
13704         if(typeof keyCode == "string"){
13705             var ks = [];
13706             var keyString = keyCode.toUpperCase();
13707             for(var j = 0, len = keyString.length; j < len; j++){
13708                 ks.push(keyString.charCodeAt(j));
13709             }
13710             keyCode = ks;
13711         }
13712         var keyArray = keyCode instanceof Array;
13713         var handler = function(e){
13714             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13715                 var k = e.getKey();
13716                 if(keyArray){
13717                     for(var i = 0, len = keyCode.length; i < len; i++){
13718                         if(keyCode[i] == k){
13719                           if(this.stopEvent){
13720                               e.stopEvent();
13721                           }
13722                           fn.call(scope || window, k, e);
13723                           return;
13724                         }
13725                     }
13726                 }else{
13727                     if(k == keyCode){
13728                         if(this.stopEvent){
13729                            e.stopEvent();
13730                         }
13731                         fn.call(scope || window, k, e);
13732                     }
13733                 }
13734             }
13735         };
13736         this.bindings.push(handler);  
13737         },
13738
13739     /**
13740      * Shorthand for adding a single key listener
13741      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13742      * following options:
13743      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13744      * @param {Function} fn The function to call
13745      * @param {Object} scope (optional) The scope of the function
13746      */
13747     on : function(key, fn, scope){
13748         var keyCode, shift, ctrl, alt;
13749         if(typeof key == "object" && !(key instanceof Array)){
13750             keyCode = key.key;
13751             shift = key.shift;
13752             ctrl = key.ctrl;
13753             alt = key.alt;
13754         }else{
13755             keyCode = key;
13756         }
13757         this.addBinding({
13758             key: keyCode,
13759             shift: shift,
13760             ctrl: ctrl,
13761             alt: alt,
13762             fn: fn,
13763             scope: scope
13764         })
13765     },
13766
13767     // private
13768     handleKeyDown : function(e){
13769             if(this.enabled){ //just in case
13770             var b = this.bindings;
13771             for(var i = 0, len = b.length; i < len; i++){
13772                 b[i].call(this, e);
13773             }
13774             }
13775         },
13776         
13777         /**
13778          * Returns true if this KeyMap is enabled
13779          * @return {Boolean} 
13780          */
13781         isEnabled : function(){
13782             return this.enabled;  
13783         },
13784         
13785         /**
13786          * Enables this KeyMap
13787          */
13788         enable: function(){
13789                 if(!this.enabled){
13790                     this.el.on(this.eventName, this.handleKeyDown, this);
13791                     this.enabled = true;
13792                 }
13793         },
13794
13795         /**
13796          * Disable this KeyMap
13797          */
13798         disable: function(){
13799                 if(this.enabled){
13800                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13801                     this.enabled = false;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.util.TextMetrics
13818  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13819  * wide, in pixels, a given block of text will be.
13820  * @singleton
13821  */
13822 Roo.util.TextMetrics = function(){
13823     var shared;
13824     return {
13825         /**
13826          * Measures the size of the specified text
13827          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13828          * that can affect the size of the rendered text
13829          * @param {String} text The text to measure
13830          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13831          * in order to accurately measure the text height
13832          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13833          */
13834         measure : function(el, text, fixedWidth){
13835             if(!shared){
13836                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13837             }
13838             shared.bind(el);
13839             shared.setFixedWidth(fixedWidth || 'auto');
13840             return shared.getSize(text);
13841         },
13842
13843         /**
13844          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13845          * the overhead of multiple calls to initialize the style properties on each measurement.
13846          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13847          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13848          * in order to accurately measure the text height
13849          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13850          */
13851         createInstance : function(el, fixedWidth){
13852             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13853         }
13854     };
13855 }();
13856
13857 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13858     var ml = new Roo.Element(document.createElement('div'));
13859     document.body.appendChild(ml.dom);
13860     ml.position('absolute');
13861     ml.setLeftTop(-1000, -1000);
13862     ml.hide();
13863
13864     if(fixedWidth){
13865         ml.setWidth(fixedWidth);
13866     }
13867
13868     var instance = {
13869         /**
13870          * Returns the size of the specified text based on the internal element's style and width properties
13871          * @param {String} text The text to measure
13872          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13873          */
13874         getSize : function(text){
13875             ml.update(text);
13876             var s = ml.getSize();
13877             ml.update('');
13878             return s;
13879         },
13880
13881         /**
13882          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13883          * that can affect the size of the rendered text
13884          * @param {String/HTMLElement} el The element, dom node or id
13885          */
13886         bind : function(el){
13887             ml.setStyle(
13888                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13889             );
13890         },
13891
13892         /**
13893          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13894          * to set a fixed width in order to accurately measure the text height.
13895          * @param {Number} width The width to set on the element
13896          */
13897         setFixedWidth : function(width){
13898             ml.setWidth(width);
13899         },
13900
13901         /**
13902          * Returns the measured width of the specified text
13903          * @param {String} text The text to measure
13904          * @return {Number} width The width in pixels
13905          */
13906         getWidth : function(text){
13907             ml.dom.style.width = 'auto';
13908             return this.getSize(text).width;
13909         },
13910
13911         /**
13912          * Returns the measured height of the specified text.  For multiline text, be sure to call
13913          * {@link #setFixedWidth} if necessary.
13914          * @param {String} text The text to measure
13915          * @return {Number} height The height in pixels
13916          */
13917         getHeight : function(text){
13918             return this.getSize(text).height;
13919         }
13920     };
13921
13922     instance.bind(bindTo);
13923
13924     return instance;
13925 };
13926
13927 // backwards compat
13928 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13929  * Based on:
13930  * Ext JS Library 1.1.1
13931  * Copyright(c) 2006-2007, Ext JS, LLC.
13932  *
13933  * Originally Released Under LGPL - original licence link has changed is not relivant.
13934  *
13935  * Fork - LGPL
13936  * <script type="text/javascript">
13937  */
13938
13939 /**
13940  * @class Roo.state.Provider
13941  * Abstract base class for state provider implementations. This class provides methods
13942  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13943  * Provider interface.
13944  */
13945 Roo.state.Provider = function(){
13946     /**
13947      * @event statechange
13948      * Fires when a state change occurs.
13949      * @param {Provider} this This state provider
13950      * @param {String} key The state key which was changed
13951      * @param {String} value The encoded value for the state
13952      */
13953     this.addEvents({
13954         "statechange": true
13955     });
13956     this.state = {};
13957     Roo.state.Provider.superclass.constructor.call(this);
13958 };
13959 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13960     /**
13961      * Returns the current value for a key
13962      * @param {String} name The key name
13963      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13964      * @return {Mixed} The state data
13965      */
13966     get : function(name, defaultValue){
13967         return typeof this.state[name] == "undefined" ?
13968             defaultValue : this.state[name];
13969     },
13970     
13971     /**
13972      * Clears a value from the state
13973      * @param {String} name The key name
13974      */
13975     clear : function(name){
13976         delete this.state[name];
13977         this.fireEvent("statechange", this, name, null);
13978     },
13979     
13980     /**
13981      * Sets the value for a key
13982      * @param {String} name The key name
13983      * @param {Mixed} value The value to set
13984      */
13985     set : function(name, value){
13986         this.state[name] = value;
13987         this.fireEvent("statechange", this, name, value);
13988     },
13989     
13990     /**
13991      * Decodes a string previously encoded with {@link #encodeValue}.
13992      * @param {String} value The value to decode
13993      * @return {Mixed} The decoded value
13994      */
13995     decodeValue : function(cookie){
13996         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13997         var matches = re.exec(unescape(cookie));
13998         if(!matches || !matches[1]) return; // non state cookie
13999         var type = matches[1];
14000         var v = matches[2];
14001         switch(type){
14002             case "n":
14003                 return parseFloat(v);
14004             case "d":
14005                 return new Date(Date.parse(v));
14006             case "b":
14007                 return (v == "1");
14008             case "a":
14009                 var all = [];
14010                 var values = v.split("^");
14011                 for(var i = 0, len = values.length; i < len; i++){
14012                     all.push(this.decodeValue(values[i]));
14013                 }
14014                 return all;
14015            case "o":
14016                 var all = {};
14017                 var values = v.split("^");
14018                 for(var i = 0, len = values.length; i < len; i++){
14019                     var kv = values[i].split("=");
14020                     all[kv[0]] = this.decodeValue(kv[1]);
14021                 }
14022                 return all;
14023            default:
14024                 return v;
14025         }
14026     },
14027     
14028     /**
14029      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14030      * @param {Mixed} value The value to encode
14031      * @return {String} The encoded value
14032      */
14033     encodeValue : function(v){
14034         var enc;
14035         if(typeof v == "number"){
14036             enc = "n:" + v;
14037         }else if(typeof v == "boolean"){
14038             enc = "b:" + (v ? "1" : "0");
14039         }else if(v instanceof Date){
14040             enc = "d:" + v.toGMTString();
14041         }else if(v instanceof Array){
14042             var flat = "";
14043             for(var i = 0, len = v.length; i < len; i++){
14044                 flat += this.encodeValue(v[i]);
14045                 if(i != len-1) flat += "^";
14046             }
14047             enc = "a:" + flat;
14048         }else if(typeof v == "object"){
14049             var flat = "";
14050             for(var key in v){
14051                 if(typeof v[key] != "function"){
14052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14053                 }
14054             }
14055             enc = "o:" + flat.substring(0, flat.length-1);
14056         }else{
14057             enc = "s:" + v;
14058         }
14059         return escape(enc);        
14060     }
14061 });
14062
14063 /*
14064  * Based on:
14065  * Ext JS Library 1.1.1
14066  * Copyright(c) 2006-2007, Ext JS, LLC.
14067  *
14068  * Originally Released Under LGPL - original licence link has changed is not relivant.
14069  *
14070  * Fork - LGPL
14071  * <script type="text/javascript">
14072  */
14073 /**
14074  * @class Roo.state.Manager
14075  * This is the global state manager. By default all components that are "state aware" check this class
14076  * for state information if you don't pass them a custom state provider. In order for this class
14077  * to be useful, it must be initialized with a provider when your application initializes.
14078  <pre><code>
14079 // in your initialization function
14080 init : function(){
14081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14082    ...
14083    // supposed you have a {@link Roo.BorderLayout}
14084    var layout = new Roo.BorderLayout(...);
14085    layout.restoreState();
14086    // or a {Roo.BasicDialog}
14087    var dialog = new Roo.BasicDialog(...);
14088    dialog.restoreState();
14089  </code></pre>
14090  * @singleton
14091  */
14092 Roo.state.Manager = function(){
14093     var provider = new Roo.state.Provider();
14094     
14095     return {
14096         /**
14097          * Configures the default state provider for your application
14098          * @param {Provider} stateProvider The state provider to set
14099          */
14100         setProvider : function(stateProvider){
14101             provider = stateProvider;
14102         },
14103         
14104         /**
14105          * Returns the current value for a key
14106          * @param {String} name The key name
14107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14108          * @return {Mixed} The state data
14109          */
14110         get : function(key, defaultValue){
14111             return provider.get(key, defaultValue);
14112         },
14113         
14114         /**
14115          * Sets the value for a key
14116          * @param {String} name The key name
14117          * @param {Mixed} value The state data
14118          */
14119          set : function(key, value){
14120             provider.set(key, value);
14121         },
14122         
14123         /**
14124          * Clears a value from the state
14125          * @param {String} name The key name
14126          */
14127         clear : function(key){
14128             provider.clear(key);
14129         },
14130         
14131         /**
14132          * Gets the currently configured state provider
14133          * @return {Provider} The state provider
14134          */
14135         getProvider : function(){
14136             return provider;
14137         }
14138     };
14139 }();
14140 /*
14141  * Based on:
14142  * Ext JS Library 1.1.1
14143  * Copyright(c) 2006-2007, Ext JS, LLC.
14144  *
14145  * Originally Released Under LGPL - original licence link has changed is not relivant.
14146  *
14147  * Fork - LGPL
14148  * <script type="text/javascript">
14149  */
14150 /**
14151  * @class Roo.state.CookieProvider
14152  * @extends Roo.state.Provider
14153  * The default Provider implementation which saves state via cookies.
14154  * <br />Usage:
14155  <pre><code>
14156    var cp = new Roo.state.CookieProvider({
14157        path: "/cgi-bin/",
14158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14159        domain: "roojs.com"
14160    })
14161    Roo.state.Manager.setProvider(cp);
14162  </code></pre>
14163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14168  * domain the page is running on including the 'www' like 'www.roojs.com')
14169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14170  * @constructor
14171  * Create a new CookieProvider
14172  * @param {Object} config The configuration object
14173  */
14174 Roo.state.CookieProvider = function(config){
14175     Roo.state.CookieProvider.superclass.constructor.call(this);
14176     this.path = "/";
14177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14178     this.domain = null;
14179     this.secure = false;
14180     Roo.apply(this, config);
14181     this.state = this.readCookies();
14182 };
14183
14184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14185     // private
14186     set : function(name, value){
14187         if(typeof value == "undefined" || value === null){
14188             this.clear(name);
14189             return;
14190         }
14191         this.setCookie(name, value);
14192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14193     },
14194
14195     // private
14196     clear : function(name){
14197         this.clearCookie(name);
14198         Roo.state.CookieProvider.superclass.clear.call(this, name);
14199     },
14200
14201     // private
14202     readCookies : function(){
14203         var cookies = {};
14204         var c = document.cookie + ";";
14205         var re = /\s?(.*?)=(.*?);/g;
14206         var matches;
14207         while((matches = re.exec(c)) != null){
14208             var name = matches[1];
14209             var value = matches[2];
14210             if(name && name.substring(0,3) == "ys-"){
14211                 cookies[name.substr(3)] = this.decodeValue(value);
14212             }
14213         }
14214         return cookies;
14215     },
14216
14217     // private
14218     setCookie : function(name, value){
14219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14221            ((this.path == null) ? "" : ("; path=" + this.path)) +
14222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14223            ((this.secure == true) ? "; secure" : "");
14224     },
14225
14226     // private
14227     clearCookie : function(name){
14228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14229            ((this.path == null) ? "" : ("; path=" + this.path)) +
14230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14231            ((this.secure == true) ? "; secure" : "");
14232     }
14233 });/*
14234  * Based on:
14235  * Ext JS Library 1.1.1
14236  * Copyright(c) 2006-2007, Ext JS, LLC.
14237  *
14238  * Originally Released Under LGPL - original licence link has changed is not relivant.
14239  *
14240  * Fork - LGPL
14241  * <script type="text/javascript">
14242  */
14243
14244
14245
14246 /*
14247  * These classes are derivatives of the similarly named classes in the YUI Library.
14248  * The original license:
14249  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14250  * Code licensed under the BSD License:
14251  * http://developer.yahoo.net/yui/license.txt
14252  */
14253
14254 (function() {
14255
14256 var Event=Roo.EventManager;
14257 var Dom=Roo.lib.Dom;
14258
14259 /**
14260  * @class Roo.dd.DragDrop
14261  * Defines the interface and base operation of items that that can be
14262  * dragged or can be drop targets.  It was designed to be extended, overriding
14263  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14264  * Up to three html elements can be associated with a DragDrop instance:
14265  * <ul>
14266  * <li>linked element: the element that is passed into the constructor.
14267  * This is the element which defines the boundaries for interaction with
14268  * other DragDrop objects.</li>
14269  * <li>handle element(s): The drag operation only occurs if the element that
14270  * was clicked matches a handle element.  By default this is the linked
14271  * element, but there are times that you will want only a portion of the
14272  * linked element to initiate the drag operation, and the setHandleElId()
14273  * method provides a way to define this.</li>
14274  * <li>drag element: this represents the element that would be moved along
14275  * with the cursor during a drag operation.  By default, this is the linked
14276  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14277  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14278  * </li>
14279  * </ul>
14280  * This class should not be instantiated until the onload event to ensure that
14281  * the associated elements are available.
14282  * The following would define a DragDrop obj that would interact with any
14283  * other DragDrop obj in the "group1" group:
14284  * <pre>
14285  *  dd = new Roo.dd.DragDrop("div1", "group1");
14286  * </pre>
14287  * Since none of the event handlers have been implemented, nothing would
14288  * actually happen if you were to run the code above.  Normally you would
14289  * override this class or one of the default implementations, but you can
14290  * also override the methods you want on an instance of the class...
14291  * <pre>
14292  *  dd.onDragDrop = function(e, id) {
14293  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14294  *  }
14295  * </pre>
14296  * @constructor
14297  * @param {String} id of the element that is linked to this instance
14298  * @param {String} sGroup the group of related DragDrop objects
14299  * @param {object} config an object containing configurable attributes
14300  *                Valid properties for DragDrop:
14301  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14302  */
14303 Roo.dd.DragDrop = function(id, sGroup, config) {
14304     if (id) {
14305         this.init(id, sGroup, config);
14306     }
14307 };
14308
14309 Roo.dd.DragDrop.prototype = {
14310
14311     /**
14312      * The id of the element associated with this object.  This is what we
14313      * refer to as the "linked element" because the size and position of
14314      * this element is used to determine when the drag and drop objects have
14315      * interacted.
14316      * @property id
14317      * @type String
14318      */
14319     id: null,
14320
14321     /**
14322      * Configuration attributes passed into the constructor
14323      * @property config
14324      * @type object
14325      */
14326     config: null,
14327
14328     /**
14329      * The id of the element that will be dragged.  By default this is same
14330      * as the linked element , but could be changed to another element. Ex:
14331      * Roo.dd.DDProxy
14332      * @property dragElId
14333      * @type String
14334      * @private
14335      */
14336     dragElId: null,
14337
14338     /**
14339      * the id of the element that initiates the drag operation.  By default
14340      * this is the linked element, but could be changed to be a child of this
14341      * element.  This lets us do things like only starting the drag when the
14342      * header element within the linked html element is clicked.
14343      * @property handleElId
14344      * @type String
14345      * @private
14346      */
14347     handleElId: null,
14348
14349     /**
14350      * An associative array of HTML tags that will be ignored if clicked.
14351      * @property invalidHandleTypes
14352      * @type {string: string}
14353      */
14354     invalidHandleTypes: null,
14355
14356     /**
14357      * An associative array of ids for elements that will be ignored if clicked
14358      * @property invalidHandleIds
14359      * @type {string: string}
14360      */
14361     invalidHandleIds: null,
14362
14363     /**
14364      * An indexted array of css class names for elements that will be ignored
14365      * if clicked.
14366      * @property invalidHandleClasses
14367      * @type string[]
14368      */
14369     invalidHandleClasses: null,
14370
14371     /**
14372      * The linked element's absolute X position at the time the drag was
14373      * started
14374      * @property startPageX
14375      * @type int
14376      * @private
14377      */
14378     startPageX: 0,
14379
14380     /**
14381      * The linked element's absolute X position at the time the drag was
14382      * started
14383      * @property startPageY
14384      * @type int
14385      * @private
14386      */
14387     startPageY: 0,
14388
14389     /**
14390      * The group defines a logical collection of DragDrop objects that are
14391      * related.  Instances only get events when interacting with other
14392      * DragDrop object in the same group.  This lets us define multiple
14393      * groups using a single DragDrop subclass if we want.
14394      * @property groups
14395      * @type {string: string}
14396      */
14397     groups: null,
14398
14399     /**
14400      * Individual drag/drop instances can be locked.  This will prevent
14401      * onmousedown start drag.
14402      * @property locked
14403      * @type boolean
14404      * @private
14405      */
14406     locked: false,
14407
14408     /**
14409      * Lock this instance
14410      * @method lock
14411      */
14412     lock: function() { this.locked = true; },
14413
14414     /**
14415      * Unlock this instace
14416      * @method unlock
14417      */
14418     unlock: function() { this.locked = false; },
14419
14420     /**
14421      * By default, all insances can be a drop target.  This can be disabled by
14422      * setting isTarget to false.
14423      * @method isTarget
14424      * @type boolean
14425      */
14426     isTarget: true,
14427
14428     /**
14429      * The padding configured for this drag and drop object for calculating
14430      * the drop zone intersection with this object.
14431      * @method padding
14432      * @type int[]
14433      */
14434     padding: null,
14435
14436     /**
14437      * Cached reference to the linked element
14438      * @property _domRef
14439      * @private
14440      */
14441     _domRef: null,
14442
14443     /**
14444      * Internal typeof flag
14445      * @property __ygDragDrop
14446      * @private
14447      */
14448     __ygDragDrop: true,
14449
14450     /**
14451      * Set to true when horizontal contraints are applied
14452      * @property constrainX
14453      * @type boolean
14454      * @private
14455      */
14456     constrainX: false,
14457
14458     /**
14459      * Set to true when vertical contraints are applied
14460      * @property constrainY
14461      * @type boolean
14462      * @private
14463      */
14464     constrainY: false,
14465
14466     /**
14467      * The left constraint
14468      * @property minX
14469      * @type int
14470      * @private
14471      */
14472     minX: 0,
14473
14474     /**
14475      * The right constraint
14476      * @property maxX
14477      * @type int
14478      * @private
14479      */
14480     maxX: 0,
14481
14482     /**
14483      * The up constraint
14484      * @property minY
14485      * @type int
14486      * @type int
14487      * @private
14488      */
14489     minY: 0,
14490
14491     /**
14492      * The down constraint
14493      * @property maxY
14494      * @type int
14495      * @private
14496      */
14497     maxY: 0,
14498
14499     /**
14500      * Maintain offsets when we resetconstraints.  Set to true when you want
14501      * the position of the element relative to its parent to stay the same
14502      * when the page changes
14503      *
14504      * @property maintainOffset
14505      * @type boolean
14506      */
14507     maintainOffset: false,
14508
14509     /**
14510      * Array of pixel locations the element will snap to if we specified a
14511      * horizontal graduation/interval.  This array is generated automatically
14512      * when you define a tick interval.
14513      * @property xTicks
14514      * @type int[]
14515      */
14516     xTicks: null,
14517
14518     /**
14519      * Array of pixel locations the element will snap to if we specified a
14520      * vertical graduation/interval.  This array is generated automatically
14521      * when you define a tick interval.
14522      * @property yTicks
14523      * @type int[]
14524      */
14525     yTicks: null,
14526
14527     /**
14528      * By default the drag and drop instance will only respond to the primary
14529      * button click (left button for a right-handed mouse).  Set to true to
14530      * allow drag and drop to start with any mouse click that is propogated
14531      * by the browser
14532      * @property primaryButtonOnly
14533      * @type boolean
14534      */
14535     primaryButtonOnly: true,
14536
14537     /**
14538      * The availabe property is false until the linked dom element is accessible.
14539      * @property available
14540      * @type boolean
14541      */
14542     available: false,
14543
14544     /**
14545      * By default, drags can only be initiated if the mousedown occurs in the
14546      * region the linked element is.  This is done in part to work around a
14547      * bug in some browsers that mis-report the mousedown if the previous
14548      * mouseup happened outside of the window.  This property is set to true
14549      * if outer handles are defined.
14550      *
14551      * @property hasOuterHandles
14552      * @type boolean
14553      * @default false
14554      */
14555     hasOuterHandles: false,
14556
14557     /**
14558      * Code that executes immediately before the startDrag event
14559      * @method b4StartDrag
14560      * @private
14561      */
14562     b4StartDrag: function(x, y) { },
14563
14564     /**
14565      * Abstract method called after a drag/drop object is clicked
14566      * and the drag or mousedown time thresholds have beeen met.
14567      * @method startDrag
14568      * @param {int} X click location
14569      * @param {int} Y click location
14570      */
14571     startDrag: function(x, y) { /* override this */ },
14572
14573     /**
14574      * Code that executes immediately before the onDrag event
14575      * @method b4Drag
14576      * @private
14577      */
14578     b4Drag: function(e) { },
14579
14580     /**
14581      * Abstract method called during the onMouseMove event while dragging an
14582      * object.
14583      * @method onDrag
14584      * @param {Event} e the mousemove event
14585      */
14586     onDrag: function(e) { /* override this */ },
14587
14588     /**
14589      * Abstract method called when this element fist begins hovering over
14590      * another DragDrop obj
14591      * @method onDragEnter
14592      * @param {Event} e the mousemove event
14593      * @param {String|DragDrop[]} id In POINT mode, the element
14594      * id this is hovering over.  In INTERSECT mode, an array of one or more
14595      * dragdrop items being hovered over.
14596      */
14597     onDragEnter: function(e, id) { /* override this */ },
14598
14599     /**
14600      * Code that executes immediately before the onDragOver event
14601      * @method b4DragOver
14602      * @private
14603      */
14604     b4DragOver: function(e) { },
14605
14606     /**
14607      * Abstract method called when this element is hovering over another
14608      * DragDrop obj
14609      * @method onDragOver
14610      * @param {Event} e the mousemove event
14611      * @param {String|DragDrop[]} id In POINT mode, the element
14612      * id this is hovering over.  In INTERSECT mode, an array of dd items
14613      * being hovered over.
14614      */
14615     onDragOver: function(e, id) { /* override this */ },
14616
14617     /**
14618      * Code that executes immediately before the onDragOut event
14619      * @method b4DragOut
14620      * @private
14621      */
14622     b4DragOut: function(e) { },
14623
14624     /**
14625      * Abstract method called when we are no longer hovering over an element
14626      * @method onDragOut
14627      * @param {Event} e the mousemove event
14628      * @param {String|DragDrop[]} id In POINT mode, the element
14629      * id this was hovering over.  In INTERSECT mode, an array of dd items
14630      * that the mouse is no longer over.
14631      */
14632     onDragOut: function(e, id) { /* override this */ },
14633
14634     /**
14635      * Code that executes immediately before the onDragDrop event
14636      * @method b4DragDrop
14637      * @private
14638      */
14639     b4DragDrop: function(e) { },
14640
14641     /**
14642      * Abstract method called when this item is dropped on another DragDrop
14643      * obj
14644      * @method onDragDrop
14645      * @param {Event} e the mouseup event
14646      * @param {String|DragDrop[]} id In POINT mode, the element
14647      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14648      * was dropped on.
14649      */
14650     onDragDrop: function(e, id) { /* override this */ },
14651
14652     /**
14653      * Abstract method called when this item is dropped on an area with no
14654      * drop target
14655      * @method onInvalidDrop
14656      * @param {Event} e the mouseup event
14657      */
14658     onInvalidDrop: function(e) { /* override this */ },
14659
14660     /**
14661      * Code that executes immediately before the endDrag event
14662      * @method b4EndDrag
14663      * @private
14664      */
14665     b4EndDrag: function(e) { },
14666
14667     /**
14668      * Fired when we are done dragging the object
14669      * @method endDrag
14670      * @param {Event} e the mouseup event
14671      */
14672     endDrag: function(e) { /* override this */ },
14673
14674     /**
14675      * Code executed immediately before the onMouseDown event
14676      * @method b4MouseDown
14677      * @param {Event} e the mousedown event
14678      * @private
14679      */
14680     b4MouseDown: function(e) {  },
14681
14682     /**
14683      * Event handler that fires when a drag/drop obj gets a mousedown
14684      * @method onMouseDown
14685      * @param {Event} e the mousedown event
14686      */
14687     onMouseDown: function(e) { /* override this */ },
14688
14689     /**
14690      * Event handler that fires when a drag/drop obj gets a mouseup
14691      * @method onMouseUp
14692      * @param {Event} e the mouseup event
14693      */
14694     onMouseUp: function(e) { /* override this */ },
14695
14696     /**
14697      * Override the onAvailable method to do what is needed after the initial
14698      * position was determined.
14699      * @method onAvailable
14700      */
14701     onAvailable: function () {
14702     },
14703
14704     /*
14705      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14706      * @type Object
14707      */
14708     defaultPadding : {left:0, right:0, top:0, bottom:0},
14709
14710     /*
14711      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14712  *
14713  * Usage:
14714  <pre><code>
14715  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14716                 { dragElId: "existingProxyDiv" });
14717  dd.startDrag = function(){
14718      this.constrainTo("parent-id");
14719  };
14720  </code></pre>
14721  * Or you can initalize it using the {@link Roo.Element} object:
14722  <pre><code>
14723  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14724      startDrag : function(){
14725          this.constrainTo("parent-id");
14726      }
14727  });
14728  </code></pre>
14729      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14730      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14731      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14732      * an object containing the sides to pad. For example: {right:10, bottom:10}
14733      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14734      */
14735     constrainTo : function(constrainTo, pad, inContent){
14736         if(typeof pad == "number"){
14737             pad = {left: pad, right:pad, top:pad, bottom:pad};
14738         }
14739         pad = pad || this.defaultPadding;
14740         var b = Roo.get(this.getEl()).getBox();
14741         var ce = Roo.get(constrainTo);
14742         var s = ce.getScroll();
14743         var c, cd = ce.dom;
14744         if(cd == document.body){
14745             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14746         }else{
14747             xy = ce.getXY();
14748             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14749         }
14750
14751
14752         var topSpace = b.y - c.y;
14753         var leftSpace = b.x - c.x;
14754
14755         this.resetConstraints();
14756         this.setXConstraint(leftSpace - (pad.left||0), // left
14757                 c.width - leftSpace - b.width - (pad.right||0) //right
14758         );
14759         this.setYConstraint(topSpace - (pad.top||0), //top
14760                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14761         );
14762     },
14763
14764     /**
14765      * Returns a reference to the linked element
14766      * @method getEl
14767      * @return {HTMLElement} the html element
14768      */
14769     getEl: function() {
14770         if (!this._domRef) {
14771             this._domRef = Roo.getDom(this.id);
14772         }
14773
14774         return this._domRef;
14775     },
14776
14777     /**
14778      * Returns a reference to the actual element to drag.  By default this is
14779      * the same as the html element, but it can be assigned to another
14780      * element. An example of this can be found in Roo.dd.DDProxy
14781      * @method getDragEl
14782      * @return {HTMLElement} the html element
14783      */
14784     getDragEl: function() {
14785         return Roo.getDom(this.dragElId);
14786     },
14787
14788     /**
14789      * Sets up the DragDrop object.  Must be called in the constructor of any
14790      * Roo.dd.DragDrop subclass
14791      * @method init
14792      * @param id the id of the linked element
14793      * @param {String} sGroup the group of related items
14794      * @param {object} config configuration attributes
14795      */
14796     init: function(id, sGroup, config) {
14797         this.initTarget(id, sGroup, config);
14798         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14799         // Event.on(this.id, "selectstart", Event.preventDefault);
14800     },
14801
14802     /**
14803      * Initializes Targeting functionality only... the object does not
14804      * get a mousedown handler.
14805      * @method initTarget
14806      * @param id the id of the linked element
14807      * @param {String} sGroup the group of related items
14808      * @param {object} config configuration attributes
14809      */
14810     initTarget: function(id, sGroup, config) {
14811
14812         // configuration attributes
14813         this.config = config || {};
14814
14815         // create a local reference to the drag and drop manager
14816         this.DDM = Roo.dd.DDM;
14817         // initialize the groups array
14818         this.groups = {};
14819
14820         // assume that we have an element reference instead of an id if the
14821         // parameter is not a string
14822         if (typeof id !== "string") {
14823             id = Roo.id(id);
14824         }
14825
14826         // set the id
14827         this.id = id;
14828
14829         // add to an interaction group
14830         this.addToGroup((sGroup) ? sGroup : "default");
14831
14832         // We don't want to register this as the handle with the manager
14833         // so we just set the id rather than calling the setter.
14834         this.handleElId = id;
14835
14836         // the linked element is the element that gets dragged by default
14837         this.setDragElId(id);
14838
14839         // by default, clicked anchors will not start drag operations.
14840         this.invalidHandleTypes = { A: "A" };
14841         this.invalidHandleIds = {};
14842         this.invalidHandleClasses = [];
14843
14844         this.applyConfig();
14845
14846         this.handleOnAvailable();
14847     },
14848
14849     /**
14850      * Applies the configuration parameters that were passed into the constructor.
14851      * This is supposed to happen at each level through the inheritance chain.  So
14852      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14853      * DragDrop in order to get all of the parameters that are available in
14854      * each object.
14855      * @method applyConfig
14856      */
14857     applyConfig: function() {
14858
14859         // configurable properties:
14860         //    padding, isTarget, maintainOffset, primaryButtonOnly
14861         this.padding           = this.config.padding || [0, 0, 0, 0];
14862         this.isTarget          = (this.config.isTarget !== false);
14863         this.maintainOffset    = (this.config.maintainOffset);
14864         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14865
14866     },
14867
14868     /**
14869      * Executed when the linked element is available
14870      * @method handleOnAvailable
14871      * @private
14872      */
14873     handleOnAvailable: function() {
14874         this.available = true;
14875         this.resetConstraints();
14876         this.onAvailable();
14877     },
14878
14879      /**
14880      * Configures the padding for the target zone in px.  Effectively expands
14881      * (or reduces) the virtual object size for targeting calculations.
14882      * Supports css-style shorthand; if only one parameter is passed, all sides
14883      * will have that padding, and if only two are passed, the top and bottom
14884      * will have the first param, the left and right the second.
14885      * @method setPadding
14886      * @param {int} iTop    Top pad
14887      * @param {int} iRight  Right pad
14888      * @param {int} iBot    Bot pad
14889      * @param {int} iLeft   Left pad
14890      */
14891     setPadding: function(iTop, iRight, iBot, iLeft) {
14892         // this.padding = [iLeft, iRight, iTop, iBot];
14893         if (!iRight && 0 !== iRight) {
14894             this.padding = [iTop, iTop, iTop, iTop];
14895         } else if (!iBot && 0 !== iBot) {
14896             this.padding = [iTop, iRight, iTop, iRight];
14897         } else {
14898             this.padding = [iTop, iRight, iBot, iLeft];
14899         }
14900     },
14901
14902     /**
14903      * Stores the initial placement of the linked element.
14904      * @method setInitialPosition
14905      * @param {int} diffX   the X offset, default 0
14906      * @param {int} diffY   the Y offset, default 0
14907      */
14908     setInitPosition: function(diffX, diffY) {
14909         var el = this.getEl();
14910
14911         if (!this.DDM.verifyEl(el)) {
14912             return;
14913         }
14914
14915         var dx = diffX || 0;
14916         var dy = diffY || 0;
14917
14918         var p = Dom.getXY( el );
14919
14920         this.initPageX = p[0] - dx;
14921         this.initPageY = p[1] - dy;
14922
14923         this.lastPageX = p[0];
14924         this.lastPageY = p[1];
14925
14926
14927         this.setStartPosition(p);
14928     },
14929
14930     /**
14931      * Sets the start position of the element.  This is set when the obj
14932      * is initialized, the reset when a drag is started.
14933      * @method setStartPosition
14934      * @param pos current position (from previous lookup)
14935      * @private
14936      */
14937     setStartPosition: function(pos) {
14938         var p = pos || Dom.getXY( this.getEl() );
14939         this.deltaSetXY = null;
14940
14941         this.startPageX = p[0];
14942         this.startPageY = p[1];
14943     },
14944
14945     /**
14946      * Add this instance to a group of related drag/drop objects.  All
14947      * instances belong to at least one group, and can belong to as many
14948      * groups as needed.
14949      * @method addToGroup
14950      * @param sGroup {string} the name of the group
14951      */
14952     addToGroup: function(sGroup) {
14953         this.groups[sGroup] = true;
14954         this.DDM.regDragDrop(this, sGroup);
14955     },
14956
14957     /**
14958      * Remove's this instance from the supplied interaction group
14959      * @method removeFromGroup
14960      * @param {string}  sGroup  The group to drop
14961      */
14962     removeFromGroup: function(sGroup) {
14963         if (this.groups[sGroup]) {
14964             delete this.groups[sGroup];
14965         }
14966
14967         this.DDM.removeDDFromGroup(this, sGroup);
14968     },
14969
14970     /**
14971      * Allows you to specify that an element other than the linked element
14972      * will be moved with the cursor during a drag
14973      * @method setDragElId
14974      * @param id {string} the id of the element that will be used to initiate the drag
14975      */
14976     setDragElId: function(id) {
14977         this.dragElId = id;
14978     },
14979
14980     /**
14981      * Allows you to specify a child of the linked element that should be
14982      * used to initiate the drag operation.  An example of this would be if
14983      * you have a content div with text and links.  Clicking anywhere in the
14984      * content area would normally start the drag operation.  Use this method
14985      * to specify that an element inside of the content div is the element
14986      * that starts the drag operation.
14987      * @method setHandleElId
14988      * @param id {string} the id of the element that will be used to
14989      * initiate the drag.
14990      */
14991     setHandleElId: function(id) {
14992         if (typeof id !== "string") {
14993             id = Roo.id(id);
14994         }
14995         this.handleElId = id;
14996         this.DDM.regHandle(this.id, id);
14997     },
14998
14999     /**
15000      * Allows you to set an element outside of the linked element as a drag
15001      * handle
15002      * @method setOuterHandleElId
15003      * @param id the id of the element that will be used to initiate the drag
15004      */
15005     setOuterHandleElId: function(id) {
15006         if (typeof id !== "string") {
15007             id = Roo.id(id);
15008         }
15009         Event.on(id, "mousedown",
15010                 this.handleMouseDown, this);
15011         this.setHandleElId(id);
15012
15013         this.hasOuterHandles = true;
15014     },
15015
15016     /**
15017      * Remove all drag and drop hooks for this element
15018      * @method unreg
15019      */
15020     unreg: function() {
15021         Event.un(this.id, "mousedown",
15022                 this.handleMouseDown);
15023         this._domRef = null;
15024         this.DDM._remove(this);
15025     },
15026
15027     destroy : function(){
15028         this.unreg();
15029     },
15030
15031     /**
15032      * Returns true if this instance is locked, or the drag drop mgr is locked
15033      * (meaning that all drag/drop is disabled on the page.)
15034      * @method isLocked
15035      * @return {boolean} true if this obj or all drag/drop is locked, else
15036      * false
15037      */
15038     isLocked: function() {
15039         return (this.DDM.isLocked() || this.locked);
15040     },
15041
15042     /**
15043      * Fired when this object is clicked
15044      * @method handleMouseDown
15045      * @param {Event} e
15046      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15047      * @private
15048      */
15049     handleMouseDown: function(e, oDD){
15050         if (this.primaryButtonOnly && e.button != 0) {
15051             return;
15052         }
15053
15054         if (this.isLocked()) {
15055             return;
15056         }
15057
15058         this.DDM.refreshCache(this.groups);
15059
15060         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15061         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15062         } else {
15063             if (this.clickValidator(e)) {
15064
15065                 // set the initial element position
15066                 this.setStartPosition();
15067
15068
15069                 this.b4MouseDown(e);
15070                 this.onMouseDown(e);
15071
15072                 this.DDM.handleMouseDown(e, this);
15073
15074                 this.DDM.stopEvent(e);
15075             } else {
15076
15077
15078             }
15079         }
15080     },
15081
15082     clickValidator: function(e) {
15083         var target = e.getTarget();
15084         return ( this.isValidHandleChild(target) &&
15085                     (this.id == this.handleElId ||
15086                         this.DDM.handleWasClicked(target, this.id)) );
15087     },
15088
15089     /**
15090      * Allows you to specify a tag name that should not start a drag operation
15091      * when clicked.  This is designed to facilitate embedding links within a
15092      * drag handle that do something other than start the drag.
15093      * @method addInvalidHandleType
15094      * @param {string} tagName the type of element to exclude
15095      */
15096     addInvalidHandleType: function(tagName) {
15097         var type = tagName.toUpperCase();
15098         this.invalidHandleTypes[type] = type;
15099     },
15100
15101     /**
15102      * Lets you to specify an element id for a child of a drag handle
15103      * that should not initiate a drag
15104      * @method addInvalidHandleId
15105      * @param {string} id the element id of the element you wish to ignore
15106      */
15107     addInvalidHandleId: function(id) {
15108         if (typeof id !== "string") {
15109             id = Roo.id(id);
15110         }
15111         this.invalidHandleIds[id] = id;
15112     },
15113
15114     /**
15115      * Lets you specify a css class of elements that will not initiate a drag
15116      * @method addInvalidHandleClass
15117      * @param {string} cssClass the class of the elements you wish to ignore
15118      */
15119     addInvalidHandleClass: function(cssClass) {
15120         this.invalidHandleClasses.push(cssClass);
15121     },
15122
15123     /**
15124      * Unsets an excluded tag name set by addInvalidHandleType
15125      * @method removeInvalidHandleType
15126      * @param {string} tagName the type of element to unexclude
15127      */
15128     removeInvalidHandleType: function(tagName) {
15129         var type = tagName.toUpperCase();
15130         // this.invalidHandleTypes[type] = null;
15131         delete this.invalidHandleTypes[type];
15132     },
15133
15134     /**
15135      * Unsets an invalid handle id
15136      * @method removeInvalidHandleId
15137      * @param {string} id the id of the element to re-enable
15138      */
15139     removeInvalidHandleId: function(id) {
15140         if (typeof id !== "string") {
15141             id = Roo.id(id);
15142         }
15143         delete this.invalidHandleIds[id];
15144     },
15145
15146     /**
15147      * Unsets an invalid css class
15148      * @method removeInvalidHandleClass
15149      * @param {string} cssClass the class of the element(s) you wish to
15150      * re-enable
15151      */
15152     removeInvalidHandleClass: function(cssClass) {
15153         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15154             if (this.invalidHandleClasses[i] == cssClass) {
15155                 delete this.invalidHandleClasses[i];
15156             }
15157         }
15158     },
15159
15160     /**
15161      * Checks the tag exclusion list to see if this click should be ignored
15162      * @method isValidHandleChild
15163      * @param {HTMLElement} node the HTMLElement to evaluate
15164      * @return {boolean} true if this is a valid tag type, false if not
15165      */
15166     isValidHandleChild: function(node) {
15167
15168         var valid = true;
15169         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15170         var nodeName;
15171         try {
15172             nodeName = node.nodeName.toUpperCase();
15173         } catch(e) {
15174             nodeName = node.nodeName;
15175         }
15176         valid = valid && !this.invalidHandleTypes[nodeName];
15177         valid = valid && !this.invalidHandleIds[node.id];
15178
15179         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15180             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15181         }
15182
15183
15184         return valid;
15185
15186     },
15187
15188     /**
15189      * Create the array of horizontal tick marks if an interval was specified
15190      * in setXConstraint().
15191      * @method setXTicks
15192      * @private
15193      */
15194     setXTicks: function(iStartX, iTickSize) {
15195         this.xTicks = [];
15196         this.xTickSize = iTickSize;
15197
15198         var tickMap = {};
15199
15200         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15201             if (!tickMap[i]) {
15202                 this.xTicks[this.xTicks.length] = i;
15203                 tickMap[i] = true;
15204             }
15205         }
15206
15207         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15208             if (!tickMap[i]) {
15209                 this.xTicks[this.xTicks.length] = i;
15210                 tickMap[i] = true;
15211             }
15212         }
15213
15214         this.xTicks.sort(this.DDM.numericSort) ;
15215     },
15216
15217     /**
15218      * Create the array of vertical tick marks if an interval was specified in
15219      * setYConstraint().
15220      * @method setYTicks
15221      * @private
15222      */
15223     setYTicks: function(iStartY, iTickSize) {
15224         this.yTicks = [];
15225         this.yTickSize = iTickSize;
15226
15227         var tickMap = {};
15228
15229         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15230             if (!tickMap[i]) {
15231                 this.yTicks[this.yTicks.length] = i;
15232                 tickMap[i] = true;
15233             }
15234         }
15235
15236         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15237             if (!tickMap[i]) {
15238                 this.yTicks[this.yTicks.length] = i;
15239                 tickMap[i] = true;
15240             }
15241         }
15242
15243         this.yTicks.sort(this.DDM.numericSort) ;
15244     },
15245
15246     /**
15247      * By default, the element can be dragged any place on the screen.  Use
15248      * this method to limit the horizontal travel of the element.  Pass in
15249      * 0,0 for the parameters if you want to lock the drag to the y axis.
15250      * @method setXConstraint
15251      * @param {int} iLeft the number of pixels the element can move to the left
15252      * @param {int} iRight the number of pixels the element can move to the
15253      * right
15254      * @param {int} iTickSize optional parameter for specifying that the
15255      * element
15256      * should move iTickSize pixels at a time.
15257      */
15258     setXConstraint: function(iLeft, iRight, iTickSize) {
15259         this.leftConstraint = iLeft;
15260         this.rightConstraint = iRight;
15261
15262         this.minX = this.initPageX - iLeft;
15263         this.maxX = this.initPageX + iRight;
15264         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15265
15266         this.constrainX = true;
15267     },
15268
15269     /**
15270      * Clears any constraints applied to this instance.  Also clears ticks
15271      * since they can't exist independent of a constraint at this time.
15272      * @method clearConstraints
15273      */
15274     clearConstraints: function() {
15275         this.constrainX = false;
15276         this.constrainY = false;
15277         this.clearTicks();
15278     },
15279
15280     /**
15281      * Clears any tick interval defined for this instance
15282      * @method clearTicks
15283      */
15284     clearTicks: function() {
15285         this.xTicks = null;
15286         this.yTicks = null;
15287         this.xTickSize = 0;
15288         this.yTickSize = 0;
15289     },
15290
15291     /**
15292      * By default, the element can be dragged any place on the screen.  Set
15293      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15294      * parameters if you want to lock the drag to the x axis.
15295      * @method setYConstraint
15296      * @param {int} iUp the number of pixels the element can move up
15297      * @param {int} iDown the number of pixels the element can move down
15298      * @param {int} iTickSize optional parameter for specifying that the
15299      * element should move iTickSize pixels at a time.
15300      */
15301     setYConstraint: function(iUp, iDown, iTickSize) {
15302         this.topConstraint = iUp;
15303         this.bottomConstraint = iDown;
15304
15305         this.minY = this.initPageY - iUp;
15306         this.maxY = this.initPageY + iDown;
15307         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15308
15309         this.constrainY = true;
15310
15311     },
15312
15313     /**
15314      * resetConstraints must be called if you manually reposition a dd element.
15315      * @method resetConstraints
15316      * @param {boolean} maintainOffset
15317      */
15318     resetConstraints: function() {
15319
15320
15321         // Maintain offsets if necessary
15322         if (this.initPageX || this.initPageX === 0) {
15323             // figure out how much this thing has moved
15324             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15325             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15326
15327             this.setInitPosition(dx, dy);
15328
15329         // This is the first time we have detected the element's position
15330         } else {
15331             this.setInitPosition();
15332         }
15333
15334         if (this.constrainX) {
15335             this.setXConstraint( this.leftConstraint,
15336                                  this.rightConstraint,
15337                                  this.xTickSize        );
15338         }
15339
15340         if (this.constrainY) {
15341             this.setYConstraint( this.topConstraint,
15342                                  this.bottomConstraint,
15343                                  this.yTickSize         );
15344         }
15345     },
15346
15347     /**
15348      * Normally the drag element is moved pixel by pixel, but we can specify
15349      * that it move a number of pixels at a time.  This method resolves the
15350      * location when we have it set up like this.
15351      * @method getTick
15352      * @param {int} val where we want to place the object
15353      * @param {int[]} tickArray sorted array of valid points
15354      * @return {int} the closest tick
15355      * @private
15356      */
15357     getTick: function(val, tickArray) {
15358
15359         if (!tickArray) {
15360             // If tick interval is not defined, it is effectively 1 pixel,
15361             // so we return the value passed to us.
15362             return val;
15363         } else if (tickArray[0] >= val) {
15364             // The value is lower than the first tick, so we return the first
15365             // tick.
15366             return tickArray[0];
15367         } else {
15368             for (var i=0, len=tickArray.length; i<len; ++i) {
15369                 var next = i + 1;
15370                 if (tickArray[next] && tickArray[next] >= val) {
15371                     var diff1 = val - tickArray[i];
15372                     var diff2 = tickArray[next] - val;
15373                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15374                 }
15375             }
15376
15377             // The value is larger than the last tick, so we return the last
15378             // tick.
15379             return tickArray[tickArray.length - 1];
15380         }
15381     },
15382
15383     /**
15384      * toString method
15385      * @method toString
15386      * @return {string} string representation of the dd obj
15387      */
15388     toString: function() {
15389         return ("DragDrop " + this.id);
15390     }
15391
15392 };
15393
15394 })();
15395 /*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406
15407 /**
15408  * The drag and drop utility provides a framework for building drag and drop
15409  * applications.  In addition to enabling drag and drop for specific elements,
15410  * the drag and drop elements are tracked by the manager class, and the
15411  * interactions between the various elements are tracked during the drag and
15412  * the implementing code is notified about these important moments.
15413  */
15414
15415 // Only load the library once.  Rewriting the manager class would orphan
15416 // existing drag and drop instances.
15417 if (!Roo.dd.DragDropMgr) {
15418
15419 /**
15420  * @class Roo.dd.DragDropMgr
15421  * DragDropMgr is a singleton that tracks the element interaction for
15422  * all DragDrop items in the window.  Generally, you will not call
15423  * this class directly, but it does have helper methods that could
15424  * be useful in your DragDrop implementations.
15425  * @singleton
15426  */
15427 Roo.dd.DragDropMgr = function() {
15428
15429     var Event = Roo.EventManager;
15430
15431     return {
15432
15433         /**
15434          * Two dimensional Array of registered DragDrop objects.  The first
15435          * dimension is the DragDrop item group, the second the DragDrop
15436          * object.
15437          * @property ids
15438          * @type {string: string}
15439          * @private
15440          * @static
15441          */
15442         ids: {},
15443
15444         /**
15445          * Array of element ids defined as drag handles.  Used to determine
15446          * if the element that generated the mousedown event is actually the
15447          * handle and not the html element itself.
15448          * @property handleIds
15449          * @type {string: string}
15450          * @private
15451          * @static
15452          */
15453         handleIds: {},
15454
15455         /**
15456          * the DragDrop object that is currently being dragged
15457          * @property dragCurrent
15458          * @type DragDrop
15459          * @private
15460          * @static
15461          **/
15462         dragCurrent: null,
15463
15464         /**
15465          * the DragDrop object(s) that are being hovered over
15466          * @property dragOvers
15467          * @type Array
15468          * @private
15469          * @static
15470          */
15471         dragOvers: {},
15472
15473         /**
15474          * the X distance between the cursor and the object being dragged
15475          * @property deltaX
15476          * @type int
15477          * @private
15478          * @static
15479          */
15480         deltaX: 0,
15481
15482         /**
15483          * the Y distance between the cursor and the object being dragged
15484          * @property deltaY
15485          * @type int
15486          * @private
15487          * @static
15488          */
15489         deltaY: 0,
15490
15491         /**
15492          * Flag to determine if we should prevent the default behavior of the
15493          * events we define. By default this is true, but this can be set to
15494          * false if you need the default behavior (not recommended)
15495          * @property preventDefault
15496          * @type boolean
15497          * @static
15498          */
15499         preventDefault: true,
15500
15501         /**
15502          * Flag to determine if we should stop the propagation of the events
15503          * we generate. This is true by default but you may want to set it to
15504          * false if the html element contains other features that require the
15505          * mouse click.
15506          * @property stopPropagation
15507          * @type boolean
15508          * @static
15509          */
15510         stopPropagation: true,
15511
15512         /**
15513          * Internal flag that is set to true when drag and drop has been
15514          * intialized
15515          * @property initialized
15516          * @private
15517          * @static
15518          */
15519         initalized: false,
15520
15521         /**
15522          * All drag and drop can be disabled.
15523          * @property locked
15524          * @private
15525          * @static
15526          */
15527         locked: false,
15528
15529         /**
15530          * Called the first time an element is registered.
15531          * @method init
15532          * @private
15533          * @static
15534          */
15535         init: function() {
15536             this.initialized = true;
15537         },
15538
15539         /**
15540          * In point mode, drag and drop interaction is defined by the
15541          * location of the cursor during the drag/drop
15542          * @property POINT
15543          * @type int
15544          * @static
15545          */
15546         POINT: 0,
15547
15548         /**
15549          * In intersect mode, drag and drop interactio nis defined by the
15550          * overlap of two or more drag and drop objects.
15551          * @property INTERSECT
15552          * @type int
15553          * @static
15554          */
15555         INTERSECT: 1,
15556
15557         /**
15558          * The current drag and drop mode.  Default: POINT
15559          * @property mode
15560          * @type int
15561          * @static
15562          */
15563         mode: 0,
15564
15565         /**
15566          * Runs method on all drag and drop objects
15567          * @method _execOnAll
15568          * @private
15569          * @static
15570          */
15571         _execOnAll: function(sMethod, args) {
15572             for (var i in this.ids) {
15573                 for (var j in this.ids[i]) {
15574                     var oDD = this.ids[i][j];
15575                     if (! this.isTypeOfDD(oDD)) {
15576                         continue;
15577                     }
15578                     oDD[sMethod].apply(oDD, args);
15579                 }
15580             }
15581         },
15582
15583         /**
15584          * Drag and drop initialization.  Sets up the global event handlers
15585          * @method _onLoad
15586          * @private
15587          * @static
15588          */
15589         _onLoad: function() {
15590
15591             this.init();
15592
15593
15594             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15595             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15596             Event.on(window,   "unload",    this._onUnload, this, true);
15597             Event.on(window,   "resize",    this._onResize, this, true);
15598             // Event.on(window,   "mouseout",    this._test);
15599
15600         },
15601
15602         /**
15603          * Reset constraints on all drag and drop objs
15604          * @method _onResize
15605          * @private
15606          * @static
15607          */
15608         _onResize: function(e) {
15609             this._execOnAll("resetConstraints", []);
15610         },
15611
15612         /**
15613          * Lock all drag and drop functionality
15614          * @method lock
15615          * @static
15616          */
15617         lock: function() { this.locked = true; },
15618
15619         /**
15620          * Unlock all drag and drop functionality
15621          * @method unlock
15622          * @static
15623          */
15624         unlock: function() { this.locked = false; },
15625
15626         /**
15627          * Is drag and drop locked?
15628          * @method isLocked
15629          * @return {boolean} True if drag and drop is locked, false otherwise.
15630          * @static
15631          */
15632         isLocked: function() { return this.locked; },
15633
15634         /**
15635          * Location cache that is set for all drag drop objects when a drag is
15636          * initiated, cleared when the drag is finished.
15637          * @property locationCache
15638          * @private
15639          * @static
15640          */
15641         locationCache: {},
15642
15643         /**
15644          * Set useCache to false if you want to force object the lookup of each
15645          * drag and drop linked element constantly during a drag.
15646          * @property useCache
15647          * @type boolean
15648          * @static
15649          */
15650         useCache: true,
15651
15652         /**
15653          * The number of pixels that the mouse needs to move after the
15654          * mousedown before the drag is initiated.  Default=3;
15655          * @property clickPixelThresh
15656          * @type int
15657          * @static
15658          */
15659         clickPixelThresh: 3,
15660
15661         /**
15662          * The number of milliseconds after the mousedown event to initiate the
15663          * drag if we don't get a mouseup event. Default=1000
15664          * @property clickTimeThresh
15665          * @type int
15666          * @static
15667          */
15668         clickTimeThresh: 350,
15669
15670         /**
15671          * Flag that indicates that either the drag pixel threshold or the
15672          * mousdown time threshold has been met
15673          * @property dragThreshMet
15674          * @type boolean
15675          * @private
15676          * @static
15677          */
15678         dragThreshMet: false,
15679
15680         /**
15681          * Timeout used for the click time threshold
15682          * @property clickTimeout
15683          * @type Object
15684          * @private
15685          * @static
15686          */
15687         clickTimeout: null,
15688
15689         /**
15690          * The X position of the mousedown event stored for later use when a
15691          * drag threshold is met.
15692          * @property startX
15693          * @type int
15694          * @private
15695          * @static
15696          */
15697         startX: 0,
15698
15699         /**
15700          * The Y position of the mousedown event stored for later use when a
15701          * drag threshold is met.
15702          * @property startY
15703          * @type int
15704          * @private
15705          * @static
15706          */
15707         startY: 0,
15708
15709         /**
15710          * Each DragDrop instance must be registered with the DragDropMgr.
15711          * This is executed in DragDrop.init()
15712          * @method regDragDrop
15713          * @param {DragDrop} oDD the DragDrop object to register
15714          * @param {String} sGroup the name of the group this element belongs to
15715          * @static
15716          */
15717         regDragDrop: function(oDD, sGroup) {
15718             if (!this.initialized) { this.init(); }
15719
15720             if (!this.ids[sGroup]) {
15721                 this.ids[sGroup] = {};
15722             }
15723             this.ids[sGroup][oDD.id] = oDD;
15724         },
15725
15726         /**
15727          * Removes the supplied dd instance from the supplied group. Executed
15728          * by DragDrop.removeFromGroup, so don't call this function directly.
15729          * @method removeDDFromGroup
15730          * @private
15731          * @static
15732          */
15733         removeDDFromGroup: function(oDD, sGroup) {
15734             if (!this.ids[sGroup]) {
15735                 this.ids[sGroup] = {};
15736             }
15737
15738             var obj = this.ids[sGroup];
15739             if (obj && obj[oDD.id]) {
15740                 delete obj[oDD.id];
15741             }
15742         },
15743
15744         /**
15745          * Unregisters a drag and drop item.  This is executed in
15746          * DragDrop.unreg, use that method instead of calling this directly.
15747          * @method _remove
15748          * @private
15749          * @static
15750          */
15751         _remove: function(oDD) {
15752             for (var g in oDD.groups) {
15753                 if (g && this.ids[g][oDD.id]) {
15754                     delete this.ids[g][oDD.id];
15755                 }
15756             }
15757             delete this.handleIds[oDD.id];
15758         },
15759
15760         /**
15761          * Each DragDrop handle element must be registered.  This is done
15762          * automatically when executing DragDrop.setHandleElId()
15763          * @method regHandle
15764          * @param {String} sDDId the DragDrop id this element is a handle for
15765          * @param {String} sHandleId the id of the element that is the drag
15766          * handle
15767          * @static
15768          */
15769         regHandle: function(sDDId, sHandleId) {
15770             if (!this.handleIds[sDDId]) {
15771                 this.handleIds[sDDId] = {};
15772             }
15773             this.handleIds[sDDId][sHandleId] = sHandleId;
15774         },
15775
15776         /**
15777          * Utility function to determine if a given element has been
15778          * registered as a drag drop item.
15779          * @method isDragDrop
15780          * @param {String} id the element id to check
15781          * @return {boolean} true if this element is a DragDrop item,
15782          * false otherwise
15783          * @static
15784          */
15785         isDragDrop: function(id) {
15786             return ( this.getDDById(id) ) ? true : false;
15787         },
15788
15789         /**
15790          * Returns the drag and drop instances that are in all groups the
15791          * passed in instance belongs to.
15792          * @method getRelated
15793          * @param {DragDrop} p_oDD the obj to get related data for
15794          * @param {boolean} bTargetsOnly if true, only return targetable objs
15795          * @return {DragDrop[]} the related instances
15796          * @static
15797          */
15798         getRelated: function(p_oDD, bTargetsOnly) {
15799             var oDDs = [];
15800             for (var i in p_oDD.groups) {
15801                 for (j in this.ids[i]) {
15802                     var dd = this.ids[i][j];
15803                     if (! this.isTypeOfDD(dd)) {
15804                         continue;
15805                     }
15806                     if (!bTargetsOnly || dd.isTarget) {
15807                         oDDs[oDDs.length] = dd;
15808                     }
15809                 }
15810             }
15811
15812             return oDDs;
15813         },
15814
15815         /**
15816          * Returns true if the specified dd target is a legal target for
15817          * the specifice drag obj
15818          * @method isLegalTarget
15819          * @param {DragDrop} the drag obj
15820          * @param {DragDrop} the target
15821          * @return {boolean} true if the target is a legal target for the
15822          * dd obj
15823          * @static
15824          */
15825         isLegalTarget: function (oDD, oTargetDD) {
15826             var targets = this.getRelated(oDD, true);
15827             for (var i=0, len=targets.length;i<len;++i) {
15828                 if (targets[i].id == oTargetDD.id) {
15829                     return true;
15830                 }
15831             }
15832
15833             return false;
15834         },
15835
15836         /**
15837          * My goal is to be able to transparently determine if an object is
15838          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15839          * returns "object", oDD.constructor.toString() always returns
15840          * "DragDrop" and not the name of the subclass.  So for now it just
15841          * evaluates a well-known variable in DragDrop.
15842          * @method isTypeOfDD
15843          * @param {Object} the object to evaluate
15844          * @return {boolean} true if typeof oDD = DragDrop
15845          * @static
15846          */
15847         isTypeOfDD: function (oDD) {
15848             return (oDD && oDD.__ygDragDrop);
15849         },
15850
15851         /**
15852          * Utility function to determine if a given element has been
15853          * registered as a drag drop handle for the given Drag Drop object.
15854          * @method isHandle
15855          * @param {String} id the element id to check
15856          * @return {boolean} true if this element is a DragDrop handle, false
15857          * otherwise
15858          * @static
15859          */
15860         isHandle: function(sDDId, sHandleId) {
15861             return ( this.handleIds[sDDId] &&
15862                             this.handleIds[sDDId][sHandleId] );
15863         },
15864
15865         /**
15866          * Returns the DragDrop instance for a given id
15867          * @method getDDById
15868          * @param {String} id the id of the DragDrop object
15869          * @return {DragDrop} the drag drop object, null if it is not found
15870          * @static
15871          */
15872         getDDById: function(id) {
15873             for (var i in this.ids) {
15874                 if (this.ids[i][id]) {
15875                     return this.ids[i][id];
15876                 }
15877             }
15878             return null;
15879         },
15880
15881         /**
15882          * Fired after a registered DragDrop object gets the mousedown event.
15883          * Sets up the events required to track the object being dragged
15884          * @method handleMouseDown
15885          * @param {Event} e the event
15886          * @param oDD the DragDrop object being dragged
15887          * @private
15888          * @static
15889          */
15890         handleMouseDown: function(e, oDD) {
15891             if(Roo.QuickTips){
15892                 Roo.QuickTips.disable();
15893             }
15894             this.currentTarget = e.getTarget();
15895
15896             this.dragCurrent = oDD;
15897
15898             var el = oDD.getEl();
15899
15900             // track start position
15901             this.startX = e.getPageX();
15902             this.startY = e.getPageY();
15903
15904             this.deltaX = this.startX - el.offsetLeft;
15905             this.deltaY = this.startY - el.offsetTop;
15906
15907             this.dragThreshMet = false;
15908
15909             this.clickTimeout = setTimeout(
15910                     function() {
15911                         var DDM = Roo.dd.DDM;
15912                         DDM.startDrag(DDM.startX, DDM.startY);
15913                     },
15914                     this.clickTimeThresh );
15915         },
15916
15917         /**
15918          * Fired when either the drag pixel threshol or the mousedown hold
15919          * time threshold has been met.
15920          * @method startDrag
15921          * @param x {int} the X position of the original mousedown
15922          * @param y {int} the Y position of the original mousedown
15923          * @static
15924          */
15925         startDrag: function(x, y) {
15926             clearTimeout(this.clickTimeout);
15927             if (this.dragCurrent) {
15928                 this.dragCurrent.b4StartDrag(x, y);
15929                 this.dragCurrent.startDrag(x, y);
15930             }
15931             this.dragThreshMet = true;
15932         },
15933
15934         /**
15935          * Internal function to handle the mouseup event.  Will be invoked
15936          * from the context of the document.
15937          * @method handleMouseUp
15938          * @param {Event} e the event
15939          * @private
15940          * @static
15941          */
15942         handleMouseUp: function(e) {
15943
15944             if(Roo.QuickTips){
15945                 Roo.QuickTips.enable();
15946             }
15947             if (! this.dragCurrent) {
15948                 return;
15949             }
15950
15951             clearTimeout(this.clickTimeout);
15952
15953             if (this.dragThreshMet) {
15954                 this.fireEvents(e, true);
15955             } else {
15956             }
15957
15958             this.stopDrag(e);
15959
15960             this.stopEvent(e);
15961         },
15962
15963         /**
15964          * Utility to stop event propagation and event default, if these
15965          * features are turned on.
15966          * @method stopEvent
15967          * @param {Event} e the event as returned by this.getEvent()
15968          * @static
15969          */
15970         stopEvent: function(e){
15971             if(this.stopPropagation) {
15972                 e.stopPropagation();
15973             }
15974
15975             if (this.preventDefault) {
15976                 e.preventDefault();
15977             }
15978         },
15979
15980         /**
15981          * Internal function to clean up event handlers after the drag
15982          * operation is complete
15983          * @method stopDrag
15984          * @param {Event} e the event
15985          * @private
15986          * @static
15987          */
15988         stopDrag: function(e) {
15989             // Fire the drag end event for the item that was dragged
15990             if (this.dragCurrent) {
15991                 if (this.dragThreshMet) {
15992                     this.dragCurrent.b4EndDrag(e);
15993                     this.dragCurrent.endDrag(e);
15994                 }
15995
15996                 this.dragCurrent.onMouseUp(e);
15997             }
15998
15999             this.dragCurrent = null;
16000             this.dragOvers = {};
16001         },
16002
16003         /**
16004          * Internal function to handle the mousemove event.  Will be invoked
16005          * from the context of the html element.
16006          *
16007          * @TODO figure out what we can do about mouse events lost when the
16008          * user drags objects beyond the window boundary.  Currently we can
16009          * detect this in internet explorer by verifying that the mouse is
16010          * down during the mousemove event.  Firefox doesn't give us the
16011          * button state on the mousemove event.
16012          * @method handleMouseMove
16013          * @param {Event} e the event
16014          * @private
16015          * @static
16016          */
16017         handleMouseMove: function(e) {
16018             if (! this.dragCurrent) {
16019                 return true;
16020             }
16021
16022             // var button = e.which || e.button;
16023
16024             // check for IE mouseup outside of page boundary
16025             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16026                 this.stopEvent(e);
16027                 return this.handleMouseUp(e);
16028             }
16029
16030             if (!this.dragThreshMet) {
16031                 var diffX = Math.abs(this.startX - e.getPageX());
16032                 var diffY = Math.abs(this.startY - e.getPageY());
16033                 if (diffX > this.clickPixelThresh ||
16034                             diffY > this.clickPixelThresh) {
16035                     this.startDrag(this.startX, this.startY);
16036                 }
16037             }
16038
16039             if (this.dragThreshMet) {
16040                 this.dragCurrent.b4Drag(e);
16041                 this.dragCurrent.onDrag(e);
16042                 if(!this.dragCurrent.moveOnly){
16043                     this.fireEvents(e, false);
16044                 }
16045             }
16046
16047             this.stopEvent(e);
16048
16049             return true;
16050         },
16051
16052         /**
16053          * Iterates over all of the DragDrop elements to find ones we are
16054          * hovering over or dropping on
16055          * @method fireEvents
16056          * @param {Event} e the event
16057          * @param {boolean} isDrop is this a drop op or a mouseover op?
16058          * @private
16059          * @static
16060          */
16061         fireEvents: function(e, isDrop) {
16062             var dc = this.dragCurrent;
16063
16064             // If the user did the mouse up outside of the window, we could
16065             // get here even though we have ended the drag.
16066             if (!dc || dc.isLocked()) {
16067                 return;
16068             }
16069
16070             var pt = e.getPoint();
16071
16072             // cache the previous dragOver array
16073             var oldOvers = [];
16074
16075             var outEvts   = [];
16076             var overEvts  = [];
16077             var dropEvts  = [];
16078             var enterEvts = [];
16079
16080             // Check to see if the object(s) we were hovering over is no longer
16081             // being hovered over so we can fire the onDragOut event
16082             for (var i in this.dragOvers) {
16083
16084                 var ddo = this.dragOvers[i];
16085
16086                 if (! this.isTypeOfDD(ddo)) {
16087                     continue;
16088                 }
16089
16090                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16091                     outEvts.push( ddo );
16092                 }
16093
16094                 oldOvers[i] = true;
16095                 delete this.dragOvers[i];
16096             }
16097
16098             for (var sGroup in dc.groups) {
16099
16100                 if ("string" != typeof sGroup) {
16101                     continue;
16102                 }
16103
16104                 for (i in this.ids[sGroup]) {
16105                     var oDD = this.ids[sGroup][i];
16106                     if (! this.isTypeOfDD(oDD)) {
16107                         continue;
16108                     }
16109
16110                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16111                         if (this.isOverTarget(pt, oDD, this.mode)) {
16112                             // look for drop interactions
16113                             if (isDrop) {
16114                                 dropEvts.push( oDD );
16115                             // look for drag enter and drag over interactions
16116                             } else {
16117
16118                                 // initial drag over: dragEnter fires
16119                                 if (!oldOvers[oDD.id]) {
16120                                     enterEvts.push( oDD );
16121                                 // subsequent drag overs: dragOver fires
16122                                 } else {
16123                                     overEvts.push( oDD );
16124                                 }
16125
16126                                 this.dragOvers[oDD.id] = oDD;
16127                             }
16128                         }
16129                     }
16130                 }
16131             }
16132
16133             if (this.mode) {
16134                 if (outEvts.length) {
16135                     dc.b4DragOut(e, outEvts);
16136                     dc.onDragOut(e, outEvts);
16137                 }
16138
16139                 if (enterEvts.length) {
16140                     dc.onDragEnter(e, enterEvts);
16141                 }
16142
16143                 if (overEvts.length) {
16144                     dc.b4DragOver(e, overEvts);
16145                     dc.onDragOver(e, overEvts);
16146                 }
16147
16148                 if (dropEvts.length) {
16149                     dc.b4DragDrop(e, dropEvts);
16150                     dc.onDragDrop(e, dropEvts);
16151                 }
16152
16153             } else {
16154                 // fire dragout events
16155                 var len = 0;
16156                 for (i=0, len=outEvts.length; i<len; ++i) {
16157                     dc.b4DragOut(e, outEvts[i].id);
16158                     dc.onDragOut(e, outEvts[i].id);
16159                 }
16160
16161                 // fire enter events
16162                 for (i=0,len=enterEvts.length; i<len; ++i) {
16163                     // dc.b4DragEnter(e, oDD.id);
16164                     dc.onDragEnter(e, enterEvts[i].id);
16165                 }
16166
16167                 // fire over events
16168                 for (i=0,len=overEvts.length; i<len; ++i) {
16169                     dc.b4DragOver(e, overEvts[i].id);
16170                     dc.onDragOver(e, overEvts[i].id);
16171                 }
16172
16173                 // fire drop events
16174                 for (i=0, len=dropEvts.length; i<len; ++i) {
16175                     dc.b4DragDrop(e, dropEvts[i].id);
16176                     dc.onDragDrop(e, dropEvts[i].id);
16177                 }
16178
16179             }
16180
16181             // notify about a drop that did not find a target
16182             if (isDrop && !dropEvts.length) {
16183                 dc.onInvalidDrop(e);
16184             }
16185
16186         },
16187
16188         /**
16189          * Helper function for getting the best match from the list of drag
16190          * and drop objects returned by the drag and drop events when we are
16191          * in INTERSECT mode.  It returns either the first object that the
16192          * cursor is over, or the object that has the greatest overlap with
16193          * the dragged element.
16194          * @method getBestMatch
16195          * @param  {DragDrop[]} dds The array of drag and drop objects
16196          * targeted
16197          * @return {DragDrop}       The best single match
16198          * @static
16199          */
16200         getBestMatch: function(dds) {
16201             var winner = null;
16202             // Return null if the input is not what we expect
16203             //if (!dds || !dds.length || dds.length == 0) {
16204                // winner = null;
16205             // If there is only one item, it wins
16206             //} else if (dds.length == 1) {
16207
16208             var len = dds.length;
16209
16210             if (len == 1) {
16211                 winner = dds[0];
16212             } else {
16213                 // Loop through the targeted items
16214                 for (var i=0; i<len; ++i) {
16215                     var dd = dds[i];
16216                     // If the cursor is over the object, it wins.  If the
16217                     // cursor is over multiple matches, the first one we come
16218                     // to wins.
16219                     if (dd.cursorIsOver) {
16220                         winner = dd;
16221                         break;
16222                     // Otherwise the object with the most overlap wins
16223                     } else {
16224                         if (!winner ||
16225                             winner.overlap.getArea() < dd.overlap.getArea()) {
16226                             winner = dd;
16227                         }
16228                     }
16229                 }
16230             }
16231
16232             return winner;
16233         },
16234
16235         /**
16236          * Refreshes the cache of the top-left and bottom-right points of the
16237          * drag and drop objects in the specified group(s).  This is in the
16238          * format that is stored in the drag and drop instance, so typical
16239          * usage is:
16240          * <code>
16241          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16242          * </code>
16243          * Alternatively:
16244          * <code>
16245          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16246          * </code>
16247          * @TODO this really should be an indexed array.  Alternatively this
16248          * method could accept both.
16249          * @method refreshCache
16250          * @param {Object} groups an associative array of groups to refresh
16251          * @static
16252          */
16253         refreshCache: function(groups) {
16254             for (var sGroup in groups) {
16255                 if ("string" != typeof sGroup) {
16256                     continue;
16257                 }
16258                 for (var i in this.ids[sGroup]) {
16259                     var oDD = this.ids[sGroup][i];
16260
16261                     if (this.isTypeOfDD(oDD)) {
16262                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16263                         var loc = this.getLocation(oDD);
16264                         if (loc) {
16265                             this.locationCache[oDD.id] = loc;
16266                         } else {
16267                             delete this.locationCache[oDD.id];
16268                             // this will unregister the drag and drop object if
16269                             // the element is not in a usable state
16270                             // oDD.unreg();
16271                         }
16272                     }
16273                 }
16274             }
16275         },
16276
16277         /**
16278          * This checks to make sure an element exists and is in the DOM.  The
16279          * main purpose is to handle cases where innerHTML is used to remove
16280          * drag and drop objects from the DOM.  IE provides an 'unspecified
16281          * error' when trying to access the offsetParent of such an element
16282          * @method verifyEl
16283          * @param {HTMLElement} el the element to check
16284          * @return {boolean} true if the element looks usable
16285          * @static
16286          */
16287         verifyEl: function(el) {
16288             if (el) {
16289                 var parent;
16290                 if(Roo.isIE){
16291                     try{
16292                         parent = el.offsetParent;
16293                     }catch(e){}
16294                 }else{
16295                     parent = el.offsetParent;
16296                 }
16297                 if (parent) {
16298                     return true;
16299                 }
16300             }
16301
16302             return false;
16303         },
16304
16305         /**
16306          * Returns a Region object containing the drag and drop element's position
16307          * and size, including the padding configured for it
16308          * @method getLocation
16309          * @param {DragDrop} oDD the drag and drop object to get the
16310          *                       location for
16311          * @return {Roo.lib.Region} a Region object representing the total area
16312          *                             the element occupies, including any padding
16313          *                             the instance is configured for.
16314          * @static
16315          */
16316         getLocation: function(oDD) {
16317             if (! this.isTypeOfDD(oDD)) {
16318                 return null;
16319             }
16320
16321             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16322
16323             try {
16324                 pos= Roo.lib.Dom.getXY(el);
16325             } catch (e) { }
16326
16327             if (!pos) {
16328                 return null;
16329             }
16330
16331             x1 = pos[0];
16332             x2 = x1 + el.offsetWidth;
16333             y1 = pos[1];
16334             y2 = y1 + el.offsetHeight;
16335
16336             t = y1 - oDD.padding[0];
16337             r = x2 + oDD.padding[1];
16338             b = y2 + oDD.padding[2];
16339             l = x1 - oDD.padding[3];
16340
16341             return new Roo.lib.Region( t, r, b, l );
16342         },
16343
16344         /**
16345          * Checks the cursor location to see if it over the target
16346          * @method isOverTarget
16347          * @param {Roo.lib.Point} pt The point to evaluate
16348          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16349          * @return {boolean} true if the mouse is over the target
16350          * @private
16351          * @static
16352          */
16353         isOverTarget: function(pt, oTarget, intersect) {
16354             // use cache if available
16355             var loc = this.locationCache[oTarget.id];
16356             if (!loc || !this.useCache) {
16357                 loc = this.getLocation(oTarget);
16358                 this.locationCache[oTarget.id] = loc;
16359
16360             }
16361
16362             if (!loc) {
16363                 return false;
16364             }
16365
16366             oTarget.cursorIsOver = loc.contains( pt );
16367
16368             // DragDrop is using this as a sanity check for the initial mousedown
16369             // in this case we are done.  In POINT mode, if the drag obj has no
16370             // contraints, we are also done. Otherwise we need to evaluate the
16371             // location of the target as related to the actual location of the
16372             // dragged element.
16373             var dc = this.dragCurrent;
16374             if (!dc || !dc.getTargetCoord ||
16375                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16376                 return oTarget.cursorIsOver;
16377             }
16378
16379             oTarget.overlap = null;
16380
16381             // Get the current location of the drag element, this is the
16382             // location of the mouse event less the delta that represents
16383             // where the original mousedown happened on the element.  We
16384             // need to consider constraints and ticks as well.
16385             var pos = dc.getTargetCoord(pt.x, pt.y);
16386
16387             var el = dc.getDragEl();
16388             var curRegion = new Roo.lib.Region( pos.y,
16389                                                    pos.x + el.offsetWidth,
16390                                                    pos.y + el.offsetHeight,
16391                                                    pos.x );
16392
16393             var overlap = curRegion.intersect(loc);
16394
16395             if (overlap) {
16396                 oTarget.overlap = overlap;
16397                 return (intersect) ? true : oTarget.cursorIsOver;
16398             } else {
16399                 return false;
16400             }
16401         },
16402
16403         /**
16404          * unload event handler
16405          * @method _onUnload
16406          * @private
16407          * @static
16408          */
16409         _onUnload: function(e, me) {
16410             Roo.dd.DragDropMgr.unregAll();
16411         },
16412
16413         /**
16414          * Cleans up the drag and drop events and objects.
16415          * @method unregAll
16416          * @private
16417          * @static
16418          */
16419         unregAll: function() {
16420
16421             if (this.dragCurrent) {
16422                 this.stopDrag();
16423                 this.dragCurrent = null;
16424             }
16425
16426             this._execOnAll("unreg", []);
16427
16428             for (i in this.elementCache) {
16429                 delete this.elementCache[i];
16430             }
16431
16432             this.elementCache = {};
16433             this.ids = {};
16434         },
16435
16436         /**
16437          * A cache of DOM elements
16438          * @property elementCache
16439          * @private
16440          * @static
16441          */
16442         elementCache: {},
16443
16444         /**
16445          * Get the wrapper for the DOM element specified
16446          * @method getElWrapper
16447          * @param {String} id the id of the element to get
16448          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16449          * @private
16450          * @deprecated This wrapper isn't that useful
16451          * @static
16452          */
16453         getElWrapper: function(id) {
16454             var oWrapper = this.elementCache[id];
16455             if (!oWrapper || !oWrapper.el) {
16456                 oWrapper = this.elementCache[id] =
16457                     new this.ElementWrapper(Roo.getDom(id));
16458             }
16459             return oWrapper;
16460         },
16461
16462         /**
16463          * Returns the actual DOM element
16464          * @method getElement
16465          * @param {String} id the id of the elment to get
16466          * @return {Object} The element
16467          * @deprecated use Roo.getDom instead
16468          * @static
16469          */
16470         getElement: function(id) {
16471             return Roo.getDom(id);
16472         },
16473
16474         /**
16475          * Returns the style property for the DOM element (i.e.,
16476          * document.getElById(id).style)
16477          * @method getCss
16478          * @param {String} id the id of the elment to get
16479          * @return {Object} The style property of the element
16480          * @deprecated use Roo.getDom instead
16481          * @static
16482          */
16483         getCss: function(id) {
16484             var el = Roo.getDom(id);
16485             return (el) ? el.style : null;
16486         },
16487
16488         /**
16489          * Inner class for cached elements
16490          * @class DragDropMgr.ElementWrapper
16491          * @for DragDropMgr
16492          * @private
16493          * @deprecated
16494          */
16495         ElementWrapper: function(el) {
16496                 /**
16497                  * The element
16498                  * @property el
16499                  */
16500                 this.el = el || null;
16501                 /**
16502                  * The element id
16503                  * @property id
16504                  */
16505                 this.id = this.el && el.id;
16506                 /**
16507                  * A reference to the style property
16508                  * @property css
16509                  */
16510                 this.css = this.el && el.style;
16511             },
16512
16513         /**
16514          * Returns the X position of an html element
16515          * @method getPosX
16516          * @param el the element for which to get the position
16517          * @return {int} the X coordinate
16518          * @for DragDropMgr
16519          * @deprecated use Roo.lib.Dom.getX instead
16520          * @static
16521          */
16522         getPosX: function(el) {
16523             return Roo.lib.Dom.getX(el);
16524         },
16525
16526         /**
16527          * Returns the Y position of an html element
16528          * @method getPosY
16529          * @param el the element for which to get the position
16530          * @return {int} the Y coordinate
16531          * @deprecated use Roo.lib.Dom.getY instead
16532          * @static
16533          */
16534         getPosY: function(el) {
16535             return Roo.lib.Dom.getY(el);
16536         },
16537
16538         /**
16539          * Swap two nodes.  In IE, we use the native method, for others we
16540          * emulate the IE behavior
16541          * @method swapNode
16542          * @param n1 the first node to swap
16543          * @param n2 the other node to swap
16544          * @static
16545          */
16546         swapNode: function(n1, n2) {
16547             if (n1.swapNode) {
16548                 n1.swapNode(n2);
16549             } else {
16550                 var p = n2.parentNode;
16551                 var s = n2.nextSibling;
16552
16553                 if (s == n1) {
16554                     p.insertBefore(n1, n2);
16555                 } else if (n2 == n1.nextSibling) {
16556                     p.insertBefore(n2, n1);
16557                 } else {
16558                     n1.parentNode.replaceChild(n2, n1);
16559                     p.insertBefore(n1, s);
16560                 }
16561             }
16562         },
16563
16564         /**
16565          * Returns the current scroll position
16566          * @method getScroll
16567          * @private
16568          * @static
16569          */
16570         getScroll: function () {
16571             var t, l, dde=document.documentElement, db=document.body;
16572             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16573                 t = dde.scrollTop;
16574                 l = dde.scrollLeft;
16575             } else if (db) {
16576                 t = db.scrollTop;
16577                 l = db.scrollLeft;
16578             } else {
16579
16580             }
16581             return { top: t, left: l };
16582         },
16583
16584         /**
16585          * Returns the specified element style property
16586          * @method getStyle
16587          * @param {HTMLElement} el          the element
16588          * @param {string}      styleProp   the style property
16589          * @return {string} The value of the style property
16590          * @deprecated use Roo.lib.Dom.getStyle
16591          * @static
16592          */
16593         getStyle: function(el, styleProp) {
16594             return Roo.fly(el).getStyle(styleProp);
16595         },
16596
16597         /**
16598          * Gets the scrollTop
16599          * @method getScrollTop
16600          * @return {int} the document's scrollTop
16601          * @static
16602          */
16603         getScrollTop: function () { return this.getScroll().top; },
16604
16605         /**
16606          * Gets the scrollLeft
16607          * @method getScrollLeft
16608          * @return {int} the document's scrollTop
16609          * @static
16610          */
16611         getScrollLeft: function () { return this.getScroll().left; },
16612
16613         /**
16614          * Sets the x/y position of an element to the location of the
16615          * target element.
16616          * @method moveToEl
16617          * @param {HTMLElement} moveEl      The element to move
16618          * @param {HTMLElement} targetEl    The position reference element
16619          * @static
16620          */
16621         moveToEl: function (moveEl, targetEl) {
16622             var aCoord = Roo.lib.Dom.getXY(targetEl);
16623             Roo.lib.Dom.setXY(moveEl, aCoord);
16624         },
16625
16626         /**
16627          * Numeric array sort function
16628          * @method numericSort
16629          * @static
16630          */
16631         numericSort: function(a, b) { return (a - b); },
16632
16633         /**
16634          * Internal counter
16635          * @property _timeoutCount
16636          * @private
16637          * @static
16638          */
16639         _timeoutCount: 0,
16640
16641         /**
16642          * Trying to make the load order less important.  Without this we get
16643          * an error if this file is loaded before the Event Utility.
16644          * @method _addListeners
16645          * @private
16646          * @static
16647          */
16648         _addListeners: function() {
16649             var DDM = Roo.dd.DDM;
16650             if ( Roo.lib.Event && document ) {
16651                 DDM._onLoad();
16652             } else {
16653                 if (DDM._timeoutCount > 2000) {
16654                 } else {
16655                     setTimeout(DDM._addListeners, 10);
16656                     if (document && document.body) {
16657                         DDM._timeoutCount += 1;
16658                     }
16659                 }
16660             }
16661         },
16662
16663         /**
16664          * Recursively searches the immediate parent and all child nodes for
16665          * the handle element in order to determine wheter or not it was
16666          * clicked.
16667          * @method handleWasClicked
16668          * @param node the html element to inspect
16669          * @static
16670          */
16671         handleWasClicked: function(node, id) {
16672             if (this.isHandle(id, node.id)) {
16673                 return true;
16674             } else {
16675                 // check to see if this is a text node child of the one we want
16676                 var p = node.parentNode;
16677
16678                 while (p) {
16679                     if (this.isHandle(id, p.id)) {
16680                         return true;
16681                     } else {
16682                         p = p.parentNode;
16683                     }
16684                 }
16685             }
16686
16687             return false;
16688         }
16689
16690     };
16691
16692 }();
16693
16694 // shorter alias, save a few bytes
16695 Roo.dd.DDM = Roo.dd.DragDropMgr;
16696 Roo.dd.DDM._addListeners();
16697
16698 }/*
16699  * Based on:
16700  * Ext JS Library 1.1.1
16701  * Copyright(c) 2006-2007, Ext JS, LLC.
16702  *
16703  * Originally Released Under LGPL - original licence link has changed is not relivant.
16704  *
16705  * Fork - LGPL
16706  * <script type="text/javascript">
16707  */
16708
16709 /**
16710  * @class Roo.dd.DD
16711  * A DragDrop implementation where the linked element follows the
16712  * mouse cursor during a drag.
16713  * @extends Roo.dd.DragDrop
16714  * @constructor
16715  * @param {String} id the id of the linked element
16716  * @param {String} sGroup the group of related DragDrop items
16717  * @param {object} config an object containing configurable attributes
16718  *                Valid properties for DD:
16719  *                    scroll
16720  */
16721 Roo.dd.DD = function(id, sGroup, config) {
16722     if (id) {
16723         this.init(id, sGroup, config);
16724     }
16725 };
16726
16727 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16728
16729     /**
16730      * When set to true, the utility automatically tries to scroll the browser
16731      * window wehn a drag and drop element is dragged near the viewport boundary.
16732      * Defaults to true.
16733      * @property scroll
16734      * @type boolean
16735      */
16736     scroll: true,
16737
16738     /**
16739      * Sets the pointer offset to the distance between the linked element's top
16740      * left corner and the location the element was clicked
16741      * @method autoOffset
16742      * @param {int} iPageX the X coordinate of the click
16743      * @param {int} iPageY the Y coordinate of the click
16744      */
16745     autoOffset: function(iPageX, iPageY) {
16746         var x = iPageX - this.startPageX;
16747         var y = iPageY - this.startPageY;
16748         this.setDelta(x, y);
16749     },
16750
16751     /**
16752      * Sets the pointer offset.  You can call this directly to force the
16753      * offset to be in a particular location (e.g., pass in 0,0 to set it
16754      * to the center of the object)
16755      * @method setDelta
16756      * @param {int} iDeltaX the distance from the left
16757      * @param {int} iDeltaY the distance from the top
16758      */
16759     setDelta: function(iDeltaX, iDeltaY) {
16760         this.deltaX = iDeltaX;
16761         this.deltaY = iDeltaY;
16762     },
16763
16764     /**
16765      * Sets the drag element to the location of the mousedown or click event,
16766      * maintaining the cursor location relative to the location on the element
16767      * that was clicked.  Override this if you want to place the element in a
16768      * location other than where the cursor is.
16769      * @method setDragElPos
16770      * @param {int} iPageX the X coordinate of the mousedown or drag event
16771      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16772      */
16773     setDragElPos: function(iPageX, iPageY) {
16774         // the first time we do this, we are going to check to make sure
16775         // the element has css positioning
16776
16777         var el = this.getDragEl();
16778         this.alignElWithMouse(el, iPageX, iPageY);
16779     },
16780
16781     /**
16782      * Sets the element to the location of the mousedown or click event,
16783      * maintaining the cursor location relative to the location on the element
16784      * that was clicked.  Override this if you want to place the element in a
16785      * location other than where the cursor is.
16786      * @method alignElWithMouse
16787      * @param {HTMLElement} el the element to move
16788      * @param {int} iPageX the X coordinate of the mousedown or drag event
16789      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16790      */
16791     alignElWithMouse: function(el, iPageX, iPageY) {
16792         var oCoord = this.getTargetCoord(iPageX, iPageY);
16793         var fly = el.dom ? el : Roo.fly(el);
16794         if (!this.deltaSetXY) {
16795             var aCoord = [oCoord.x, oCoord.y];
16796             fly.setXY(aCoord);
16797             var newLeft = fly.getLeft(true);
16798             var newTop  = fly.getTop(true);
16799             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16800         } else {
16801             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16802         }
16803
16804         this.cachePosition(oCoord.x, oCoord.y);
16805         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16806         return oCoord;
16807     },
16808
16809     /**
16810      * Saves the most recent position so that we can reset the constraints and
16811      * tick marks on-demand.  We need to know this so that we can calculate the
16812      * number of pixels the element is offset from its original position.
16813      * @method cachePosition
16814      * @param iPageX the current x position (optional, this just makes it so we
16815      * don't have to look it up again)
16816      * @param iPageY the current y position (optional, this just makes it so we
16817      * don't have to look it up again)
16818      */
16819     cachePosition: function(iPageX, iPageY) {
16820         if (iPageX) {
16821             this.lastPageX = iPageX;
16822             this.lastPageY = iPageY;
16823         } else {
16824             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16825             this.lastPageX = aCoord[0];
16826             this.lastPageY = aCoord[1];
16827         }
16828     },
16829
16830     /**
16831      * Auto-scroll the window if the dragged object has been moved beyond the
16832      * visible window boundary.
16833      * @method autoScroll
16834      * @param {int} x the drag element's x position
16835      * @param {int} y the drag element's y position
16836      * @param {int} h the height of the drag element
16837      * @param {int} w the width of the drag element
16838      * @private
16839      */
16840     autoScroll: function(x, y, h, w) {
16841
16842         if (this.scroll) {
16843             // The client height
16844             var clientH = Roo.lib.Dom.getViewWidth();
16845
16846             // The client width
16847             var clientW = Roo.lib.Dom.getViewHeight();
16848
16849             // The amt scrolled down
16850             var st = this.DDM.getScrollTop();
16851
16852             // The amt scrolled right
16853             var sl = this.DDM.getScrollLeft();
16854
16855             // Location of the bottom of the element
16856             var bot = h + y;
16857
16858             // Location of the right of the element
16859             var right = w + x;
16860
16861             // The distance from the cursor to the bottom of the visible area,
16862             // adjusted so that we don't scroll if the cursor is beyond the
16863             // element drag constraints
16864             var toBot = (clientH + st - y - this.deltaY);
16865
16866             // The distance from the cursor to the right of the visible area
16867             var toRight = (clientW + sl - x - this.deltaX);
16868
16869
16870             // How close to the edge the cursor must be before we scroll
16871             // var thresh = (document.all) ? 100 : 40;
16872             var thresh = 40;
16873
16874             // How many pixels to scroll per autoscroll op.  This helps to reduce
16875             // clunky scrolling. IE is more sensitive about this ... it needs this
16876             // value to be higher.
16877             var scrAmt = (document.all) ? 80 : 30;
16878
16879             // Scroll down if we are near the bottom of the visible page and the
16880             // obj extends below the crease
16881             if ( bot > clientH && toBot < thresh ) {
16882                 window.scrollTo(sl, st + scrAmt);
16883             }
16884
16885             // Scroll up if the window is scrolled down and the top of the object
16886             // goes above the top border
16887             if ( y < st && st > 0 && y - st < thresh ) {
16888                 window.scrollTo(sl, st - scrAmt);
16889             }
16890
16891             // Scroll right if the obj is beyond the right border and the cursor is
16892             // near the border.
16893             if ( right > clientW && toRight < thresh ) {
16894                 window.scrollTo(sl + scrAmt, st);
16895             }
16896
16897             // Scroll left if the window has been scrolled to the right and the obj
16898             // extends past the left border
16899             if ( x < sl && sl > 0 && x - sl < thresh ) {
16900                 window.scrollTo(sl - scrAmt, st);
16901             }
16902         }
16903     },
16904
16905     /**
16906      * Finds the location the element should be placed if we want to move
16907      * it to where the mouse location less the click offset would place us.
16908      * @method getTargetCoord
16909      * @param {int} iPageX the X coordinate of the click
16910      * @param {int} iPageY the Y coordinate of the click
16911      * @return an object that contains the coordinates (Object.x and Object.y)
16912      * @private
16913      */
16914     getTargetCoord: function(iPageX, iPageY) {
16915
16916
16917         var x = iPageX - this.deltaX;
16918         var y = iPageY - this.deltaY;
16919
16920         if (this.constrainX) {
16921             if (x < this.minX) { x = this.minX; }
16922             if (x > this.maxX) { x = this.maxX; }
16923         }
16924
16925         if (this.constrainY) {
16926             if (y < this.minY) { y = this.minY; }
16927             if (y > this.maxY) { y = this.maxY; }
16928         }
16929
16930         x = this.getTick(x, this.xTicks);
16931         y = this.getTick(y, this.yTicks);
16932
16933
16934         return {x:x, y:y};
16935     },
16936
16937     /*
16938      * Sets up config options specific to this class. Overrides
16939      * Roo.dd.DragDrop, but all versions of this method through the
16940      * inheritance chain are called
16941      */
16942     applyConfig: function() {
16943         Roo.dd.DD.superclass.applyConfig.call(this);
16944         this.scroll = (this.config.scroll !== false);
16945     },
16946
16947     /*
16948      * Event that fires prior to the onMouseDown event.  Overrides
16949      * Roo.dd.DragDrop.
16950      */
16951     b4MouseDown: function(e) {
16952         // this.resetConstraints();
16953         this.autoOffset(e.getPageX(),
16954                             e.getPageY());
16955     },
16956
16957     /*
16958      * Event that fires prior to the onDrag event.  Overrides
16959      * Roo.dd.DragDrop.
16960      */
16961     b4Drag: function(e) {
16962         this.setDragElPos(e.getPageX(),
16963                             e.getPageY());
16964     },
16965
16966     toString: function() {
16967         return ("DD " + this.id);
16968     }
16969
16970     //////////////////////////////////////////////////////////////////////////
16971     // Debugging ygDragDrop events that can be overridden
16972     //////////////////////////////////////////////////////////////////////////
16973     /*
16974     startDrag: function(x, y) {
16975     },
16976
16977     onDrag: function(e) {
16978     },
16979
16980     onDragEnter: function(e, id) {
16981     },
16982
16983     onDragOver: function(e, id) {
16984     },
16985
16986     onDragOut: function(e, id) {
16987     },
16988
16989     onDragDrop: function(e, id) {
16990     },
16991
16992     endDrag: function(e) {
16993     }
16994
16995     */
16996
16997 });/*
16998  * Based on:
16999  * Ext JS Library 1.1.1
17000  * Copyright(c) 2006-2007, Ext JS, LLC.
17001  *
17002  * Originally Released Under LGPL - original licence link has changed is not relivant.
17003  *
17004  * Fork - LGPL
17005  * <script type="text/javascript">
17006  */
17007
17008 /**
17009  * @class Roo.dd.DDProxy
17010  * A DragDrop implementation that inserts an empty, bordered div into
17011  * the document that follows the cursor during drag operations.  At the time of
17012  * the click, the frame div is resized to the dimensions of the linked html
17013  * element, and moved to the exact location of the linked element.
17014  *
17015  * References to the "frame" element refer to the single proxy element that
17016  * was created to be dragged in place of all DDProxy elements on the
17017  * page.
17018  *
17019  * @extends Roo.dd.DD
17020  * @constructor
17021  * @param {String} id the id of the linked html element
17022  * @param {String} sGroup the group of related DragDrop objects
17023  * @param {object} config an object containing configurable attributes
17024  *                Valid properties for DDProxy in addition to those in DragDrop:
17025  *                   resizeFrame, centerFrame, dragElId
17026  */
17027 Roo.dd.DDProxy = function(id, sGroup, config) {
17028     if (id) {
17029         this.init(id, sGroup, config);
17030         this.initFrame();
17031     }
17032 };
17033
17034 /**
17035  * The default drag frame div id
17036  * @property Roo.dd.DDProxy.dragElId
17037  * @type String
17038  * @static
17039  */
17040 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17041
17042 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17043
17044     /**
17045      * By default we resize the drag frame to be the same size as the element
17046      * we want to drag (this is to get the frame effect).  We can turn it off
17047      * if we want a different behavior.
17048      * @property resizeFrame
17049      * @type boolean
17050      */
17051     resizeFrame: true,
17052
17053     /**
17054      * By default the frame is positioned exactly where the drag element is, so
17055      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17056      * you do not have constraints on the obj is to have the drag frame centered
17057      * around the cursor.  Set centerFrame to true for this effect.
17058      * @property centerFrame
17059      * @type boolean
17060      */
17061     centerFrame: false,
17062
17063     /**
17064      * Creates the proxy element if it does not yet exist
17065      * @method createFrame
17066      */
17067     createFrame: function() {
17068         var self = this;
17069         var body = document.body;
17070
17071         if (!body || !body.firstChild) {
17072             setTimeout( function() { self.createFrame(); }, 50 );
17073             return;
17074         }
17075
17076         var div = this.getDragEl();
17077
17078         if (!div) {
17079             div    = document.createElement("div");
17080             div.id = this.dragElId;
17081             var s  = div.style;
17082
17083             s.position   = "absolute";
17084             s.visibility = "hidden";
17085             s.cursor     = "move";
17086             s.border     = "2px solid #aaa";
17087             s.zIndex     = 999;
17088
17089             // appendChild can blow up IE if invoked prior to the window load event
17090             // while rendering a table.  It is possible there are other scenarios
17091             // that would cause this to happen as well.
17092             body.insertBefore(div, body.firstChild);
17093         }
17094     },
17095
17096     /**
17097      * Initialization for the drag frame element.  Must be called in the
17098      * constructor of all subclasses
17099      * @method initFrame
17100      */
17101     initFrame: function() {
17102         this.createFrame();
17103     },
17104
17105     applyConfig: function() {
17106         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17107
17108         this.resizeFrame = (this.config.resizeFrame !== false);
17109         this.centerFrame = (this.config.centerFrame);
17110         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17111     },
17112
17113     /**
17114      * Resizes the drag frame to the dimensions of the clicked object, positions
17115      * it over the object, and finally displays it
17116      * @method showFrame
17117      * @param {int} iPageX X click position
17118      * @param {int} iPageY Y click position
17119      * @private
17120      */
17121     showFrame: function(iPageX, iPageY) {
17122         var el = this.getEl();
17123         var dragEl = this.getDragEl();
17124         var s = dragEl.style;
17125
17126         this._resizeProxy();
17127
17128         if (this.centerFrame) {
17129             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17130                            Math.round(parseInt(s.height, 10)/2) );
17131         }
17132
17133         this.setDragElPos(iPageX, iPageY);
17134
17135         Roo.fly(dragEl).show();
17136     },
17137
17138     /**
17139      * The proxy is automatically resized to the dimensions of the linked
17140      * element when a drag is initiated, unless resizeFrame is set to false
17141      * @method _resizeProxy
17142      * @private
17143      */
17144     _resizeProxy: function() {
17145         if (this.resizeFrame) {
17146             var el = this.getEl();
17147             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17148         }
17149     },
17150
17151     // overrides Roo.dd.DragDrop
17152     b4MouseDown: function(e) {
17153         var x = e.getPageX();
17154         var y = e.getPageY();
17155         this.autoOffset(x, y);
17156         this.setDragElPos(x, y);
17157     },
17158
17159     // overrides Roo.dd.DragDrop
17160     b4StartDrag: function(x, y) {
17161         // show the drag frame
17162         this.showFrame(x, y);
17163     },
17164
17165     // overrides Roo.dd.DragDrop
17166     b4EndDrag: function(e) {
17167         Roo.fly(this.getDragEl()).hide();
17168     },
17169
17170     // overrides Roo.dd.DragDrop
17171     // By default we try to move the element to the last location of the frame.
17172     // This is so that the default behavior mirrors that of Roo.dd.DD.
17173     endDrag: function(e) {
17174
17175         var lel = this.getEl();
17176         var del = this.getDragEl();
17177
17178         // Show the drag frame briefly so we can get its position
17179         del.style.visibility = "";
17180
17181         this.beforeMove();
17182         // Hide the linked element before the move to get around a Safari
17183         // rendering bug.
17184         lel.style.visibility = "hidden";
17185         Roo.dd.DDM.moveToEl(lel, del);
17186         del.style.visibility = "hidden";
17187         lel.style.visibility = "";
17188
17189         this.afterDrag();
17190     },
17191
17192     beforeMove : function(){
17193
17194     },
17195
17196     afterDrag : function(){
17197
17198     },
17199
17200     toString: function() {
17201         return ("DDProxy " + this.id);
17202     }
17203
17204 });
17205 /*
17206  * Based on:
17207  * Ext JS Library 1.1.1
17208  * Copyright(c) 2006-2007, Ext JS, LLC.
17209  *
17210  * Originally Released Under LGPL - original licence link has changed is not relivant.
17211  *
17212  * Fork - LGPL
17213  * <script type="text/javascript">
17214  */
17215
17216  /**
17217  * @class Roo.dd.DDTarget
17218  * A DragDrop implementation that does not move, but can be a drop
17219  * target.  You would get the same result by simply omitting implementation
17220  * for the event callbacks, but this way we reduce the processing cost of the
17221  * event listener and the callbacks.
17222  * @extends Roo.dd.DragDrop
17223  * @constructor
17224  * @param {String} id the id of the element that is a drop target
17225  * @param {String} sGroup the group of related DragDrop objects
17226  * @param {object} config an object containing configurable attributes
17227  *                 Valid properties for DDTarget in addition to those in
17228  *                 DragDrop:
17229  *                    none
17230  */
17231 Roo.dd.DDTarget = function(id, sGroup, config) {
17232     if (id) {
17233         this.initTarget(id, sGroup, config);
17234     }
17235 };
17236
17237 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17238 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17239     toString: function() {
17240         return ("DDTarget " + this.id);
17241     }
17242 });
17243 /*
17244  * Based on:
17245  * Ext JS Library 1.1.1
17246  * Copyright(c) 2006-2007, Ext JS, LLC.
17247  *
17248  * Originally Released Under LGPL - original licence link has changed is not relivant.
17249  *
17250  * Fork - LGPL
17251  * <script type="text/javascript">
17252  */
17253  
17254
17255 /**
17256  * @class Roo.dd.ScrollManager
17257  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17258  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17259  * @singleton
17260  */
17261 Roo.dd.ScrollManager = function(){
17262     var ddm = Roo.dd.DragDropMgr;
17263     var els = {};
17264     var dragEl = null;
17265     var proc = {};
17266     
17267     var onStop = function(e){
17268         dragEl = null;
17269         clearProc();
17270     };
17271     
17272     var triggerRefresh = function(){
17273         if(ddm.dragCurrent){
17274              ddm.refreshCache(ddm.dragCurrent.groups);
17275         }
17276     };
17277     
17278     var doScroll = function(){
17279         if(ddm.dragCurrent){
17280             var dds = Roo.dd.ScrollManager;
17281             if(!dds.animate){
17282                 if(proc.el.scroll(proc.dir, dds.increment)){
17283                     triggerRefresh();
17284                 }
17285             }else{
17286                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17287             }
17288         }
17289     };
17290     
17291     var clearProc = function(){
17292         if(proc.id){
17293             clearInterval(proc.id);
17294         }
17295         proc.id = 0;
17296         proc.el = null;
17297         proc.dir = "";
17298     };
17299     
17300     var startProc = function(el, dir){
17301         clearProc();
17302         proc.el = el;
17303         proc.dir = dir;
17304         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17305     };
17306     
17307     var onFire = function(e, isDrop){
17308         if(isDrop || !ddm.dragCurrent){ return; }
17309         var dds = Roo.dd.ScrollManager;
17310         if(!dragEl || dragEl != ddm.dragCurrent){
17311             dragEl = ddm.dragCurrent;
17312             // refresh regions on drag start
17313             dds.refreshCache();
17314         }
17315         
17316         var xy = Roo.lib.Event.getXY(e);
17317         var pt = new Roo.lib.Point(xy[0], xy[1]);
17318         for(var id in els){
17319             var el = els[id], r = el._region;
17320             if(r && r.contains(pt) && el.isScrollable()){
17321                 if(r.bottom - pt.y <= dds.thresh){
17322                     if(proc.el != el){
17323                         startProc(el, "down");
17324                     }
17325                     return;
17326                 }else if(r.right - pt.x <= dds.thresh){
17327                     if(proc.el != el){
17328                         startProc(el, "left");
17329                     }
17330                     return;
17331                 }else if(pt.y - r.top <= dds.thresh){
17332                     if(proc.el != el){
17333                         startProc(el, "up");
17334                     }
17335                     return;
17336                 }else if(pt.x - r.left <= dds.thresh){
17337                     if(proc.el != el){
17338                         startProc(el, "right");
17339                     }
17340                     return;
17341                 }
17342             }
17343         }
17344         clearProc();
17345     };
17346     
17347     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17348     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17349     
17350     return {
17351         /**
17352          * Registers new overflow element(s) to auto scroll
17353          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17354          */
17355         register : function(el){
17356             if(el instanceof Array){
17357                 for(var i = 0, len = el.length; i < len; i++) {
17358                         this.register(el[i]);
17359                 }
17360             }else{
17361                 el = Roo.get(el);
17362                 els[el.id] = el;
17363             }
17364         },
17365         
17366         /**
17367          * Unregisters overflow element(s) so they are no longer scrolled
17368          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17369          */
17370         unregister : function(el){
17371             if(el instanceof Array){
17372                 for(var i = 0, len = el.length; i < len; i++) {
17373                         this.unregister(el[i]);
17374                 }
17375             }else{
17376                 el = Roo.get(el);
17377                 delete els[el.id];
17378             }
17379         },
17380         
17381         /**
17382          * The number of pixels from the edge of a container the pointer needs to be to 
17383          * trigger scrolling (defaults to 25)
17384          * @type Number
17385          */
17386         thresh : 25,
17387         
17388         /**
17389          * The number of pixels to scroll in each scroll increment (defaults to 50)
17390          * @type Number
17391          */
17392         increment : 100,
17393         
17394         /**
17395          * The frequency of scrolls in milliseconds (defaults to 500)
17396          * @type Number
17397          */
17398         frequency : 500,
17399         
17400         /**
17401          * True to animate the scroll (defaults to true)
17402          * @type Boolean
17403          */
17404         animate: true,
17405         
17406         /**
17407          * The animation duration in seconds - 
17408          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17409          * @type Number
17410          */
17411         animDuration: .4,
17412         
17413         /**
17414          * Manually trigger a cache refresh.
17415          */
17416         refreshCache : function(){
17417             for(var id in els){
17418                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17419                     els[id]._region = els[id].getRegion();
17420                 }
17421             }
17422         }
17423     };
17424 }();/*
17425  * Based on:
17426  * Ext JS Library 1.1.1
17427  * Copyright(c) 2006-2007, Ext JS, LLC.
17428  *
17429  * Originally Released Under LGPL - original licence link has changed is not relivant.
17430  *
17431  * Fork - LGPL
17432  * <script type="text/javascript">
17433  */
17434  
17435
17436 /**
17437  * @class Roo.dd.Registry
17438  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17439  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17440  * @singleton
17441  */
17442 Roo.dd.Registry = function(){
17443     var elements = {}; 
17444     var handles = {}; 
17445     var autoIdSeed = 0;
17446
17447     var getId = function(el, autogen){
17448         if(typeof el == "string"){
17449             return el;
17450         }
17451         var id = el.id;
17452         if(!id && autogen !== false){
17453             id = "roodd-" + (++autoIdSeed);
17454             el.id = id;
17455         }
17456         return id;
17457     };
17458     
17459     return {
17460     /**
17461      * Register a drag drop element
17462      * @param {String/HTMLElement) element The id or DOM node to register
17463      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17464      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17465      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17466      * populated in the data object (if applicable):
17467      * <pre>
17468 Value      Description<br />
17469 ---------  ------------------------------------------<br />
17470 handles    Array of DOM nodes that trigger dragging<br />
17471            for the element being registered<br />
17472 isHandle   True if the element passed in triggers<br />
17473            dragging itself, else false
17474 </pre>
17475      */
17476         register : function(el, data){
17477             data = data || {};
17478             if(typeof el == "string"){
17479                 el = document.getElementById(el);
17480             }
17481             data.ddel = el;
17482             elements[getId(el)] = data;
17483             if(data.isHandle !== false){
17484                 handles[data.ddel.id] = data;
17485             }
17486             if(data.handles){
17487                 var hs = data.handles;
17488                 for(var i = 0, len = hs.length; i < len; i++){
17489                         handles[getId(hs[i])] = data;
17490                 }
17491             }
17492         },
17493
17494     /**
17495      * Unregister a drag drop element
17496      * @param {String/HTMLElement) element The id or DOM node to unregister
17497      */
17498         unregister : function(el){
17499             var id = getId(el, false);
17500             var data = elements[id];
17501             if(data){
17502                 delete elements[id];
17503                 if(data.handles){
17504                     var hs = data.handles;
17505                     for(var i = 0, len = hs.length; i < len; i++){
17506                         delete handles[getId(hs[i], false)];
17507                     }
17508                 }
17509             }
17510         },
17511
17512     /**
17513      * Returns the handle registered for a DOM Node by id
17514      * @param {String/HTMLElement} id The DOM node or id to look up
17515      * @return {Object} handle The custom handle data
17516      */
17517         getHandle : function(id){
17518             if(typeof id != "string"){ // must be element?
17519                 id = id.id;
17520             }
17521             return handles[id];
17522         },
17523
17524     /**
17525      * Returns the handle that is registered for the DOM node that is the target of the event
17526      * @param {Event} e The event
17527      * @return {Object} handle The custom handle data
17528      */
17529         getHandleFromEvent : function(e){
17530             var t = Roo.lib.Event.getTarget(e);
17531             return t ? handles[t.id] : null;
17532         },
17533
17534     /**
17535      * Returns a custom data object that is registered for a DOM node by id
17536      * @param {String/HTMLElement} id The DOM node or id to look up
17537      * @return {Object} data The custom data
17538      */
17539         getTarget : function(id){
17540             if(typeof id != "string"){ // must be element?
17541                 id = id.id;
17542             }
17543             return elements[id];
17544         },
17545
17546     /**
17547      * Returns a custom data object that is registered for the DOM node that is the target of the event
17548      * @param {Event} e The event
17549      * @return {Object} data The custom data
17550      */
17551         getTargetFromEvent : function(e){
17552             var t = Roo.lib.Event.getTarget(e);
17553             return t ? elements[t.id] || handles[t.id] : null;
17554         }
17555     };
17556 }();/*
17557  * Based on:
17558  * Ext JS Library 1.1.1
17559  * Copyright(c) 2006-2007, Ext JS, LLC.
17560  *
17561  * Originally Released Under LGPL - original licence link has changed is not relivant.
17562  *
17563  * Fork - LGPL
17564  * <script type="text/javascript">
17565  */
17566  
17567
17568 /**
17569  * @class Roo.dd.StatusProxy
17570  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17571  * default drag proxy used by all Roo.dd components.
17572  * @constructor
17573  * @param {Object} config
17574  */
17575 Roo.dd.StatusProxy = function(config){
17576     Roo.apply(this, config);
17577     this.id = this.id || Roo.id();
17578     this.el = new Roo.Layer({
17579         dh: {
17580             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17581                 {tag: "div", cls: "x-dd-drop-icon"},
17582                 {tag: "div", cls: "x-dd-drag-ghost"}
17583             ]
17584         }, 
17585         shadow: !config || config.shadow !== false
17586     });
17587     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17588     this.dropStatus = this.dropNotAllowed;
17589 };
17590
17591 Roo.dd.StatusProxy.prototype = {
17592     /**
17593      * @cfg {String} dropAllowed
17594      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17595      */
17596     dropAllowed : "x-dd-drop-ok",
17597     /**
17598      * @cfg {String} dropNotAllowed
17599      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17600      */
17601     dropNotAllowed : "x-dd-drop-nodrop",
17602
17603     /**
17604      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17605      * over the current target element.
17606      * @param {String} cssClass The css class for the new drop status indicator image
17607      */
17608     setStatus : function(cssClass){
17609         cssClass = cssClass || this.dropNotAllowed;
17610         if(this.dropStatus != cssClass){
17611             this.el.replaceClass(this.dropStatus, cssClass);
17612             this.dropStatus = cssClass;
17613         }
17614     },
17615
17616     /**
17617      * Resets the status indicator to the default dropNotAllowed value
17618      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17619      */
17620     reset : function(clearGhost){
17621         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17622         this.dropStatus = this.dropNotAllowed;
17623         if(clearGhost){
17624             this.ghost.update("");
17625         }
17626     },
17627
17628     /**
17629      * Updates the contents of the ghost element
17630      * @param {String} html The html that will replace the current innerHTML of the ghost element
17631      */
17632     update : function(html){
17633         if(typeof html == "string"){
17634             this.ghost.update(html);
17635         }else{
17636             this.ghost.update("");
17637             html.style.margin = "0";
17638             this.ghost.dom.appendChild(html);
17639         }
17640         // ensure float = none set?? cant remember why though.
17641         var el = this.ghost.dom.firstChild;
17642                 if(el){
17643                         Roo.fly(el).setStyle('float', 'none');
17644                 }
17645     },
17646     
17647     /**
17648      * Returns the underlying proxy {@link Roo.Layer}
17649      * @return {Roo.Layer} el
17650     */
17651     getEl : function(){
17652         return this.el;
17653     },
17654
17655     /**
17656      * Returns the ghost element
17657      * @return {Roo.Element} el
17658      */
17659     getGhost : function(){
17660         return this.ghost;
17661     },
17662
17663     /**
17664      * Hides the proxy
17665      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17666      */
17667     hide : function(clear){
17668         this.el.hide();
17669         if(clear){
17670             this.reset(true);
17671         }
17672     },
17673
17674     /**
17675      * Stops the repair animation if it's currently running
17676      */
17677     stop : function(){
17678         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17679             this.anim.stop();
17680         }
17681     },
17682
17683     /**
17684      * Displays this proxy
17685      */
17686     show : function(){
17687         this.el.show();
17688     },
17689
17690     /**
17691      * Force the Layer to sync its shadow and shim positions to the element
17692      */
17693     sync : function(){
17694         this.el.sync();
17695     },
17696
17697     /**
17698      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17699      * invalid drop operation by the item being dragged.
17700      * @param {Array} xy The XY position of the element ([x, y])
17701      * @param {Function} callback The function to call after the repair is complete
17702      * @param {Object} scope The scope in which to execute the callback
17703      */
17704     repair : function(xy, callback, scope){
17705         this.callback = callback;
17706         this.scope = scope;
17707         if(xy && this.animRepair !== false){
17708             this.el.addClass("x-dd-drag-repair");
17709             this.el.hideUnders(true);
17710             this.anim = this.el.shift({
17711                 duration: this.repairDuration || .5,
17712                 easing: 'easeOut',
17713                 xy: xy,
17714                 stopFx: true,
17715                 callback: this.afterRepair,
17716                 scope: this
17717             });
17718         }else{
17719             this.afterRepair();
17720         }
17721     },
17722
17723     // private
17724     afterRepair : function(){
17725         this.hide(true);
17726         if(typeof this.callback == "function"){
17727             this.callback.call(this.scope || this);
17728         }
17729         this.callback = null;
17730         this.scope = null;
17731     }
17732 };/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.dd.DragSource
17745  * @extends Roo.dd.DDProxy
17746  * A simple class that provides the basic implementation needed to make any element draggable.
17747  * @constructor
17748  * @param {String/HTMLElement/Element} el The container element
17749  * @param {Object} config
17750  */
17751 Roo.dd.DragSource = function(el, config){
17752     this.el = Roo.get(el);
17753     this.dragData = {};
17754     
17755     Roo.apply(this, config);
17756     
17757     if(!this.proxy){
17758         this.proxy = new Roo.dd.StatusProxy();
17759     }
17760
17761     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17762           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17763     
17764     this.dragging = false;
17765 };
17766
17767 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17768     /**
17769      * @cfg {String} dropAllowed
17770      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17771      */
17772     dropAllowed : "x-dd-drop-ok",
17773     /**
17774      * @cfg {String} dropNotAllowed
17775      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17776      */
17777     dropNotAllowed : "x-dd-drop-nodrop",
17778
17779     /**
17780      * Returns the data object associated with this drag source
17781      * @return {Object} data An object containing arbitrary data
17782      */
17783     getDragData : function(e){
17784         return this.dragData;
17785     },
17786
17787     // private
17788     onDragEnter : function(e, id){
17789         var target = Roo.dd.DragDropMgr.getDDById(id);
17790         this.cachedTarget = target;
17791         if(this.beforeDragEnter(target, e, id) !== false){
17792             if(target.isNotifyTarget){
17793                 var status = target.notifyEnter(this, e, this.dragData);
17794                 this.proxy.setStatus(status);
17795             }else{
17796                 this.proxy.setStatus(this.dropAllowed);
17797             }
17798             
17799             if(this.afterDragEnter){
17800                 /**
17801                  * An empty function by default, but provided so that you can perform a custom action
17802                  * when the dragged item enters the drop target by providing an implementation.
17803                  * @param {Roo.dd.DragDrop} target The drop target
17804                  * @param {Event} e The event object
17805                  * @param {String} id The id of the dragged element
17806                  * @method afterDragEnter
17807                  */
17808                 this.afterDragEnter(target, e, id);
17809             }
17810         }
17811     },
17812
17813     /**
17814      * An empty function by default, but provided so that you can perform a custom action
17815      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17816      * @param {Roo.dd.DragDrop} target The drop target
17817      * @param {Event} e The event object
17818      * @param {String} id The id of the dragged element
17819      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17820      */
17821     beforeDragEnter : function(target, e, id){
17822         return true;
17823     },
17824
17825     // private
17826     alignElWithMouse: function() {
17827         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17828         this.proxy.sync();
17829     },
17830
17831     // private
17832     onDragOver : function(e, id){
17833         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17834         if(this.beforeDragOver(target, e, id) !== false){
17835             if(target.isNotifyTarget){
17836                 var status = target.notifyOver(this, e, this.dragData);
17837                 this.proxy.setStatus(status);
17838             }
17839
17840             if(this.afterDragOver){
17841                 /**
17842                  * An empty function by default, but provided so that you can perform a custom action
17843                  * while the dragged item is over the drop target by providing an implementation.
17844                  * @param {Roo.dd.DragDrop} target The drop target
17845                  * @param {Event} e The event object
17846                  * @param {String} id The id of the dragged element
17847                  * @method afterDragOver
17848                  */
17849                 this.afterDragOver(target, e, id);
17850             }
17851         }
17852     },
17853
17854     /**
17855      * An empty function by default, but provided so that you can perform a custom action
17856      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17857      * @param {Roo.dd.DragDrop} target The drop target
17858      * @param {Event} e The event object
17859      * @param {String} id The id of the dragged element
17860      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17861      */
17862     beforeDragOver : function(target, e, id){
17863         return true;
17864     },
17865
17866     // private
17867     onDragOut : function(e, id){
17868         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17869         if(this.beforeDragOut(target, e, id) !== false){
17870             if(target.isNotifyTarget){
17871                 target.notifyOut(this, e, this.dragData);
17872             }
17873             this.proxy.reset();
17874             if(this.afterDragOut){
17875                 /**
17876                  * An empty function by default, but provided so that you can perform a custom action
17877                  * after the dragged item is dragged out of the target without dropping.
17878                  * @param {Roo.dd.DragDrop} target The drop target
17879                  * @param {Event} e The event object
17880                  * @param {String} id The id of the dragged element
17881                  * @method afterDragOut
17882                  */
17883                 this.afterDragOut(target, e, id);
17884             }
17885         }
17886         this.cachedTarget = null;
17887     },
17888
17889     /**
17890      * An empty function by default, but provided so that you can perform a custom action before the dragged
17891      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17892      * @param {Roo.dd.DragDrop} target The drop target
17893      * @param {Event} e The event object
17894      * @param {String} id The id of the dragged element
17895      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17896      */
17897     beforeDragOut : function(target, e, id){
17898         return true;
17899     },
17900     
17901     // private
17902     onDragDrop : function(e, id){
17903         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17904         if(this.beforeDragDrop(target, e, id) !== false){
17905             if(target.isNotifyTarget){
17906                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17907                     this.onValidDrop(target, e, id);
17908                 }else{
17909                     this.onInvalidDrop(target, e, id);
17910                 }
17911             }else{
17912                 this.onValidDrop(target, e, id);
17913             }
17914             
17915             if(this.afterDragDrop){
17916                 /**
17917                  * An empty function by default, but provided so that you can perform a custom action
17918                  * after a valid drag drop has occurred by providing an implementation.
17919                  * @param {Roo.dd.DragDrop} target The drop target
17920                  * @param {Event} e The event object
17921                  * @param {String} id The id of the dropped element
17922                  * @method afterDragDrop
17923                  */
17924                 this.afterDragDrop(target, e, id);
17925             }
17926         }
17927         delete this.cachedTarget;
17928     },
17929
17930     /**
17931      * An empty function by default, but provided so that you can perform a custom action before the dragged
17932      * item is dropped onto the target and optionally cancel the onDragDrop.
17933      * @param {Roo.dd.DragDrop} target The drop target
17934      * @param {Event} e The event object
17935      * @param {String} id The id of the dragged element
17936      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17937      */
17938     beforeDragDrop : function(target, e, id){
17939         return true;
17940     },
17941
17942     // private
17943     onValidDrop : function(target, e, id){
17944         this.hideProxy();
17945         if(this.afterValidDrop){
17946             /**
17947              * An empty function by default, but provided so that you can perform a custom action
17948              * after a valid drop has occurred by providing an implementation.
17949              * @param {Object} target The target DD 
17950              * @param {Event} e The event object
17951              * @param {String} id The id of the dropped element
17952              * @method afterInvalidDrop
17953              */
17954             this.afterValidDrop(target, e, id);
17955         }
17956     },
17957
17958     // private
17959     getRepairXY : function(e, data){
17960         return this.el.getXY();  
17961     },
17962
17963     // private
17964     onInvalidDrop : function(target, e, id){
17965         this.beforeInvalidDrop(target, e, id);
17966         if(this.cachedTarget){
17967             if(this.cachedTarget.isNotifyTarget){
17968                 this.cachedTarget.notifyOut(this, e, this.dragData);
17969             }
17970             this.cacheTarget = null;
17971         }
17972         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
17973
17974         if(this.afterInvalidDrop){
17975             /**
17976              * An empty function by default, but provided so that you can perform a custom action
17977              * after an invalid drop has occurred by providing an implementation.
17978              * @param {Event} e The event object
17979              * @param {String} id The id of the dropped element
17980              * @method afterInvalidDrop
17981              */
17982             this.afterInvalidDrop(e, id);
17983         }
17984     },
17985
17986     // private
17987     afterRepair : function(){
17988         if(Roo.enableFx){
17989             this.el.highlight(this.hlColor || "c3daf9");
17990         }
17991         this.dragging = false;
17992     },
17993
17994     /**
17995      * An empty function by default, but provided so that you can perform a custom action after an invalid
17996      * drop has occurred.
17997      * @param {Roo.dd.DragDrop} target The drop target
17998      * @param {Event} e The event object
17999      * @param {String} id The id of the dragged element
18000      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18001      */
18002     beforeInvalidDrop : function(target, e, id){
18003         return true;
18004     },
18005
18006     // private
18007     handleMouseDown : function(e){
18008         if(this.dragging) {
18009             return;
18010         }
18011         var data = this.getDragData(e);
18012         if(data && this.onBeforeDrag(data, e) !== false){
18013             this.dragData = data;
18014             this.proxy.stop();
18015             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18016         } 
18017     },
18018
18019     /**
18020      * An empty function by default, but provided so that you can perform a custom action before the initial
18021      * drag event begins and optionally cancel it.
18022      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18023      * @param {Event} e The event object
18024      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18025      */
18026     onBeforeDrag : function(data, e){
18027         return true;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action once the initial
18032      * drag event has begun.  The drag cannot be canceled from this function.
18033      * @param {Number} x The x position of the click on the dragged object
18034      * @param {Number} y The y position of the click on the dragged object
18035      */
18036     onStartDrag : Roo.emptyFn,
18037
18038     // private - YUI override
18039     startDrag : function(x, y){
18040         this.proxy.reset();
18041         this.dragging = true;
18042         this.proxy.update("");
18043         this.onInitDrag(x, y);
18044         this.proxy.show();
18045     },
18046
18047     // private
18048     onInitDrag : function(x, y){
18049         var clone = this.el.dom.cloneNode(true);
18050         clone.id = Roo.id(); // prevent duplicate ids
18051         this.proxy.update(clone);
18052         this.onStartDrag(x, y);
18053         return true;
18054     },
18055
18056     /**
18057      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18058      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18059      */
18060     getProxy : function(){
18061         return this.proxy;  
18062     },
18063
18064     /**
18065      * Hides the drag source's {@link Roo.dd.StatusProxy}
18066      */
18067     hideProxy : function(){
18068         this.proxy.hide();  
18069         this.proxy.reset(true);
18070         this.dragging = false;
18071     },
18072
18073     // private
18074     triggerCacheRefresh : function(){
18075         Roo.dd.DDM.refreshCache(this.groups);
18076     },
18077
18078     // private - override to prevent hiding
18079     b4EndDrag: function(e) {
18080     },
18081
18082     // private - override to prevent moving
18083     endDrag : function(e){
18084         this.onEndDrag(this.dragData, e);
18085     },
18086
18087     // private
18088     onEndDrag : function(data, e){
18089     },
18090     
18091     // private - pin to cursor
18092     autoOffset : function(x, y) {
18093         this.setDelta(-12, -20);
18094     }    
18095 });/*
18096  * Based on:
18097  * Ext JS Library 1.1.1
18098  * Copyright(c) 2006-2007, Ext JS, LLC.
18099  *
18100  * Originally Released Under LGPL - original licence link has changed is not relivant.
18101  *
18102  * Fork - LGPL
18103  * <script type="text/javascript">
18104  */
18105
18106
18107 /**
18108  * @class Roo.dd.DropTarget
18109  * @extends Roo.dd.DDTarget
18110  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18111  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18112  * @constructor
18113  * @param {String/HTMLElement/Element} el The container element
18114  * @param {Object} config
18115  */
18116 Roo.dd.DropTarget = function(el, config){
18117     this.el = Roo.get(el);
18118     
18119     Roo.apply(this, config);
18120     
18121     if(this.containerScroll){
18122         Roo.dd.ScrollManager.register(this.el);
18123     }
18124     
18125     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18126           {isTarget: true});
18127
18128 };
18129
18130 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18131     /**
18132      * @cfg {String} overClass
18133      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18134      */
18135     /**
18136      * @cfg {String} dropAllowed
18137      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18138      */
18139     dropAllowed : "x-dd-drop-ok",
18140     /**
18141      * @cfg {String} dropNotAllowed
18142      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18143      */
18144     dropNotAllowed : "x-dd-drop-nodrop",
18145
18146     // private
18147     isTarget : true,
18148
18149     // private
18150     isNotifyTarget : true,
18151
18152     /**
18153      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18154      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18155      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18157      * @param {Event} e The event
18158      * @param {Object} data An object containing arbitrary data supplied by the drag source
18159      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18160      * underlying {@link Roo.dd.StatusProxy} can be updated
18161      */
18162     notifyEnter : function(dd, e, data){
18163         if(this.overClass){
18164             this.el.addClass(this.overClass);
18165         }
18166         return this.dropAllowed;
18167     },
18168
18169     /**
18170      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18171      * This method will be called on every mouse movement while the drag source is over the drop target.
18172      * This default implementation simply returns the dropAllowed config value.
18173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18174      * @param {Event} e The event
18175      * @param {Object} data An object containing arbitrary data supplied by the drag source
18176      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18177      * underlying {@link Roo.dd.StatusProxy} can be updated
18178      */
18179     notifyOver : function(dd, e, data){
18180         return this.dropAllowed;
18181     },
18182
18183     /**
18184      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18185      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18186      * overClass (if any) from the drop element.
18187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18188      * @param {Event} e The event
18189      * @param {Object} data An object containing arbitrary data supplied by the drag source
18190      */
18191     notifyOut : function(dd, e, data){
18192         if(this.overClass){
18193             this.el.removeClass(this.overClass);
18194         }
18195     },
18196
18197     /**
18198      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18199      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18200      * implementation that does something to process the drop event and returns true so that the drag source's
18201      * repair action does not run.
18202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18203      * @param {Event} e The event
18204      * @param {Object} data An object containing arbitrary data supplied by the drag source
18205      * @return {Boolean} True if the drop was valid, else false
18206      */
18207     notifyDrop : function(dd, e, data){
18208         return false;
18209     }
18210 });/*
18211  * Based on:
18212  * Ext JS Library 1.1.1
18213  * Copyright(c) 2006-2007, Ext JS, LLC.
18214  *
18215  * Originally Released Under LGPL - original licence link has changed is not relivant.
18216  *
18217  * Fork - LGPL
18218  * <script type="text/javascript">
18219  */
18220
18221
18222 /**
18223  * @class Roo.dd.DragZone
18224  * @extends Roo.dd.DragSource
18225  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18226  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18227  * @constructor
18228  * @param {String/HTMLElement/Element} el The container element
18229  * @param {Object} config
18230  */
18231 Roo.dd.DragZone = function(el, config){
18232     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18233     if(this.containerScroll){
18234         Roo.dd.ScrollManager.register(this.el);
18235     }
18236 };
18237
18238 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18239     /**
18240      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18241      * for auto scrolling during drag operations.
18242      */
18243     /**
18244      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18245      * method after a failed drop (defaults to "c3daf9" - light blue)
18246      */
18247
18248     /**
18249      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18250      * for a valid target to drag based on the mouse down. Override this method
18251      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18252      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18253      * @param {EventObject} e The mouse down event
18254      * @return {Object} The dragData
18255      */
18256     getDragData : function(e){
18257         return Roo.dd.Registry.getHandleFromEvent(e);
18258     },
18259     
18260     /**
18261      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18262      * this.dragData.ddel
18263      * @param {Number} x The x position of the click on the dragged object
18264      * @param {Number} y The y position of the click on the dragged object
18265      * @return {Boolean} true to continue the drag, false to cancel
18266      */
18267     onInitDrag : function(x, y){
18268         this.proxy.update(this.dragData.ddel.cloneNode(true));
18269         this.onStartDrag(x, y);
18270         return true;
18271     },
18272     
18273     /**
18274      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18275      */
18276     afterRepair : function(){
18277         if(Roo.enableFx){
18278             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18279         }
18280         this.dragging = false;
18281     },
18282
18283     /**
18284      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18285      * the XY of this.dragData.ddel
18286      * @param {EventObject} e The mouse up event
18287      * @return {Array} The xy location (e.g. [100, 200])
18288      */
18289     getRepairXY : function(e){
18290         return Roo.Element.fly(this.dragData.ddel).getXY();  
18291     }
18292 });/*
18293  * Based on:
18294  * Ext JS Library 1.1.1
18295  * Copyright(c) 2006-2007, Ext JS, LLC.
18296  *
18297  * Originally Released Under LGPL - original licence link has changed is not relivant.
18298  *
18299  * Fork - LGPL
18300  * <script type="text/javascript">
18301  */
18302 /**
18303  * @class Roo.dd.DropZone
18304  * @extends Roo.dd.DropTarget
18305  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18306  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18307  * @constructor
18308  * @param {String/HTMLElement/Element} el The container element
18309  * @param {Object} config
18310  */
18311 Roo.dd.DropZone = function(el, config){
18312     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18313 };
18314
18315 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18316     /**
18317      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18318      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18319      * provide your own custom lookup.
18320      * @param {Event} e The event
18321      * @return {Object} data The custom data
18322      */
18323     getTargetFromEvent : function(e){
18324         return Roo.dd.Registry.getTargetFromEvent(e);
18325     },
18326
18327     /**
18328      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18329      * that it has registered.  This method has no default implementation and should be overridden to provide
18330      * node-specific processing if necessary.
18331      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18332      * {@link #getTargetFromEvent} for this node)
18333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18334      * @param {Event} e The event
18335      * @param {Object} data An object containing arbitrary data supplied by the drag source
18336      */
18337     onNodeEnter : function(n, dd, e, data){
18338         
18339     },
18340
18341     /**
18342      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18343      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18344      * overridden to provide the proper feedback.
18345      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18346      * {@link #getTargetFromEvent} for this node)
18347      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18348      * @param {Event} e The event
18349      * @param {Object} data An object containing arbitrary data supplied by the drag source
18350      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18351      * underlying {@link Roo.dd.StatusProxy} can be updated
18352      */
18353     onNodeOver : function(n, dd, e, data){
18354         return this.dropAllowed;
18355     },
18356
18357     /**
18358      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18359      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18360      * node-specific processing if necessary.
18361      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18362      * {@link #getTargetFromEvent} for this node)
18363      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18364      * @param {Event} e The event
18365      * @param {Object} data An object containing arbitrary data supplied by the drag source
18366      */
18367     onNodeOut : function(n, dd, e, data){
18368         
18369     },
18370
18371     /**
18372      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18373      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18374      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18375      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18376      * {@link #getTargetFromEvent} for this node)
18377      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18378      * @param {Event} e The event
18379      * @param {Object} data An object containing arbitrary data supplied by the drag source
18380      * @return {Boolean} True if the drop was valid, else false
18381      */
18382     onNodeDrop : function(n, dd, e, data){
18383         return false;
18384     },
18385
18386     /**
18387      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18388      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18389      * it should be overridden to provide the proper feedback if necessary.
18390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18391      * @param {Event} e The event
18392      * @param {Object} data An object containing arbitrary data supplied by the drag source
18393      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18394      * underlying {@link Roo.dd.StatusProxy} can be updated
18395      */
18396     onContainerOver : function(dd, e, data){
18397         return this.dropNotAllowed;
18398     },
18399
18400     /**
18401      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18402      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18403      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18404      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18405      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18406      * @param {Event} e The event
18407      * @param {Object} data An object containing arbitrary data supplied by the drag source
18408      * @return {Boolean} True if the drop was valid, else false
18409      */
18410     onContainerDrop : function(dd, e, data){
18411         return false;
18412     },
18413
18414     /**
18415      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18416      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18417      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18418      * you should override this method and provide a custom implementation.
18419      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18420      * @param {Event} e The event
18421      * @param {Object} data An object containing arbitrary data supplied by the drag source
18422      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18423      * underlying {@link Roo.dd.StatusProxy} can be updated
18424      */
18425     notifyEnter : function(dd, e, data){
18426         return this.dropNotAllowed;
18427     },
18428
18429     /**
18430      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18431      * This method will be called on every mouse movement while the drag source is over the drop zone.
18432      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18433      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18434      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18435      * registered node, it will call {@link #onContainerOver}.
18436      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18437      * @param {Event} e The event
18438      * @param {Object} data An object containing arbitrary data supplied by the drag source
18439      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18440      * underlying {@link Roo.dd.StatusProxy} can be updated
18441      */
18442     notifyOver : function(dd, e, data){
18443         var n = this.getTargetFromEvent(e);
18444         if(!n){ // not over valid drop target
18445             if(this.lastOverNode){
18446                 this.onNodeOut(this.lastOverNode, dd, e, data);
18447                 this.lastOverNode = null;
18448             }
18449             return this.onContainerOver(dd, e, data);
18450         }
18451         if(this.lastOverNode != n){
18452             if(this.lastOverNode){
18453                 this.onNodeOut(this.lastOverNode, dd, e, data);
18454             }
18455             this.onNodeEnter(n, dd, e, data);
18456             this.lastOverNode = n;
18457         }
18458         return this.onNodeOver(n, dd, e, data);
18459     },
18460
18461     /**
18462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18463      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18464      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18465      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18466      * @param {Event} e The event
18467      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18468      */
18469     notifyOut : function(dd, e, data){
18470         if(this.lastOverNode){
18471             this.onNodeOut(this.lastOverNode, dd, e, data);
18472             this.lastOverNode = null;
18473         }
18474     },
18475
18476     /**
18477      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18478      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18479      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18480      * otherwise it will call {@link #onContainerDrop}.
18481      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18482      * @param {Event} e The event
18483      * @param {Object} data An object containing arbitrary data supplied by the drag source
18484      * @return {Boolean} True if the drop was valid, else false
18485      */
18486     notifyDrop : function(dd, e, data){
18487         if(this.lastOverNode){
18488             this.onNodeOut(this.lastOverNode, dd, e, data);
18489             this.lastOverNode = null;
18490         }
18491         var n = this.getTargetFromEvent(e);
18492         return n ?
18493             this.onNodeDrop(n, dd, e, data) :
18494             this.onContainerDrop(dd, e, data);
18495     },
18496
18497     // private
18498     triggerCacheRefresh : function(){
18499         Roo.dd.DDM.refreshCache(this.groups);
18500     }  
18501 });/*
18502  * Based on:
18503  * Ext JS Library 1.1.1
18504  * Copyright(c) 2006-2007, Ext JS, LLC.
18505  *
18506  * Originally Released Under LGPL - original licence link has changed is not relivant.
18507  *
18508  * Fork - LGPL
18509  * <script type="text/javascript">
18510  */
18511
18512
18513 /**
18514  * @class Roo.data.SortTypes
18515  * @singleton
18516  * Defines the default sorting (casting?) comparison functions used when sorting data.
18517  */
18518 Roo.data.SortTypes = {
18519     /**
18520      * Default sort that does nothing
18521      * @param {Mixed} s The value being converted
18522      * @return {Mixed} The comparison value
18523      */
18524     none : function(s){
18525         return s;
18526     },
18527     
18528     /**
18529      * The regular expression used to strip tags
18530      * @type {RegExp}
18531      * @property
18532      */
18533     stripTagsRE : /<\/?[^>]+>/gi,
18534     
18535     /**
18536      * Strips all HTML tags to sort on text only
18537      * @param {Mixed} s The value being converted
18538      * @return {String} The comparison value
18539      */
18540     asText : function(s){
18541         return String(s).replace(this.stripTagsRE, "");
18542     },
18543     
18544     /**
18545      * Strips all HTML tags to sort on text only - Case insensitive
18546      * @param {Mixed} s The value being converted
18547      * @return {String} The comparison value
18548      */
18549     asUCText : function(s){
18550         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18551     },
18552     
18553     /**
18554      * Case insensitive string
18555      * @param {Mixed} s The value being converted
18556      * @return {String} The comparison value
18557      */
18558     asUCString : function(s) {
18559         return String(s).toUpperCase();
18560     },
18561     
18562     /**
18563      * Date sorting
18564      * @param {Mixed} s The value being converted
18565      * @return {Number} The comparison value
18566      */
18567     asDate : function(s) {
18568         if(!s){
18569             return 0;
18570         }
18571         if(s instanceof Date){
18572             return s.getTime();
18573         }
18574         return Date.parse(String(s));
18575     },
18576     
18577     /**
18578      * Float sorting
18579      * @param {Mixed} s The value being converted
18580      * @return {Float} The comparison value
18581      */
18582     asFloat : function(s) {
18583         var val = parseFloat(String(s).replace(/,/g, ""));
18584         if(isNaN(val)) val = 0;
18585         return val;
18586     },
18587     
18588     /**
18589      * Integer sorting
18590      * @param {Mixed} s The value being converted
18591      * @return {Number} The comparison value
18592      */
18593     asInt : function(s) {
18594         var val = parseInt(String(s).replace(/,/g, ""));
18595         if(isNaN(val)) val = 0;
18596         return val;
18597     }
18598 };/*
18599  * Based on:
18600  * Ext JS Library 1.1.1
18601  * Copyright(c) 2006-2007, Ext JS, LLC.
18602  *
18603  * Originally Released Under LGPL - original licence link has changed is not relivant.
18604  *
18605  * Fork - LGPL
18606  * <script type="text/javascript">
18607  */
18608
18609 /**
18610 * @class Roo.data.Record
18611  * Instances of this class encapsulate both record <em>definition</em> information, and record
18612  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18613  * to access Records cached in an {@link Roo.data.Store} object.<br>
18614  * <p>
18615  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18616  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18617  * objects.<br>
18618  * <p>
18619  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18620  * @constructor
18621  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18622  * {@link #create}. The parameters are the same.
18623  * @param {Array} data An associative Array of data values keyed by the field name.
18624  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18625  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18626  * not specified an integer id is generated.
18627  */
18628 Roo.data.Record = function(data, id){
18629     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18630     this.data = data;
18631 };
18632
18633 /**
18634  * Generate a constructor for a specific record layout.
18635  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18636  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18637  * Each field definition object may contain the following properties: <ul>
18638  * <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,
18639  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18640  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18641  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18642  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18643  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18644  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18645  * this may be omitted.</p></li>
18646  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18647  * <ul><li>auto (Default, implies no conversion)</li>
18648  * <li>string</li>
18649  * <li>int</li>
18650  * <li>float</li>
18651  * <li>boolean</li>
18652  * <li>date</li></ul></p></li>
18653  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18654  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18655  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18656  * by the Reader into an object that will be stored in the Record. It is passed the
18657  * following parameters:<ul>
18658  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18659  * </ul></p></li>
18660  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18661  * </ul>
18662  * <br>usage:<br><pre><code>
18663 var TopicRecord = Roo.data.Record.create(
18664     {name: 'title', mapping: 'topic_title'},
18665     {name: 'author', mapping: 'username'},
18666     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18667     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18668     {name: 'lastPoster', mapping: 'user2'},
18669     {name: 'excerpt', mapping: 'post_text'}
18670 );
18671
18672 var myNewRecord = new TopicRecord({
18673     title: 'Do my job please',
18674     author: 'noobie',
18675     totalPosts: 1,
18676     lastPost: new Date(),
18677     lastPoster: 'Animal',
18678     excerpt: 'No way dude!'
18679 });
18680 myStore.add(myNewRecord);
18681 </code></pre>
18682  * @method create
18683  * @static
18684  */
18685 Roo.data.Record.create = function(o){
18686     var f = function(){
18687         f.superclass.constructor.apply(this, arguments);
18688     };
18689     Roo.extend(f, Roo.data.Record);
18690     var p = f.prototype;
18691     p.fields = new Roo.util.MixedCollection(false, function(field){
18692         return field.name;
18693     });
18694     for(var i = 0, len = o.length; i < len; i++){
18695         p.fields.add(new Roo.data.Field(o[i]));
18696     }
18697     f.getField = function(name){
18698         return p.fields.get(name);  
18699     };
18700     return f;
18701 };
18702
18703 Roo.data.Record.AUTO_ID = 1000;
18704 Roo.data.Record.EDIT = 'edit';
18705 Roo.data.Record.REJECT = 'reject';
18706 Roo.data.Record.COMMIT = 'commit';
18707
18708 Roo.data.Record.prototype = {
18709     /**
18710      * Readonly flag - true if this record has been modified.
18711      * @type Boolean
18712      */
18713     dirty : false,
18714     editing : false,
18715     error: null,
18716     modified: null,
18717
18718     // private
18719     join : function(store){
18720         this.store = store;
18721     },
18722
18723     /**
18724      * Set the named field to the specified value.
18725      * @param {String} name The name of the field to set.
18726      * @param {Object} value The value to set the field to.
18727      */
18728     set : function(name, value){
18729         if(this.data[name] == value){
18730             return;
18731         }
18732         this.dirty = true;
18733         if(!this.modified){
18734             this.modified = {};
18735         }
18736         if(typeof this.modified[name] == 'undefined'){
18737             this.modified[name] = this.data[name];
18738         }
18739         this.data[name] = value;
18740         if(!this.editing){
18741             this.store.afterEdit(this);
18742         }       
18743     },
18744
18745     /**
18746      * Get the value of the named field.
18747      * @param {String} name The name of the field to get the value of.
18748      * @return {Object} The value of the field.
18749      */
18750     get : function(name){
18751         return this.data[name]; 
18752     },
18753
18754     // private
18755     beginEdit : function(){
18756         this.editing = true;
18757         this.modified = {}; 
18758     },
18759
18760     // private
18761     cancelEdit : function(){
18762         this.editing = false;
18763         delete this.modified;
18764     },
18765
18766     // private
18767     endEdit : function(){
18768         this.editing = false;
18769         if(this.dirty && this.store){
18770             this.store.afterEdit(this);
18771         }
18772     },
18773
18774     /**
18775      * Usually called by the {@link Roo.data.Store} which owns the Record.
18776      * Rejects all changes made to the Record since either creation, or the last commit operation.
18777      * Modified fields are reverted to their original values.
18778      * <p>
18779      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18780      * of reject operations.
18781      */
18782     reject : function(){
18783         var m = this.modified;
18784         for(var n in m){
18785             if(typeof m[n] != "function"){
18786                 this.data[n] = m[n];
18787             }
18788         }
18789         this.dirty = false;
18790         delete this.modified;
18791         this.editing = false;
18792         if(this.store){
18793             this.store.afterReject(this);
18794         }
18795     },
18796
18797     /**
18798      * Usually called by the {@link Roo.data.Store} which owns the Record.
18799      * Commits all changes made to the Record since either creation, or the last commit operation.
18800      * <p>
18801      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18802      * of commit operations.
18803      */
18804     commit : function(){
18805         this.dirty = false;
18806         delete this.modified;
18807         this.editing = false;
18808         if(this.store){
18809             this.store.afterCommit(this);
18810         }
18811     },
18812
18813     // private
18814     hasError : function(){
18815         return this.error != null;
18816     },
18817
18818     // private
18819     clearError : function(){
18820         this.error = null;
18821     },
18822
18823     /**
18824      * Creates a copy of this record.
18825      * @param {String} id (optional) A new record id if you don't want to use this record's id
18826      * @return {Record}
18827      */
18828     copy : function(newId) {
18829         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18830     }
18831 };/*
18832  * Based on:
18833  * Ext JS Library 1.1.1
18834  * Copyright(c) 2006-2007, Ext JS, LLC.
18835  *
18836  * Originally Released Under LGPL - original licence link has changed is not relivant.
18837  *
18838  * Fork - LGPL
18839  * <script type="text/javascript">
18840  */
18841
18842
18843
18844 /**
18845  * @class Roo.data.Store
18846  * @extends Roo.util.Observable
18847  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18848  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18849  * <p>
18850  * 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
18851  * has no knowledge of the format of the data returned by the Proxy.<br>
18852  * <p>
18853  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18854  * instances from the data object. These records are cached and made available through accessor functions.
18855  * @constructor
18856  * Creates a new Store.
18857  * @param {Object} config A config object containing the objects needed for the Store to access data,
18858  * and read the data into Records.
18859  */
18860 Roo.data.Store = function(config){
18861     this.data = new Roo.util.MixedCollection(false);
18862     this.data.getKey = function(o){
18863         return o.id;
18864     };
18865     this.baseParams = {};
18866     // private
18867     this.paramNames = {
18868         "start" : "start",
18869         "limit" : "limit",
18870         "sort" : "sort",
18871         "dir" : "dir"
18872     };
18873
18874     if(config && config.data){
18875         this.inlineData = config.data;
18876         delete config.data;
18877     }
18878
18879     Roo.apply(this, config);
18880     
18881     if(this.reader){ // reader passed
18882         this.reader = Roo.factory(this.reader, Roo.data);
18883         this.reader.xmodule = this.xmodule || false;
18884         if(!this.recordType){
18885             this.recordType = this.reader.recordType;
18886         }
18887         if(this.reader.onMetaChange){
18888             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18889         }
18890     }
18891
18892     if(this.recordType){
18893         this.fields = this.recordType.prototype.fields;
18894     }
18895     this.modified = [];
18896
18897     this.addEvents({
18898         /**
18899          * @event datachanged
18900          * Fires when the data cache has changed, and a widget which is using this Store
18901          * as a Record cache should refresh its view.
18902          * @param {Store} this
18903          */
18904         datachanged : true,
18905         /**
18906          * @event metachange
18907          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18908          * @param {Store} this
18909          * @param {Object} meta The JSON metadata
18910          */
18911         metachange : true,
18912         /**
18913          * @event add
18914          * Fires when Records have been added to the Store
18915          * @param {Store} this
18916          * @param {Roo.data.Record[]} records The array of Records added
18917          * @param {Number} index The index at which the record(s) were added
18918          */
18919         add : true,
18920         /**
18921          * @event remove
18922          * Fires when a Record has been removed from the Store
18923          * @param {Store} this
18924          * @param {Roo.data.Record} record The Record that was removed
18925          * @param {Number} index The index at which the record was removed
18926          */
18927         remove : true,
18928         /**
18929          * @event update
18930          * Fires when a Record has been updated
18931          * @param {Store} this
18932          * @param {Roo.data.Record} record The Record that was updated
18933          * @param {String} operation The update operation being performed.  Value may be one of:
18934          * <pre><code>
18935  Roo.data.Record.EDIT
18936  Roo.data.Record.REJECT
18937  Roo.data.Record.COMMIT
18938          * </code></pre>
18939          */
18940         update : true,
18941         /**
18942          * @event clear
18943          * Fires when the data cache has been cleared.
18944          * @param {Store} this
18945          */
18946         clear : true,
18947         /**
18948          * @event beforeload
18949          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18950          * the load action will be canceled.
18951          * @param {Store} this
18952          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18953          */
18954         beforeload : true,
18955         /**
18956          * @event load
18957          * Fires after a new set of Records has been loaded.
18958          * @param {Store} this
18959          * @param {Roo.data.Record[]} records The Records that were loaded
18960          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18961          */
18962         load : true,
18963         /**
18964          * @event loadexception
18965          * Fires if an exception occurs in the Proxy during loading.
18966          * Called with the signature of the Proxy's "loadexception" event.
18967          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
18968          * 
18969          * @param {Proxy} 
18970          * @param {Object} return from JsonData.reader() - success, totalRecords, records
18971          * @param {Object} load options 
18972          * @param {Object} jsonData from your request (normally this contains the Exception)
18973          */
18974         loadexception : true
18975     });
18976     
18977     if(this.proxy){
18978         this.proxy = Roo.factory(this.proxy, Roo.data);
18979         this.proxy.xmodule = this.xmodule || false;
18980         this.relayEvents(this.proxy,  ["loadexception"]);
18981     }
18982     this.sortToggle = {};
18983
18984     Roo.data.Store.superclass.constructor.call(this);
18985
18986     if(this.inlineData){
18987         this.loadData(this.inlineData);
18988         delete this.inlineData;
18989     }
18990 };
18991 Roo.extend(Roo.data.Store, Roo.util.Observable, {
18992      /**
18993     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
18994     * without a remote query - used by combo/forms at present.
18995     */
18996     
18997     /**
18998     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
18999     */
19000     /**
19001     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19002     */
19003     /**
19004     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19005     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19006     */
19007     /**
19008     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19009     * on any HTTP request
19010     */
19011     /**
19012     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19013     */
19014     /**
19015     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19016     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19017     */
19018     remoteSort : false,
19019
19020     /**
19021     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19022      * loaded or when a record is removed. (defaults to false).
19023     */
19024     pruneModifiedRecords : false,
19025
19026     // private
19027     lastOptions : null,
19028
19029     /**
19030      * Add Records to the Store and fires the add event.
19031      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19032      */
19033     add : function(records){
19034         records = [].concat(records);
19035         for(var i = 0, len = records.length; i < len; i++){
19036             records[i].join(this);
19037         }
19038         var index = this.data.length;
19039         this.data.addAll(records);
19040         this.fireEvent("add", this, records, index);
19041     },
19042
19043     /**
19044      * Remove a Record from the Store and fires the remove event.
19045      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19046      */
19047     remove : function(record){
19048         var index = this.data.indexOf(record);
19049         this.data.removeAt(index);
19050         if(this.pruneModifiedRecords){
19051             this.modified.remove(record);
19052         }
19053         this.fireEvent("remove", this, record, index);
19054     },
19055
19056     /**
19057      * Remove all Records from the Store and fires the clear event.
19058      */
19059     removeAll : function(){
19060         this.data.clear();
19061         if(this.pruneModifiedRecords){
19062             this.modified = [];
19063         }
19064         this.fireEvent("clear", this);
19065     },
19066
19067     /**
19068      * Inserts Records to the Store at the given index and fires the add event.
19069      * @param {Number} index The start index at which to insert the passed Records.
19070      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19071      */
19072     insert : function(index, records){
19073         records = [].concat(records);
19074         for(var i = 0, len = records.length; i < len; i++){
19075             this.data.insert(index, records[i]);
19076             records[i].join(this);
19077         }
19078         this.fireEvent("add", this, records, index);
19079     },
19080
19081     /**
19082      * Get the index within the cache of the passed Record.
19083      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19084      * @return {Number} The index of the passed Record. Returns -1 if not found.
19085      */
19086     indexOf : function(record){
19087         return this.data.indexOf(record);
19088     },
19089
19090     /**
19091      * Get the index within the cache of the Record with the passed id.
19092      * @param {String} id The id of the Record to find.
19093      * @return {Number} The index of the Record. Returns -1 if not found.
19094      */
19095     indexOfId : function(id){
19096         return this.data.indexOfKey(id);
19097     },
19098
19099     /**
19100      * Get the Record with the specified id.
19101      * @param {String} id The id of the Record to find.
19102      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19103      */
19104     getById : function(id){
19105         return this.data.key(id);
19106     },
19107
19108     /**
19109      * Get the Record at the specified index.
19110      * @param {Number} index The index of the Record to find.
19111      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19112      */
19113     getAt : function(index){
19114         return this.data.itemAt(index);
19115     },
19116
19117     /**
19118      * Returns a range of Records between specified indices.
19119      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19120      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19121      * @return {Roo.data.Record[]} An array of Records
19122      */
19123     getRange : function(start, end){
19124         return this.data.getRange(start, end);
19125     },
19126
19127     // private
19128     storeOptions : function(o){
19129         o = Roo.apply({}, o);
19130         delete o.callback;
19131         delete o.scope;
19132         this.lastOptions = o;
19133     },
19134
19135     /**
19136      * Loads the Record cache from the configured Proxy using the configured Reader.
19137      * <p>
19138      * If using remote paging, then the first load call must specify the <em>start</em>
19139      * and <em>limit</em> properties in the options.params property to establish the initial
19140      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19141      * <p>
19142      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19143      * and this call will return before the new data has been loaded. Perform any post-processing
19144      * in a callback function, or in a "load" event handler.</strong>
19145      * <p>
19146      * @param {Object} options An object containing properties which control loading options:<ul>
19147      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19148      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19149      * passed the following arguments:<ul>
19150      * <li>r : Roo.data.Record[]</li>
19151      * <li>options: Options object from the load call</li>
19152      * <li>success: Boolean success indicator</li></ul></li>
19153      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19154      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19155      * </ul>
19156      */
19157     load : function(options){
19158         options = options || {};
19159         if(this.fireEvent("beforeload", this, options) !== false){
19160             this.storeOptions(options);
19161             var p = Roo.apply(options.params || {}, this.baseParams);
19162             if(this.sortInfo && this.remoteSort){
19163                 var pn = this.paramNames;
19164                 p[pn["sort"]] = this.sortInfo.field;
19165                 p[pn["dir"]] = this.sortInfo.direction;
19166             }
19167             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19168         }
19169     },
19170
19171     /**
19172      * Reloads the Record cache from the configured Proxy using the configured Reader and
19173      * the options from the last load operation performed.
19174      * @param {Object} options (optional) An object containing properties which may override the options
19175      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19176      * the most recently used options are reused).
19177      */
19178     reload : function(options){
19179         this.load(Roo.applyIf(options||{}, this.lastOptions));
19180     },
19181
19182     // private
19183     // Called as a callback by the Reader during a load operation.
19184     loadRecords : function(o, options, success){
19185         if(!o || success === false){
19186             if(success !== false){
19187                 this.fireEvent("load", this, [], options);
19188             }
19189             if(options.callback){
19190                 options.callback.call(options.scope || this, [], options, false);
19191             }
19192             return;
19193         }
19194         // if data returned failure - throw an exception.
19195         if (o.success === false) {
19196             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19197             return;
19198         }
19199         var r = o.records, t = o.totalRecords || r.length;
19200         if(!options || options.add !== true){
19201             if(this.pruneModifiedRecords){
19202                 this.modified = [];
19203             }
19204             for(var i = 0, len = r.length; i < len; i++){
19205                 r[i].join(this);
19206             }
19207             if(this.snapshot){
19208                 this.data = this.snapshot;
19209                 delete this.snapshot;
19210             }
19211             this.data.clear();
19212             this.data.addAll(r);
19213             this.totalLength = t;
19214             this.applySort();
19215             this.fireEvent("datachanged", this);
19216         }else{
19217             this.totalLength = Math.max(t, this.data.length+r.length);
19218             this.add(r);
19219         }
19220         this.fireEvent("load", this, r, options);
19221         if(options.callback){
19222             options.callback.call(options.scope || this, r, options, true);
19223         }
19224     },
19225
19226     /**
19227      * Loads data from a passed data block. A Reader which understands the format of the data
19228      * must have been configured in the constructor.
19229      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19230      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19231      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19232      */
19233     loadData : function(o, append){
19234         var r = this.reader.readRecords(o);
19235         this.loadRecords(r, {add: append}, true);
19236     },
19237
19238     /**
19239      * Gets the number of cached records.
19240      * <p>
19241      * <em>If using paging, this may not be the total size of the dataset. If the data object
19242      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19243      * the data set size</em>
19244      */
19245     getCount : function(){
19246         return this.data.length || 0;
19247     },
19248
19249     /**
19250      * Gets the total number of records in the dataset as returned by the server.
19251      * <p>
19252      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19253      * the dataset size</em>
19254      */
19255     getTotalCount : function(){
19256         return this.totalLength || 0;
19257     },
19258
19259     /**
19260      * Returns the sort state of the Store as an object with two properties:
19261      * <pre><code>
19262  field {String} The name of the field by which the Records are sorted
19263  direction {String} The sort order, "ASC" or "DESC"
19264      * </code></pre>
19265      */
19266     getSortState : function(){
19267         return this.sortInfo;
19268     },
19269
19270     // private
19271     applySort : function(){
19272         if(this.sortInfo && !this.remoteSort){
19273             var s = this.sortInfo, f = s.field;
19274             var st = this.fields.get(f).sortType;
19275             var fn = function(r1, r2){
19276                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19277                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19278             };
19279             this.data.sort(s.direction, fn);
19280             if(this.snapshot && this.snapshot != this.data){
19281                 this.snapshot.sort(s.direction, fn);
19282             }
19283         }
19284     },
19285
19286     /**
19287      * Sets the default sort column and order to be used by the next load operation.
19288      * @param {String} fieldName The name of the field to sort by.
19289      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19290      */
19291     setDefaultSort : function(field, dir){
19292         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19293     },
19294
19295     /**
19296      * Sort the Records.
19297      * If remote sorting is used, the sort is performed on the server, and the cache is
19298      * reloaded. If local sorting is used, the cache is sorted internally.
19299      * @param {String} fieldName The name of the field to sort by.
19300      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19301      */
19302     sort : function(fieldName, dir){
19303         var f = this.fields.get(fieldName);
19304         if(!dir){
19305             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19306                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19307             }else{
19308                 dir = f.sortDir;
19309             }
19310         }
19311         this.sortToggle[f.name] = dir;
19312         this.sortInfo = {field: f.name, direction: dir};
19313         if(!this.remoteSort){
19314             this.applySort();
19315             this.fireEvent("datachanged", this);
19316         }else{
19317             this.load(this.lastOptions);
19318         }
19319     },
19320
19321     /**
19322      * Calls the specified function for each of the Records in the cache.
19323      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19324      * Returning <em>false</em> aborts and exits the iteration.
19325      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19326      */
19327     each : function(fn, scope){
19328         this.data.each(fn, scope);
19329     },
19330
19331     /**
19332      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19333      * (e.g., during paging).
19334      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19335      */
19336     getModifiedRecords : function(){
19337         return this.modified;
19338     },
19339
19340     // private
19341     createFilterFn : function(property, value, anyMatch){
19342         if(!value.exec){ // not a regex
19343             value = String(value);
19344             if(value.length == 0){
19345                 return false;
19346             }
19347             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19348         }
19349         return function(r){
19350             return value.test(r.data[property]);
19351         };
19352     },
19353
19354     /**
19355      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19356      * @param {String} property A field on your records
19357      * @param {Number} start The record index to start at (defaults to 0)
19358      * @param {Number} end The last record index to include (defaults to length - 1)
19359      * @return {Number} The sum
19360      */
19361     sum : function(property, start, end){
19362         var rs = this.data.items, v = 0;
19363         start = start || 0;
19364         end = (end || end === 0) ? end : rs.length-1;
19365
19366         for(var i = start; i <= end; i++){
19367             v += (rs[i].data[property] || 0);
19368         }
19369         return v;
19370     },
19371
19372     /**
19373      * Filter the records by a specified property.
19374      * @param {String} field A field on your records
19375      * @param {String/RegExp} value Either a string that the field
19376      * should start with or a RegExp to test against the field
19377      * @param {Boolean} anyMatch True to match any part not just the beginning
19378      */
19379     filter : function(property, value, anyMatch){
19380         var fn = this.createFilterFn(property, value, anyMatch);
19381         return fn ? this.filterBy(fn) : this.clearFilter();
19382     },
19383
19384     /**
19385      * Filter by a function. The specified function will be called with each
19386      * record in this data source. If the function returns true the record is included,
19387      * otherwise it is filtered.
19388      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19389      * @param {Object} scope (optional) The scope of the function (defaults to this)
19390      */
19391     filterBy : function(fn, scope){
19392         this.snapshot = this.snapshot || this.data;
19393         this.data = this.queryBy(fn, scope||this);
19394         this.fireEvent("datachanged", this);
19395     },
19396
19397     /**
19398      * Query the records by a specified property.
19399      * @param {String} field A field on your records
19400      * @param {String/RegExp} value Either a string that the field
19401      * should start with or a RegExp to test against the field
19402      * @param {Boolean} anyMatch True to match any part not just the beginning
19403      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19404      */
19405     query : function(property, value, anyMatch){
19406         var fn = this.createFilterFn(property, value, anyMatch);
19407         return fn ? this.queryBy(fn) : this.data.clone();
19408     },
19409
19410     /**
19411      * Query by a function. The specified function will be called with each
19412      * record in this data source. If the function returns true the record is included
19413      * in the results.
19414      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19415      * @param {Object} scope (optional) The scope of the function (defaults to this)
19416       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19417      **/
19418     queryBy : function(fn, scope){
19419         var data = this.snapshot || this.data;
19420         return data.filterBy(fn, scope||this);
19421     },
19422
19423     /**
19424      * Collects unique values for a particular dataIndex from this store.
19425      * @param {String} dataIndex The property to collect
19426      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19427      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19428      * @return {Array} An array of the unique values
19429      **/
19430     collect : function(dataIndex, allowNull, bypassFilter){
19431         var d = (bypassFilter === true && this.snapshot) ?
19432                 this.snapshot.items : this.data.items;
19433         var v, sv, r = [], l = {};
19434         for(var i = 0, len = d.length; i < len; i++){
19435             v = d[i].data[dataIndex];
19436             sv = String(v);
19437             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19438                 l[sv] = true;
19439                 r[r.length] = v;
19440             }
19441         }
19442         return r;
19443     },
19444
19445     /**
19446      * Revert to a view of the Record cache with no filtering applied.
19447      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19448      */
19449     clearFilter : function(suppressEvent){
19450         if(this.snapshot && this.snapshot != this.data){
19451             this.data = this.snapshot;
19452             delete this.snapshot;
19453             if(suppressEvent !== true){
19454                 this.fireEvent("datachanged", this);
19455             }
19456         }
19457     },
19458
19459     // private
19460     afterEdit : function(record){
19461         if(this.modified.indexOf(record) == -1){
19462             this.modified.push(record);
19463         }
19464         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19465     },
19466
19467     // private
19468     afterReject : function(record){
19469         this.modified.remove(record);
19470         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19471     },
19472
19473     // private
19474     afterCommit : function(record){
19475         this.modified.remove(record);
19476         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19477     },
19478
19479     /**
19480      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19481      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19482      */
19483     commitChanges : function(){
19484         var m = this.modified.slice(0);
19485         this.modified = [];
19486         for(var i = 0, len = m.length; i < len; i++){
19487             m[i].commit();
19488         }
19489     },
19490
19491     /**
19492      * Cancel outstanding changes on all changed records.
19493      */
19494     rejectChanges : function(){
19495         var m = this.modified.slice(0);
19496         this.modified = [];
19497         for(var i = 0, len = m.length; i < len; i++){
19498             m[i].reject();
19499         }
19500     },
19501
19502     onMetaChange : function(meta, rtype, o){
19503         this.recordType = rtype;
19504         this.fields = rtype.prototype.fields;
19505         delete this.snapshot;
19506         this.sortInfo = meta.sortInfo;
19507         this.modified = [];
19508         this.fireEvent('metachange', this, this.reader.meta);
19509     }
19510 });/*
19511  * Based on:
19512  * Ext JS Library 1.1.1
19513  * Copyright(c) 2006-2007, Ext JS, LLC.
19514  *
19515  * Originally Released Under LGPL - original licence link has changed is not relivant.
19516  *
19517  * Fork - LGPL
19518  * <script type="text/javascript">
19519  */
19520
19521 /**
19522  * @class Roo.data.SimpleStore
19523  * @extends Roo.data.Store
19524  * Small helper class to make creating Stores from Array data easier.
19525  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19526  * @cfg {Array} fields An array of field definition objects, or field name strings.
19527  * @cfg {Array} data The multi-dimensional array of data
19528  * @constructor
19529  * @param {Object} config
19530  */
19531 Roo.data.SimpleStore = function(config){
19532     Roo.data.SimpleStore.superclass.constructor.call(this, {
19533         isLocal : true,
19534         reader: new Roo.data.ArrayReader({
19535                 id: config.id
19536             },
19537             Roo.data.Record.create(config.fields)
19538         ),
19539         proxy : new Roo.data.MemoryProxy(config.data)
19540     });
19541     this.load();
19542 };
19543 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19544  * Based on:
19545  * Ext JS Library 1.1.1
19546  * Copyright(c) 2006-2007, Ext JS, LLC.
19547  *
19548  * Originally Released Under LGPL - original licence link has changed is not relivant.
19549  *
19550  * Fork - LGPL
19551  * <script type="text/javascript">
19552  */
19553
19554 /**
19555 /**
19556  * @extends Roo.data.Store
19557  * @class Roo.data.JsonStore
19558  * Small helper class to make creating Stores for JSON data easier. <br/>
19559 <pre><code>
19560 var store = new Roo.data.JsonStore({
19561     url: 'get-images.php',
19562     root: 'images',
19563     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19564 });
19565 </code></pre>
19566  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19567  * JsonReader and HttpProxy (unless inline data is provided).</b>
19568  * @cfg {Array} fields An array of field definition objects, or field name strings.
19569  * @constructor
19570  * @param {Object} config
19571  */
19572 Roo.data.JsonStore = function(c){
19573     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19574         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19575         reader: new Roo.data.JsonReader(c, c.fields)
19576     }));
19577 };
19578 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19579  * Based on:
19580  * Ext JS Library 1.1.1
19581  * Copyright(c) 2006-2007, Ext JS, LLC.
19582  *
19583  * Originally Released Under LGPL - original licence link has changed is not relivant.
19584  *
19585  * Fork - LGPL
19586  * <script type="text/javascript">
19587  */
19588
19589  
19590 Roo.data.Field = function(config){
19591     if(typeof config == "string"){
19592         config = {name: config};
19593     }
19594     Roo.apply(this, config);
19595     
19596     if(!this.type){
19597         this.type = "auto";
19598     }
19599     
19600     var st = Roo.data.SortTypes;
19601     // named sortTypes are supported, here we look them up
19602     if(typeof this.sortType == "string"){
19603         this.sortType = st[this.sortType];
19604     }
19605     
19606     // set default sortType for strings and dates
19607     if(!this.sortType){
19608         switch(this.type){
19609             case "string":
19610                 this.sortType = st.asUCString;
19611                 break;
19612             case "date":
19613                 this.sortType = st.asDate;
19614                 break;
19615             default:
19616                 this.sortType = st.none;
19617         }
19618     }
19619
19620     // define once
19621     var stripRe = /[\$,%]/g;
19622
19623     // prebuilt conversion function for this field, instead of
19624     // switching every time we're reading a value
19625     if(!this.convert){
19626         var cv, dateFormat = this.dateFormat;
19627         switch(this.type){
19628             case "":
19629             case "auto":
19630             case undefined:
19631                 cv = function(v){ return v; };
19632                 break;
19633             case "string":
19634                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19635                 break;
19636             case "int":
19637                 cv = function(v){
19638                     return v !== undefined && v !== null && v !== '' ?
19639                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19640                     };
19641                 break;
19642             case "float":
19643                 cv = function(v){
19644                     return v !== undefined && v !== null && v !== '' ?
19645                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19646                     };
19647                 break;
19648             case "bool":
19649             case "boolean":
19650                 cv = function(v){ return v === true || v === "true" || v == 1; };
19651                 break;
19652             case "date":
19653                 cv = function(v){
19654                     if(!v){
19655                         return '';
19656                     }
19657                     if(v instanceof Date){
19658                         return v;
19659                     }
19660                     if(dateFormat){
19661                         if(dateFormat == "timestamp"){
19662                             return new Date(v*1000);
19663                         }
19664                         return Date.parseDate(v, dateFormat);
19665                     }
19666                     var parsed = Date.parse(v);
19667                     return parsed ? new Date(parsed) : null;
19668                 };
19669              break;
19670             
19671         }
19672         this.convert = cv;
19673     }
19674 };
19675
19676 Roo.data.Field.prototype = {
19677     dateFormat: null,
19678     defaultValue: "",
19679     mapping: null,
19680     sortType : null,
19681     sortDir : "ASC"
19682 };/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693 // Base class for reading structured data from a data source.  This class is intended to be
19694 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19695
19696 /**
19697  * @class Roo.data.DataReader
19698  * Base class for reading structured data from a data source.  This class is intended to be
19699  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19700  */
19701
19702 Roo.data.DataReader = function(meta, recordType){
19703     
19704     this.meta = meta;
19705     
19706     this.recordType = recordType instanceof Array ? 
19707         Roo.data.Record.create(recordType) : recordType;
19708 };
19709
19710 Roo.data.DataReader.prototype = {
19711      /**
19712      * Create an empty record
19713      * @param {Object} data (optional) - overlay some values
19714      * @return {Roo.data.Record} record created.
19715      */
19716     newRow :  function(d) {
19717         var da =  {};
19718         this.recordType.prototype.fields.each(function(c) {
19719             switch( c.type) {
19720                 case 'int' : da[c.name] = 0; break;
19721                 case 'date' : da[c.name] = new Date(); break;
19722                 case 'float' : da[c.name] = 0.0; break;
19723                 case 'boolean' : da[c.name] = false; break;
19724                 default : da[c.name] = ""; break;
19725             }
19726             
19727         });
19728         return new this.recordType(Roo.apply(da, d));
19729     }
19730     
19731 };/*
19732  * Based on:
19733  * Ext JS Library 1.1.1
19734  * Copyright(c) 2006-2007, Ext JS, LLC.
19735  *
19736  * Originally Released Under LGPL - original licence link has changed is not relivant.
19737  *
19738  * Fork - LGPL
19739  * <script type="text/javascript">
19740  */
19741
19742 /**
19743  * @class Roo.data.DataProxy
19744  * @extends Roo.data.Observable
19745  * This class is an abstract base class for implementations which provide retrieval of
19746  * unformatted data objects.<br>
19747  * <p>
19748  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19749  * (of the appropriate type which knows how to parse the data object) to provide a block of
19750  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19751  * <p>
19752  * Custom implementations must implement the load method as described in
19753  * {@link Roo.data.HttpProxy#load}.
19754  */
19755 Roo.data.DataProxy = function(){
19756     this.addEvents({
19757         /**
19758          * @event beforeload
19759          * Fires before a network request is made to retrieve a data object.
19760          * @param {Object} This DataProxy object.
19761          * @param {Object} params The params parameter to the load function.
19762          */
19763         beforeload : true,
19764         /**
19765          * @event load
19766          * Fires before the load method's callback is called.
19767          * @param {Object} This DataProxy object.
19768          * @param {Object} o The data object.
19769          * @param {Object} arg The callback argument object passed to the load function.
19770          */
19771         load : true,
19772         /**
19773          * @event loadexception
19774          * Fires if an Exception occurs during data retrieval.
19775          * @param {Object} This DataProxy object.
19776          * @param {Object} o The data object.
19777          * @param {Object} arg The callback argument object passed to the load function.
19778          * @param {Object} e The Exception.
19779          */
19780         loadexception : true
19781     });
19782     Roo.data.DataProxy.superclass.constructor.call(this);
19783 };
19784
19785 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19786
19787     /**
19788      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19789      */
19790 /*
19791  * Based on:
19792  * Ext JS Library 1.1.1
19793  * Copyright(c) 2006-2007, Ext JS, LLC.
19794  *
19795  * Originally Released Under LGPL - original licence link has changed is not relivant.
19796  *
19797  * Fork - LGPL
19798  * <script type="text/javascript">
19799  */
19800 /**
19801  * @class Roo.data.MemoryProxy
19802  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19803  * to the Reader when its load method is called.
19804  * @constructor
19805  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19806  */
19807 Roo.data.MemoryProxy = function(data){
19808     if (data.data) {
19809         data = data.data;
19810     }
19811     Roo.data.MemoryProxy.superclass.constructor.call(this);
19812     this.data = data;
19813 };
19814
19815 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19816     /**
19817      * Load data from the requested source (in this case an in-memory
19818      * data object passed to the constructor), read the data object into
19819      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19820      * process that block using the passed callback.
19821      * @param {Object} params This parameter is not used by the MemoryProxy class.
19822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19823      * object into a block of Roo.data.Records.
19824      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19825      * The function must be passed <ul>
19826      * <li>The Record block object</li>
19827      * <li>The "arg" argument from the load function</li>
19828      * <li>A boolean success indicator</li>
19829      * </ul>
19830      * @param {Object} scope The scope in which to call the callback
19831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19832      */
19833     load : function(params, reader, callback, scope, arg){
19834         params = params || {};
19835         var result;
19836         try {
19837             result = reader.readRecords(this.data);
19838         }catch(e){
19839             this.fireEvent("loadexception", this, arg, null, e);
19840             callback.call(scope, null, arg, false);
19841             return;
19842         }
19843         callback.call(scope, result, arg, true);
19844     },
19845     
19846     // private
19847     update : function(params, records){
19848         
19849     }
19850 });/*
19851  * Based on:
19852  * Ext JS Library 1.1.1
19853  * Copyright(c) 2006-2007, Ext JS, LLC.
19854  *
19855  * Originally Released Under LGPL - original licence link has changed is not relivant.
19856  *
19857  * Fork - LGPL
19858  * <script type="text/javascript">
19859  */
19860 /**
19861  * @class Roo.data.HttpProxy
19862  * @extends Roo.data.DataProxy
19863  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19864  * configured to reference a certain URL.<br><br>
19865  * <p>
19866  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19867  * from which the running page was served.<br><br>
19868  * <p>
19869  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19870  * <p>
19871  * Be aware that to enable the browser to parse an XML document, the server must set
19872  * the Content-Type header in the HTTP response to "text/xml".
19873  * @constructor
19874  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19875  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19876  * will be used to make the request.
19877  */
19878 Roo.data.HttpProxy = function(conn){
19879     Roo.data.HttpProxy.superclass.constructor.call(this);
19880     // is conn a conn config or a real conn?
19881     this.conn = conn;
19882     this.useAjax = !conn || !conn.events;
19883   
19884 };
19885
19886 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19887     // thse are take from connection...
19888     
19889     /**
19890      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19891      */
19892     /**
19893      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19894      * extra parameters to each request made by this object. (defaults to undefined)
19895      */
19896     /**
19897      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19898      *  to each request made by this object. (defaults to undefined)
19899      */
19900     /**
19901      * @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)
19902      */
19903     /**
19904      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19905      */
19906      /**
19907      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19908      * @type Boolean
19909      */
19910   
19911
19912     /**
19913      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19914      * @type Boolean
19915      */
19916     /**
19917      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19918      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19919      * a finer-grained basis than the DataProxy events.
19920      */
19921     getConnection : function(){
19922         return this.useAjax ? Roo.Ajax : this.conn;
19923     },
19924
19925     /**
19926      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19927      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19928      * process that block using the passed callback.
19929      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19930      * for the request to the remote server.
19931      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19932      * object into a block of Roo.data.Records.
19933      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19934      * The function must be passed <ul>
19935      * <li>The Record block object</li>
19936      * <li>The "arg" argument from the load function</li>
19937      * <li>A boolean success indicator</li>
19938      * </ul>
19939      * @param {Object} scope The scope in which to call the callback
19940      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19941      */
19942     load : function(params, reader, callback, scope, arg){
19943         if(this.fireEvent("beforeload", this, params) !== false){
19944             var  o = {
19945                 params : params || {},
19946                 request: {
19947                     callback : callback,
19948                     scope : scope,
19949                     arg : arg
19950                 },
19951                 reader: reader,
19952                 callback : this.loadResponse,
19953                 scope: this
19954             };
19955             if(this.useAjax){
19956                 Roo.applyIf(o, this.conn);
19957                 if(this.activeRequest){
19958                     Roo.Ajax.abort(this.activeRequest);
19959                 }
19960                 this.activeRequest = Roo.Ajax.request(o);
19961             }else{
19962                 this.conn.request(o);
19963             }
19964         }else{
19965             callback.call(scope||this, null, arg, false);
19966         }
19967     },
19968
19969     // private
19970     loadResponse : function(o, success, response){
19971         delete this.activeRequest;
19972         if(!success){
19973             this.fireEvent("loadexception", this, o, response);
19974             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19975             return;
19976         }
19977         var result;
19978         try {
19979             result = o.reader.read(response);
19980         }catch(e){
19981             this.fireEvent("loadexception", this, o, response, e);
19982             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19983             return;
19984         }
19985         
19986         this.fireEvent("load", this, o, o.request.arg);
19987         o.request.callback.call(o.request.scope, result, o.request.arg, true);
19988     },
19989
19990     // private
19991     update : function(dataSet){
19992
19993     },
19994
19995     // private
19996     updateResponse : function(dataSet){
19997
19998     }
19999 });/*
20000  * Based on:
20001  * Ext JS Library 1.1.1
20002  * Copyright(c) 2006-2007, Ext JS, LLC.
20003  *
20004  * Originally Released Under LGPL - original licence link has changed is not relivant.
20005  *
20006  * Fork - LGPL
20007  * <script type="text/javascript">
20008  */
20009
20010 /**
20011  * @class Roo.data.ScriptTagProxy
20012  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20013  * other than the originating domain of the running page.<br><br>
20014  * <p>
20015  * <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
20016  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20017  * <p>
20018  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20019  * source code that is used as the source inside a &lt;script> tag.<br><br>
20020  * <p>
20021  * In order for the browser to process the returned data, the server must wrap the data object
20022  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20023  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20024  * depending on whether the callback name was passed:
20025  * <p>
20026  * <pre><code>
20027 boolean scriptTag = false;
20028 String cb = request.getParameter("callback");
20029 if (cb != null) {
20030     scriptTag = true;
20031     response.setContentType("text/javascript");
20032 } else {
20033     response.setContentType("application/x-json");
20034 }
20035 Writer out = response.getWriter();
20036 if (scriptTag) {
20037     out.write(cb + "(");
20038 }
20039 out.print(dataBlock.toJsonString());
20040 if (scriptTag) {
20041     out.write(");");
20042 }
20043 </pre></code>
20044  *
20045  * @constructor
20046  * @param {Object} config A configuration object.
20047  */
20048 Roo.data.ScriptTagProxy = function(config){
20049     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20050     Roo.apply(this, config);
20051     this.head = document.getElementsByTagName("head")[0];
20052 };
20053
20054 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20055
20056 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20057     /**
20058      * @cfg {String} url The URL from which to request the data object.
20059      */
20060     /**
20061      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20062      */
20063     timeout : 30000,
20064     /**
20065      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20066      * the server the name of the callback function set up by the load call to process the returned data object.
20067      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20068      * javascript output which calls this named function passing the data object as its only parameter.
20069      */
20070     callbackParam : "callback",
20071     /**
20072      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20073      * name to the request.
20074      */
20075     nocache : true,
20076
20077     /**
20078      * Load data from the configured URL, read the data object into
20079      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20080      * process that block using the passed callback.
20081      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20082      * for the request to the remote server.
20083      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20084      * object into a block of Roo.data.Records.
20085      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20086      * The function must be passed <ul>
20087      * <li>The Record block object</li>
20088      * <li>The "arg" argument from the load function</li>
20089      * <li>A boolean success indicator</li>
20090      * </ul>
20091      * @param {Object} scope The scope in which to call the callback
20092      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20093      */
20094     load : function(params, reader, callback, scope, arg){
20095         if(this.fireEvent("beforeload", this, params) !== false){
20096
20097             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20098
20099             var url = this.url;
20100             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20101             if(this.nocache){
20102                 url += "&_dc=" + (new Date().getTime());
20103             }
20104             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20105             var trans = {
20106                 id : transId,
20107                 cb : "stcCallback"+transId,
20108                 scriptId : "stcScript"+transId,
20109                 params : params,
20110                 arg : arg,
20111                 url : url,
20112                 callback : callback,
20113                 scope : scope,
20114                 reader : reader
20115             };
20116             var conn = this;
20117
20118             window[trans.cb] = function(o){
20119                 conn.handleResponse(o, trans);
20120             };
20121
20122             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20123
20124             if(this.autoAbort !== false){
20125                 this.abort();
20126             }
20127
20128             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20129
20130             var script = document.createElement("script");
20131             script.setAttribute("src", url);
20132             script.setAttribute("type", "text/javascript");
20133             script.setAttribute("id", trans.scriptId);
20134             this.head.appendChild(script);
20135
20136             this.trans = trans;
20137         }else{
20138             callback.call(scope||this, null, arg, false);
20139         }
20140     },
20141
20142     // private
20143     isLoading : function(){
20144         return this.trans ? true : false;
20145     },
20146
20147     /**
20148      * Abort the current server request.
20149      */
20150     abort : function(){
20151         if(this.isLoading()){
20152             this.destroyTrans(this.trans);
20153         }
20154     },
20155
20156     // private
20157     destroyTrans : function(trans, isLoaded){
20158         this.head.removeChild(document.getElementById(trans.scriptId));
20159         clearTimeout(trans.timeoutId);
20160         if(isLoaded){
20161             window[trans.cb] = undefined;
20162             try{
20163                 delete window[trans.cb];
20164             }catch(e){}
20165         }else{
20166             // if hasn't been loaded, wait for load to remove it to prevent script error
20167             window[trans.cb] = function(){
20168                 window[trans.cb] = undefined;
20169                 try{
20170                     delete window[trans.cb];
20171                 }catch(e){}
20172             };
20173         }
20174     },
20175
20176     // private
20177     handleResponse : function(o, trans){
20178         this.trans = false;
20179         this.destroyTrans(trans, true);
20180         var result;
20181         try {
20182             result = trans.reader.readRecords(o);
20183         }catch(e){
20184             this.fireEvent("loadexception", this, o, trans.arg, e);
20185             trans.callback.call(trans.scope||window, null, trans.arg, false);
20186             return;
20187         }
20188         this.fireEvent("load", this, o, trans.arg);
20189         trans.callback.call(trans.scope||window, result, trans.arg, true);
20190     },
20191
20192     // private
20193     handleFailure : function(trans){
20194         this.trans = false;
20195         this.destroyTrans(trans, false);
20196         this.fireEvent("loadexception", this, null, trans.arg);
20197         trans.callback.call(trans.scope||window, null, trans.arg, false);
20198     }
20199 });/*
20200  * Based on:
20201  * Ext JS Library 1.1.1
20202  * Copyright(c) 2006-2007, Ext JS, LLC.
20203  *
20204  * Originally Released Under LGPL - original licence link has changed is not relivant.
20205  *
20206  * Fork - LGPL
20207  * <script type="text/javascript">
20208  */
20209
20210 /**
20211  * @class Roo.data.JsonReader
20212  * @extends Roo.data.DataReader
20213  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20214  * based on mappings in a provided Roo.data.Record constructor.
20215  * <p>
20216  * Example code:
20217  * <pre><code>
20218 var RecordDef = Roo.data.Record.create([
20219     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20220     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20221 ]);
20222 var myReader = new Roo.data.JsonReader({
20223     totalProperty: "results",    // The property which contains the total dataset size (optional)
20224     root: "rows",                // The property which contains an Array of row objects
20225     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20226 }, RecordDef);
20227 </code></pre>
20228  * <p>
20229  * This would consume a JSON file like this:
20230  * <pre><code>
20231 { 'results': 2, 'rows': [
20232     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20233     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20234 }
20235 </code></pre>
20236  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20237  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20238  * paged from the remote server.
20239  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20240  * @cfg {String} root name of the property which contains the Array of row objects.
20241  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20242  * @constructor
20243  * Create a new JsonReader
20244  * @param {Object} meta Metadata configuration options
20245  * @param {Object} recordType Either an Array of field definition objects,
20246  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20247  */
20248 Roo.data.JsonReader = function(meta, recordType){
20249     
20250     meta = meta || {};
20251     // set some defaults:
20252     Roo.applyIf(meta, {
20253         totalProperty: 'total',
20254         successProperty : 'success',
20255         root : 'data',
20256         id : 'id'
20257     });
20258     
20259     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20260 };
20261 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20262     /**
20263      * This method is only used by a DataProxy which has retrieved data from a remote server.
20264      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20265      * @return {Object} data A data block which is used by an Roo.data.Store object as
20266      * a cache of Roo.data.Records.
20267      */
20268     read : function(response){
20269         var json = response.responseText;
20270         /* eval:var:o */
20271         var o = eval("("+json+")");
20272         if(!o) {
20273             throw {message: "JsonReader.read: Json object not found"};
20274         }
20275         
20276         if(o.metaData){
20277             delete this.ef;
20278             this.meta = o.metaData;
20279             this.recordType = Roo.data.Record.create(o.metaData.fields);
20280             this.onMetaChange(this.meta, this.recordType, o);
20281         }
20282         return this.readRecords(o);
20283     },
20284
20285     // private function a store will implement
20286     onMetaChange : function(meta, recordType, o){
20287
20288     },
20289
20290     /**
20291          * @ignore
20292          */
20293     simpleAccess: function(obj, subsc) {
20294         return obj[subsc];
20295     },
20296
20297         /**
20298          * @ignore
20299          */
20300     getJsonAccessor: function(){
20301         var re = /[\[\.]/;
20302         return function(expr) {
20303             try {
20304                 return(re.test(expr))
20305                     ? new Function("obj", "return obj." + expr)
20306                     : function(obj){
20307                         return obj[expr];
20308                     };
20309             } catch(e){}
20310             return Roo.emptyFn;
20311         };
20312     }(),
20313
20314     /**
20315      * Create a data block containing Roo.data.Records from an XML document.
20316      * @param {Object} o An object which contains an Array of row objects in the property specified
20317      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20318      * which contains the total size of the dataset.
20319      * @return {Object} data A data block which is used by an Roo.data.Store object as
20320      * a cache of Roo.data.Records.
20321      */
20322     readRecords : function(o){
20323         /**
20324          * After any data loads, the raw JSON data is available for further custom processing.
20325          * @type Object
20326          */
20327         this.jsonData = o;
20328         var s = this.meta, Record = this.recordType,
20329             f = Record.prototype.fields, fi = f.items, fl = f.length;
20330
20331 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20332         if (!this.ef) {
20333             if(s.totalProperty) {
20334                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20335                 }
20336                 if(s.successProperty) {
20337                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20338                 }
20339                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20340                 if (s.id) {
20341                         var g = this.getJsonAccessor(s.id);
20342                         this.getId = function(rec) {
20343                                 var r = g(rec);
20344                                 return (r === undefined || r === "") ? null : r;
20345                         };
20346                 } else {
20347                         this.getId = function(){return null;};
20348                 }
20349             this.ef = [];
20350             for(var i = 0; i < fl; i++){
20351                 f = fi[i];
20352                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20353                 this.ef[i] = this.getJsonAccessor(map);
20354             }
20355         }
20356
20357         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20358         if(s.totalProperty){
20359             var v = parseInt(this.getTotal(o), 10);
20360             if(!isNaN(v)){
20361                 totalRecords = v;
20362             }
20363         }
20364         if(s.successProperty){
20365             var v = this.getSuccess(o);
20366             if(v === false || v === 'false'){
20367                 success = false;
20368             }
20369         }
20370         var records = [];
20371             for(var i = 0; i < c; i++){
20372                     var n = root[i];
20373                 var values = {};
20374                 var id = this.getId(n);
20375                 for(var j = 0; j < fl; j++){
20376                     f = fi[j];
20377                 var v = this.ef[j](n);
20378                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20379                 }
20380                 var record = new Record(values, id);
20381                 record.json = n;
20382                 records[i] = record;
20383             }
20384             return {
20385                 success : success,
20386                 records : records,
20387                 totalRecords : totalRecords
20388             };
20389     }
20390 });/*
20391  * Based on:
20392  * Ext JS Library 1.1.1
20393  * Copyright(c) 2006-2007, Ext JS, LLC.
20394  *
20395  * Originally Released Under LGPL - original licence link has changed is not relivant.
20396  *
20397  * Fork - LGPL
20398  * <script type="text/javascript">
20399  */
20400
20401 /**
20402  * @class Roo.data.XmlReader
20403  * @extends Roo.data.DataReader
20404  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20405  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20406  * <p>
20407  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20408  * header in the HTTP response must be set to "text/xml".</em>
20409  * <p>
20410  * Example code:
20411  * <pre><code>
20412 var RecordDef = Roo.data.Record.create([
20413    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20414    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20415 ]);
20416 var myReader = new Roo.data.XmlReader({
20417    totalRecords: "results", // The element which contains the total dataset size (optional)
20418    record: "row",           // The repeated element which contains row information
20419    id: "id"                 // The element within the row that provides an ID for the record (optional)
20420 }, RecordDef);
20421 </code></pre>
20422  * <p>
20423  * This would consume an XML file like this:
20424  * <pre><code>
20425 &lt;?xml?>
20426 &lt;dataset>
20427  &lt;results>2&lt;/results>
20428  &lt;row>
20429    &lt;id>1&lt;/id>
20430    &lt;name>Bill&lt;/name>
20431    &lt;occupation>Gardener&lt;/occupation>
20432  &lt;/row>
20433  &lt;row>
20434    &lt;id>2&lt;/id>
20435    &lt;name>Ben&lt;/name>
20436    &lt;occupation>Horticulturalist&lt;/occupation>
20437  &lt;/row>
20438 &lt;/dataset>
20439 </code></pre>
20440  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20441  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20442  * paged from the remote server.
20443  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20444  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20445  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20446  * a record identifier value.
20447  * @constructor
20448  * Create a new XmlReader
20449  * @param {Object} meta Metadata configuration options
20450  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20451  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20452  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20453  */
20454 Roo.data.XmlReader = function(meta, recordType){
20455     meta = meta || {};
20456     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20457 };
20458 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20459     /**
20460      * This method is only used by a DataProxy which has retrieved data from a remote server.
20461          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20462          * to contain a method called 'responseXML' that returns an XML document object.
20463      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20464      * a cache of Roo.data.Records.
20465      */
20466     read : function(response){
20467         var doc = response.responseXML;
20468         if(!doc) {
20469             throw {message: "XmlReader.read: XML Document not available"};
20470         }
20471         return this.readRecords(doc);
20472     },
20473
20474     /**
20475      * Create a data block containing Roo.data.Records from an XML document.
20476          * @param {Object} doc A parsed XML document.
20477      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(doc){
20481         /**
20482          * After any data loads/reads, the raw XML Document is available for further custom processing.
20483          * @type XMLDocument
20484          */
20485         this.xmlData = doc;
20486         var root = doc.documentElement || doc;
20487         var q = Roo.DomQuery;
20488         var recordType = this.recordType, fields = recordType.prototype.fields;
20489         var sid = this.meta.id;
20490         var totalRecords = 0, success = true;
20491         if(this.meta.totalRecords){
20492             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20493         }
20494         
20495         if(this.meta.success){
20496             var sv = q.selectValue(this.meta.success, root, true);
20497             success = sv !== false && sv !== 'false';
20498         }
20499         var records = [];
20500         var ns = q.select(this.meta.record, root);
20501         for(var i = 0, len = ns.length; i < len; i++) {
20502                 var n = ns[i];
20503                 var values = {};
20504                 var id = sid ? q.selectValue(sid, n) : undefined;
20505                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20506                     var f = fields.items[j];
20507                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20508                     v = f.convert(v);
20509                     values[f.name] = v;
20510                 }
20511                 var record = new recordType(values, id);
20512                 record.node = n;
20513                 records[records.length] = record;
20514             }
20515
20516             return {
20517                 success : success,
20518                 records : records,
20519                 totalRecords : totalRecords || records.length
20520             };
20521     }
20522 });/*
20523  * Based on:
20524  * Ext JS Library 1.1.1
20525  * Copyright(c) 2006-2007, Ext JS, LLC.
20526  *
20527  * Originally Released Under LGPL - original licence link has changed is not relivant.
20528  *
20529  * Fork - LGPL
20530  * <script type="text/javascript">
20531  */
20532
20533 /**
20534  * @class Roo.data.ArrayReader
20535  * @extends Roo.data.DataReader
20536  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20537  * Each element of that Array represents a row of data fields. The
20538  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20539  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20540  * <p>
20541  * Example code:.
20542  * <pre><code>
20543 var RecordDef = Roo.data.Record.create([
20544     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20545     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20546 ]);
20547 var myReader = new Roo.data.ArrayReader({
20548     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20549 }, RecordDef);
20550 </code></pre>
20551  * <p>
20552  * This would consume an Array like this:
20553  * <pre><code>
20554 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20555   </code></pre>
20556  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20557  * @constructor
20558  * Create a new JsonReader
20559  * @param {Object} meta Metadata configuration options.
20560  * @param {Object} recordType Either an Array of field definition objects
20561  * as specified to {@link Roo.data.Record#create},
20562  * or an {@link Roo.data.Record} object
20563  * created using {@link Roo.data.Record#create}.
20564  */
20565 Roo.data.ArrayReader = function(meta, recordType){
20566     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20567 };
20568
20569 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20570     /**
20571      * Create a data block containing Roo.data.Records from an XML document.
20572      * @param {Object} o An Array of row objects which represents the dataset.
20573      * @return {Object} data A data block which is used by an Roo.data.Store object as
20574      * a cache of Roo.data.Records.
20575      */
20576     readRecords : function(o){
20577         var sid = this.meta ? this.meta.id : null;
20578         var recordType = this.recordType, fields = recordType.prototype.fields;
20579         var records = [];
20580         var root = o;
20581             for(var i = 0; i < root.length; i++){
20582                     var n = root[i];
20583                 var values = {};
20584                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20585                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20586                 var f = fields.items[j];
20587                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20588                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20589                 v = f.convert(v);
20590                 values[f.name] = v;
20591             }
20592                 var record = new recordType(values, id);
20593                 record.json = n;
20594                 records[records.length] = record;
20595             }
20596             return {
20597                 records : records,
20598                 totalRecords : records.length
20599             };
20600     }
20601 });/*
20602  * Based on:
20603  * Ext JS Library 1.1.1
20604  * Copyright(c) 2006-2007, Ext JS, LLC.
20605  *
20606  * Originally Released Under LGPL - original licence link has changed is not relivant.
20607  *
20608  * Fork - LGPL
20609  * <script type="text/javascript">
20610  */
20611
20612
20613 /**
20614  * @class Roo.data.Tree
20615  * @extends Roo.util.Observable
20616  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20617  * in the tree have most standard DOM functionality.
20618  * @constructor
20619  * @param {Node} root (optional) The root node
20620  */
20621 Roo.data.Tree = function(root){
20622    this.nodeHash = {};
20623    /**
20624     * The root node for this tree
20625     * @type Node
20626     */
20627    this.root = null;
20628    if(root){
20629        this.setRootNode(root);
20630    }
20631    this.addEvents({
20632        /**
20633         * @event append
20634         * Fires when a new child node is appended to a node in this tree.
20635         * @param {Tree} tree The owner tree
20636         * @param {Node} parent The parent node
20637         * @param {Node} node The newly appended node
20638         * @param {Number} index The index of the newly appended node
20639         */
20640        "append" : true,
20641        /**
20642         * @event remove
20643         * Fires when a child node is removed from a node in this tree.
20644         * @param {Tree} tree The owner tree
20645         * @param {Node} parent The parent node
20646         * @param {Node} node The child node removed
20647         */
20648        "remove" : true,
20649        /**
20650         * @event move
20651         * Fires when a node is moved to a new location in the tree
20652         * @param {Tree} tree The owner tree
20653         * @param {Node} node The node moved
20654         * @param {Node} oldParent The old parent of this node
20655         * @param {Node} newParent The new parent of this node
20656         * @param {Number} index The index it was moved to
20657         */
20658        "move" : true,
20659        /**
20660         * @event insert
20661         * Fires when a new child node is inserted in a node in this tree.
20662         * @param {Tree} tree The owner tree
20663         * @param {Node} parent The parent node
20664         * @param {Node} node The child node inserted
20665         * @param {Node} refNode The child node the node was inserted before
20666         */
20667        "insert" : true,
20668        /**
20669         * @event beforeappend
20670         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20671         * @param {Tree} tree The owner tree
20672         * @param {Node} parent The parent node
20673         * @param {Node} node The child node to be appended
20674         */
20675        "beforeappend" : true,
20676        /**
20677         * @event beforeremove
20678         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20679         * @param {Tree} tree The owner tree
20680         * @param {Node} parent The parent node
20681         * @param {Node} node The child node to be removed
20682         */
20683        "beforeremove" : true,
20684        /**
20685         * @event beforemove
20686         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20687         * @param {Tree} tree The owner tree
20688         * @param {Node} node The node being moved
20689         * @param {Node} oldParent The parent of the node
20690         * @param {Node} newParent The new parent the node is moving to
20691         * @param {Number} index The index it is being moved to
20692         */
20693        "beforemove" : true,
20694        /**
20695         * @event beforeinsert
20696         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20697         * @param {Tree} tree The owner tree
20698         * @param {Node} parent The parent node
20699         * @param {Node} node The child node to be inserted
20700         * @param {Node} refNode The child node the node is being inserted before
20701         */
20702        "beforeinsert" : true
20703    });
20704
20705     Roo.data.Tree.superclass.constructor.call(this);
20706 };
20707
20708 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20709     pathSeparator: "/",
20710
20711     proxyNodeEvent : function(){
20712         return this.fireEvent.apply(this, arguments);
20713     },
20714
20715     /**
20716      * Returns the root node for this tree.
20717      * @return {Node}
20718      */
20719     getRootNode : function(){
20720         return this.root;
20721     },
20722
20723     /**
20724      * Sets the root node for this tree.
20725      * @param {Node} node
20726      * @return {Node}
20727      */
20728     setRootNode : function(node){
20729         this.root = node;
20730         node.ownerTree = this;
20731         node.isRoot = true;
20732         this.registerNode(node);
20733         return node;
20734     },
20735
20736     /**
20737      * Gets a node in this tree by its id.
20738      * @param {String} id
20739      * @return {Node}
20740      */
20741     getNodeById : function(id){
20742         return this.nodeHash[id];
20743     },
20744
20745     registerNode : function(node){
20746         this.nodeHash[node.id] = node;
20747     },
20748
20749     unregisterNode : function(node){
20750         delete this.nodeHash[node.id];
20751     },
20752
20753     toString : function(){
20754         return "[Tree"+(this.id?" "+this.id:"")+"]";
20755     }
20756 });
20757
20758 /**
20759  * @class Roo.data.Node
20760  * @extends Roo.util.Observable
20761  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20762  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20763  * @constructor
20764  * @param {Object} attributes The attributes/config for the node
20765  */
20766 Roo.data.Node = function(attributes){
20767     /**
20768      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20769      * @type {Object}
20770      */
20771     this.attributes = attributes || {};
20772     this.leaf = this.attributes.leaf;
20773     /**
20774      * The node id. @type String
20775      */
20776     this.id = this.attributes.id;
20777     if(!this.id){
20778         this.id = Roo.id(null, "ynode-");
20779         this.attributes.id = this.id;
20780     }
20781     /**
20782      * All child nodes of this node. @type Array
20783      */
20784     this.childNodes = [];
20785     if(!this.childNodes.indexOf){ // indexOf is a must
20786         this.childNodes.indexOf = function(o){
20787             for(var i = 0, len = this.length; i < len; i++){
20788                 if(this[i] == o) return i;
20789             }
20790             return -1;
20791         };
20792     }
20793     /**
20794      * The parent node for this node. @type Node
20795      */
20796     this.parentNode = null;
20797     /**
20798      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20799      */
20800     this.firstChild = null;
20801     /**
20802      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20803      */
20804     this.lastChild = null;
20805     /**
20806      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20807      */
20808     this.previousSibling = null;
20809     /**
20810      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20811      */
20812     this.nextSibling = null;
20813
20814     this.addEvents({
20815        /**
20816         * @event append
20817         * Fires when a new child node is appended
20818         * @param {Tree} tree The owner tree
20819         * @param {Node} this This node
20820         * @param {Node} node The newly appended node
20821         * @param {Number} index The index of the newly appended node
20822         */
20823        "append" : true,
20824        /**
20825         * @event remove
20826         * Fires when a child node is removed
20827         * @param {Tree} tree The owner tree
20828         * @param {Node} this This node
20829         * @param {Node} node The removed node
20830         */
20831        "remove" : true,
20832        /**
20833         * @event move
20834         * Fires when this node is moved to a new location in the tree
20835         * @param {Tree} tree The owner tree
20836         * @param {Node} this This node
20837         * @param {Node} oldParent The old parent of this node
20838         * @param {Node} newParent The new parent of this node
20839         * @param {Number} index The index it was moved to
20840         */
20841        "move" : true,
20842        /**
20843         * @event insert
20844         * Fires when a new child node is inserted.
20845         * @param {Tree} tree The owner tree
20846         * @param {Node} this This node
20847         * @param {Node} node The child node inserted
20848         * @param {Node} refNode The child node the node was inserted before
20849         */
20850        "insert" : true,
20851        /**
20852         * @event beforeappend
20853         * Fires before a new child is appended, return false to cancel the append.
20854         * @param {Tree} tree The owner tree
20855         * @param {Node} this This node
20856         * @param {Node} node The child node to be appended
20857         */
20858        "beforeappend" : true,
20859        /**
20860         * @event beforeremove
20861         * Fires before a child is removed, return false to cancel the remove.
20862         * @param {Tree} tree The owner tree
20863         * @param {Node} this This node
20864         * @param {Node} node The child node to be removed
20865         */
20866        "beforeremove" : true,
20867        /**
20868         * @event beforemove
20869         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20870         * @param {Tree} tree The owner tree
20871         * @param {Node} this This node
20872         * @param {Node} oldParent The parent of this node
20873         * @param {Node} newParent The new parent this node is moving to
20874         * @param {Number} index The index it is being moved to
20875         */
20876        "beforemove" : true,
20877        /**
20878         * @event beforeinsert
20879         * Fires before a new child is inserted, return false to cancel the insert.
20880         * @param {Tree} tree The owner tree
20881         * @param {Node} this This node
20882         * @param {Node} node The child node to be inserted
20883         * @param {Node} refNode The child node the node is being inserted before
20884         */
20885        "beforeinsert" : true
20886    });
20887     this.listeners = this.attributes.listeners;
20888     Roo.data.Node.superclass.constructor.call(this);
20889 };
20890
20891 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20892     fireEvent : function(evtName){
20893         // first do standard event for this node
20894         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20895             return false;
20896         }
20897         // then bubble it up to the tree if the event wasn't cancelled
20898         var ot = this.getOwnerTree();
20899         if(ot){
20900             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20901                 return false;
20902             }
20903         }
20904         return true;
20905     },
20906
20907     /**
20908      * Returns true if this node is a leaf
20909      * @return {Boolean}
20910      */
20911     isLeaf : function(){
20912         return this.leaf === true;
20913     },
20914
20915     // private
20916     setFirstChild : function(node){
20917         this.firstChild = node;
20918     },
20919
20920     //private
20921     setLastChild : function(node){
20922         this.lastChild = node;
20923     },
20924
20925
20926     /**
20927      * Returns true if this node is the last child of its parent
20928      * @return {Boolean}
20929      */
20930     isLast : function(){
20931        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20932     },
20933
20934     /**
20935      * Returns true if this node is the first child of its parent
20936      * @return {Boolean}
20937      */
20938     isFirst : function(){
20939        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20940     },
20941
20942     hasChildNodes : function(){
20943         return !this.isLeaf() && this.childNodes.length > 0;
20944     },
20945
20946     /**
20947      * Insert node(s) as the last child node of this node.
20948      * @param {Node/Array} node The node or Array of nodes to append
20949      * @return {Node} The appended node if single append, or null if an array was passed
20950      */
20951     appendChild : function(node){
20952         var multi = false;
20953         if(node instanceof Array){
20954             multi = node;
20955         }else if(arguments.length > 1){
20956             multi = arguments;
20957         }
20958         // if passed an array or multiple args do them one by one
20959         if(multi){
20960             for(var i = 0, len = multi.length; i < len; i++) {
20961                 this.appendChild(multi[i]);
20962             }
20963         }else{
20964             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20965                 return false;
20966             }
20967             var index = this.childNodes.length;
20968             var oldParent = node.parentNode;
20969             // it's a move, make sure we move it cleanly
20970             if(oldParent){
20971                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
20972                     return false;
20973                 }
20974                 oldParent.removeChild(node);
20975             }
20976             index = this.childNodes.length;
20977             if(index == 0){
20978                 this.setFirstChild(node);
20979             }
20980             this.childNodes.push(node);
20981             node.parentNode = this;
20982             var ps = this.childNodes[index-1];
20983             if(ps){
20984                 node.previousSibling = ps;
20985                 ps.nextSibling = node;
20986             }else{
20987                 node.previousSibling = null;
20988             }
20989             node.nextSibling = null;
20990             this.setLastChild(node);
20991             node.setOwnerTree(this.getOwnerTree());
20992             this.fireEvent("append", this.ownerTree, this, node, index);
20993             if(oldParent){
20994                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
20995             }
20996             return node;
20997         }
20998     },
20999
21000     /**
21001      * Removes a child node from this node.
21002      * @param {Node} node The node to remove
21003      * @return {Node} The removed node
21004      */
21005     removeChild : function(node){
21006         var index = this.childNodes.indexOf(node);
21007         if(index == -1){
21008             return false;
21009         }
21010         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21011             return false;
21012         }
21013
21014         // remove it from childNodes collection
21015         this.childNodes.splice(index, 1);
21016
21017         // update siblings
21018         if(node.previousSibling){
21019             node.previousSibling.nextSibling = node.nextSibling;
21020         }
21021         if(node.nextSibling){
21022             node.nextSibling.previousSibling = node.previousSibling;
21023         }
21024
21025         // update child refs
21026         if(this.firstChild == node){
21027             this.setFirstChild(node.nextSibling);
21028         }
21029         if(this.lastChild == node){
21030             this.setLastChild(node.previousSibling);
21031         }
21032
21033         node.setOwnerTree(null);
21034         // clear any references from the node
21035         node.parentNode = null;
21036         node.previousSibling = null;
21037         node.nextSibling = null;
21038         this.fireEvent("remove", this.ownerTree, this, node);
21039         return node;
21040     },
21041
21042     /**
21043      * Inserts the first node before the second node in this nodes childNodes collection.
21044      * @param {Node} node The node to insert
21045      * @param {Node} refNode The node to insert before (if null the node is appended)
21046      * @return {Node} The inserted node
21047      */
21048     insertBefore : function(node, refNode){
21049         if(!refNode){ // like standard Dom, refNode can be null for append
21050             return this.appendChild(node);
21051         }
21052         // nothing to do
21053         if(node == refNode){
21054             return false;
21055         }
21056
21057         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21058             return false;
21059         }
21060         var index = this.childNodes.indexOf(refNode);
21061         var oldParent = node.parentNode;
21062         var refIndex = index;
21063
21064         // when moving internally, indexes will change after remove
21065         if(oldParent == this && this.childNodes.indexOf(node) < index){
21066             refIndex--;
21067         }
21068
21069         // it's a move, make sure we move it cleanly
21070         if(oldParent){
21071             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21072                 return false;
21073             }
21074             oldParent.removeChild(node);
21075         }
21076         if(refIndex == 0){
21077             this.setFirstChild(node);
21078         }
21079         this.childNodes.splice(refIndex, 0, node);
21080         node.parentNode = this;
21081         var ps = this.childNodes[refIndex-1];
21082         if(ps){
21083             node.previousSibling = ps;
21084             ps.nextSibling = node;
21085         }else{
21086             node.previousSibling = null;
21087         }
21088         node.nextSibling = refNode;
21089         refNode.previousSibling = node;
21090         node.setOwnerTree(this.getOwnerTree());
21091         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21092         if(oldParent){
21093             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21094         }
21095         return node;
21096     },
21097
21098     /**
21099      * Returns the child node at the specified index.
21100      * @param {Number} index
21101      * @return {Node}
21102      */
21103     item : function(index){
21104         return this.childNodes[index];
21105     },
21106
21107     /**
21108      * Replaces one child node in this node with another.
21109      * @param {Node} newChild The replacement node
21110      * @param {Node} oldChild The node to replace
21111      * @return {Node} The replaced node
21112      */
21113     replaceChild : function(newChild, oldChild){
21114         this.insertBefore(newChild, oldChild);
21115         this.removeChild(oldChild);
21116         return oldChild;
21117     },
21118
21119     /**
21120      * Returns the index of a child node
21121      * @param {Node} node
21122      * @return {Number} The index of the node or -1 if it was not found
21123      */
21124     indexOf : function(child){
21125         return this.childNodes.indexOf(child);
21126     },
21127
21128     /**
21129      * Returns the tree this node is in.
21130      * @return {Tree}
21131      */
21132     getOwnerTree : function(){
21133         // if it doesn't have one, look for one
21134         if(!this.ownerTree){
21135             var p = this;
21136             while(p){
21137                 if(p.ownerTree){
21138                     this.ownerTree = p.ownerTree;
21139                     break;
21140                 }
21141                 p = p.parentNode;
21142             }
21143         }
21144         return this.ownerTree;
21145     },
21146
21147     /**
21148      * Returns depth of this node (the root node has a depth of 0)
21149      * @return {Number}
21150      */
21151     getDepth : function(){
21152         var depth = 0;
21153         var p = this;
21154         while(p.parentNode){
21155             ++depth;
21156             p = p.parentNode;
21157         }
21158         return depth;
21159     },
21160
21161     // private
21162     setOwnerTree : function(tree){
21163         // if it's move, we need to update everyone
21164         if(tree != this.ownerTree){
21165             if(this.ownerTree){
21166                 this.ownerTree.unregisterNode(this);
21167             }
21168             this.ownerTree = tree;
21169             var cs = this.childNodes;
21170             for(var i = 0, len = cs.length; i < len; i++) {
21171                 cs[i].setOwnerTree(tree);
21172             }
21173             if(tree){
21174                 tree.registerNode(this);
21175             }
21176         }
21177     },
21178
21179     /**
21180      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21181      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21182      * @return {String} The path
21183      */
21184     getPath : function(attr){
21185         attr = attr || "id";
21186         var p = this.parentNode;
21187         var b = [this.attributes[attr]];
21188         while(p){
21189             b.unshift(p.attributes[attr]);
21190             p = p.parentNode;
21191         }
21192         var sep = this.getOwnerTree().pathSeparator;
21193         return sep + b.join(sep);
21194     },
21195
21196     /**
21197      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21198      * function call will be the scope provided or the current node. The arguments to the function
21199      * will be the args provided or the current node. If the function returns false at any point,
21200      * the bubble is stopped.
21201      * @param {Function} fn The function to call
21202      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21203      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21204      */
21205     bubble : function(fn, scope, args){
21206         var p = this;
21207         while(p){
21208             if(fn.call(scope || p, args || p) === false){
21209                 break;
21210             }
21211             p = p.parentNode;
21212         }
21213     },
21214
21215     /**
21216      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21217      * function call will be the scope provided or the current node. The arguments to the function
21218      * will be the args provided or the current node. If the function returns false at any point,
21219      * the cascade is stopped on that branch.
21220      * @param {Function} fn The function to call
21221      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21222      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21223      */
21224     cascade : function(fn, scope, args){
21225         if(fn.call(scope || this, args || this) !== false){
21226             var cs = this.childNodes;
21227             for(var i = 0, len = cs.length; i < len; i++) {
21228                 cs[i].cascade(fn, scope, args);
21229             }
21230         }
21231     },
21232
21233     /**
21234      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21235      * function call will be the scope provided or the current node. The arguments to the function
21236      * will be the args provided or the current node. If the function returns false at any point,
21237      * the iteration stops.
21238      * @param {Function} fn The function to call
21239      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21240      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21241      */
21242     eachChild : function(fn, scope, args){
21243         var cs = this.childNodes;
21244         for(var i = 0, len = cs.length; i < len; i++) {
21245                 if(fn.call(scope || this, args || cs[i]) === false){
21246                     break;
21247                 }
21248         }
21249     },
21250
21251     /**
21252      * Finds the first child that has the attribute with the specified value.
21253      * @param {String} attribute The attribute name
21254      * @param {Mixed} value The value to search for
21255      * @return {Node} The found child or null if none was found
21256      */
21257     findChild : function(attribute, value){
21258         var cs = this.childNodes;
21259         for(var i = 0, len = cs.length; i < len; i++) {
21260                 if(cs[i].attributes[attribute] == value){
21261                     return cs[i];
21262                 }
21263         }
21264         return null;
21265     },
21266
21267     /**
21268      * Finds the first child by a custom function. The child matches if the function passed
21269      * returns true.
21270      * @param {Function} fn
21271      * @param {Object} scope (optional)
21272      * @return {Node} The found child or null if none was found
21273      */
21274     findChildBy : function(fn, scope){
21275         var cs = this.childNodes;
21276         for(var i = 0, len = cs.length; i < len; i++) {
21277                 if(fn.call(scope||cs[i], cs[i]) === true){
21278                     return cs[i];
21279                 }
21280         }
21281         return null;
21282     },
21283
21284     /**
21285      * Sorts this nodes children using the supplied sort function
21286      * @param {Function} fn
21287      * @param {Object} scope (optional)
21288      */
21289     sort : function(fn, scope){
21290         var cs = this.childNodes;
21291         var len = cs.length;
21292         if(len > 0){
21293             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21294             cs.sort(sortFn);
21295             for(var i = 0; i < len; i++){
21296                 var n = cs[i];
21297                 n.previousSibling = cs[i-1];
21298                 n.nextSibling = cs[i+1];
21299                 if(i == 0){
21300                     this.setFirstChild(n);
21301                 }
21302                 if(i == len-1){
21303                     this.setLastChild(n);
21304                 }
21305             }
21306         }
21307     },
21308
21309     /**
21310      * Returns true if this node is an ancestor (at any point) of the passed node.
21311      * @param {Node} node
21312      * @return {Boolean}
21313      */
21314     contains : function(node){
21315         return node.isAncestor(this);
21316     },
21317
21318     /**
21319      * Returns true if the passed node is an ancestor (at any point) of this node.
21320      * @param {Node} node
21321      * @return {Boolean}
21322      */
21323     isAncestor : function(node){
21324         var p = this.parentNode;
21325         while(p){
21326             if(p == node){
21327                 return true;
21328             }
21329             p = p.parentNode;
21330         }
21331         return false;
21332     },
21333
21334     toString : function(){
21335         return "[Node"+(this.id?" "+this.id:"")+"]";
21336     }
21337 });/*
21338  * Based on:
21339  * Ext JS Library 1.1.1
21340  * Copyright(c) 2006-2007, Ext JS, LLC.
21341  *
21342  * Originally Released Under LGPL - original licence link has changed is not relivant.
21343  *
21344  * Fork - LGPL
21345  * <script type="text/javascript">
21346  */
21347  
21348
21349 /**
21350  * @class Roo.ComponentMgr
21351  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21352  * @singleton
21353  */
21354 Roo.ComponentMgr = function(){
21355     var all = new Roo.util.MixedCollection();
21356
21357     return {
21358         /**
21359          * Registers a component.
21360          * @param {Roo.Component} c The component
21361          */
21362         register : function(c){
21363             all.add(c);
21364         },
21365
21366         /**
21367          * Unregisters a component.
21368          * @param {Roo.Component} c The component
21369          */
21370         unregister : function(c){
21371             all.remove(c);
21372         },
21373
21374         /**
21375          * Returns a component by id
21376          * @param {String} id The component id
21377          */
21378         get : function(id){
21379             return all.get(id);
21380         },
21381
21382         /**
21383          * Registers a function that will be called when a specified component is added to ComponentMgr
21384          * @param {String} id The component id
21385          * @param {Funtction} fn The callback function
21386          * @param {Object} scope The scope of the callback
21387          */
21388         onAvailable : function(id, fn, scope){
21389             all.on("add", function(index, o){
21390                 if(o.id == id){
21391                     fn.call(scope || o, o);
21392                     all.un("add", fn, scope);
21393                 }
21394             });
21395         }
21396     };
21397 }();/*
21398  * Based on:
21399  * Ext JS Library 1.1.1
21400  * Copyright(c) 2006-2007, Ext JS, LLC.
21401  *
21402  * Originally Released Under LGPL - original licence link has changed is not relivant.
21403  *
21404  * Fork - LGPL
21405  * <script type="text/javascript">
21406  */
21407  
21408 /**
21409  * @class Roo.Component
21410  * @extends Roo.util.Observable
21411  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21412  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21413  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21414  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21415  * All visual components (widgets) that require rendering into a layout should subclass Component.
21416  * @constructor
21417  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21418  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21419  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21420  */
21421 Roo.Component = function(config){
21422     config = config || {};
21423     if(config.tagName || config.dom || typeof config == "string"){ // element object
21424         config = {el: config, id: config.id || config};
21425     }
21426     this.initialConfig = config;
21427
21428     Roo.apply(this, config);
21429     this.addEvents({
21430         /**
21431          * @event disable
21432          * Fires after the component is disabled.
21433              * @param {Roo.Component} this
21434              */
21435         disable : true,
21436         /**
21437          * @event enable
21438          * Fires after the component is enabled.
21439              * @param {Roo.Component} this
21440              */
21441         enable : true,
21442         /**
21443          * @event beforeshow
21444          * Fires before the component is shown.  Return false to stop the show.
21445              * @param {Roo.Component} this
21446              */
21447         beforeshow : true,
21448         /**
21449          * @event show
21450          * Fires after the component is shown.
21451              * @param {Roo.Component} this
21452              */
21453         show : true,
21454         /**
21455          * @event beforehide
21456          * Fires before the component is hidden. Return false to stop the hide.
21457              * @param {Roo.Component} this
21458              */
21459         beforehide : true,
21460         /**
21461          * @event hide
21462          * Fires after the component is hidden.
21463              * @param {Roo.Component} this
21464              */
21465         hide : true,
21466         /**
21467          * @event beforerender
21468          * Fires before the component is rendered. Return false to stop the render.
21469              * @param {Roo.Component} this
21470              */
21471         beforerender : true,
21472         /**
21473          * @event render
21474          * Fires after the component is rendered.
21475              * @param {Roo.Component} this
21476              */
21477         render : true,
21478         /**
21479          * @event beforedestroy
21480          * Fires before the component is destroyed. Return false to stop the destroy.
21481              * @param {Roo.Component} this
21482              */
21483         beforedestroy : true,
21484         /**
21485          * @event destroy
21486          * Fires after the component is destroyed.
21487              * @param {Roo.Component} this
21488              */
21489         destroy : true
21490     });
21491     if(!this.id){
21492         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21493     }
21494     Roo.ComponentMgr.register(this);
21495     Roo.Component.superclass.constructor.call(this);
21496     this.initComponent();
21497     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21498         this.render(this.renderTo);
21499         delete this.renderTo;
21500     }
21501 };
21502
21503 // private
21504 Roo.Component.AUTO_ID = 1000;
21505
21506 Roo.extend(Roo.Component, Roo.util.Observable, {
21507     /**
21508      * @property {Boolean} hidden
21509      * true if this component is hidden. Read-only.
21510      */
21511     hidden : false,
21512     /**
21513      * true if this component is disabled. Read-only.
21514      */
21515     disabled : false,
21516     /**
21517      * true if this component has been rendered. Read-only.
21518      */
21519     rendered : false,
21520     
21521     /** @cfg {String} disableClass
21522      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21523      */
21524     disabledClass : "x-item-disabled",
21525         /** @cfg {Boolean} allowDomMove
21526          * Whether the component can move the Dom node when rendering (defaults to true).
21527          */
21528     allowDomMove : true,
21529     /** @cfg {String} hideMode
21530      * How this component should hidden. Supported values are
21531      * "visibility" (css visibility), "offsets" (negative offset position) and
21532      * "display" (css display) - defaults to "display".
21533      */
21534     hideMode: 'display',
21535
21536     // private
21537     ctype : "Roo.Component",
21538
21539     /** @cfg {String} actionMode 
21540      * which property holds the element that used for  hide() / show() / disable() / enable()
21541      * default is 'el' 
21542      */
21543     actionMode : "el",
21544
21545     // private
21546     getActionEl : function(){
21547         return this[this.actionMode];
21548     },
21549
21550     initComponent : Roo.emptyFn,
21551     /**
21552      * If this is a lazy rendering component, render it to its container element.
21553      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21554      */
21555     render : function(container, position){
21556         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21557             if(!container && this.el){
21558                 this.el = Roo.get(this.el);
21559                 container = this.el.dom.parentNode;
21560                 this.allowDomMove = false;
21561             }
21562             this.container = Roo.get(container);
21563             this.rendered = true;
21564             if(position !== undefined){
21565                 if(typeof position == 'number'){
21566                     position = this.container.dom.childNodes[position];
21567                 }else{
21568                     position = Roo.getDom(position);
21569                 }
21570             }
21571             this.onRender(this.container, position || null);
21572             if(this.cls){
21573                 this.el.addClass(this.cls);
21574                 delete this.cls;
21575             }
21576             if(this.style){
21577                 this.el.applyStyles(this.style);
21578                 delete this.style;
21579             }
21580             this.fireEvent("render", this);
21581             this.afterRender(this.container);
21582             if(this.hidden){
21583                 this.hide();
21584             }
21585             if(this.disabled){
21586                 this.disable();
21587             }
21588         }
21589         return this;
21590     },
21591
21592     // private
21593     // default function is not really useful
21594     onRender : function(ct, position){
21595         if(this.el){
21596             this.el = Roo.get(this.el);
21597             if(this.allowDomMove !== false){
21598                 ct.dom.insertBefore(this.el.dom, position);
21599             }
21600         }
21601     },
21602
21603     // private
21604     getAutoCreate : function(){
21605         var cfg = typeof this.autoCreate == "object" ?
21606                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21607         if(this.id && !cfg.id){
21608             cfg.id = this.id;
21609         }
21610         return cfg;
21611     },
21612
21613     // private
21614     afterRender : Roo.emptyFn,
21615
21616     /**
21617      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21618      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21619      */
21620     destroy : function(){
21621         if(this.fireEvent("beforedestroy", this) !== false){
21622             this.purgeListeners();
21623             this.beforeDestroy();
21624             if(this.rendered){
21625                 this.el.removeAllListeners();
21626                 this.el.remove();
21627                 if(this.actionMode == "container"){
21628                     this.container.remove();
21629                 }
21630             }
21631             this.onDestroy();
21632             Roo.ComponentMgr.unregister(this);
21633             this.fireEvent("destroy", this);
21634         }
21635     },
21636
21637         // private
21638     beforeDestroy : function(){
21639
21640     },
21641
21642         // private
21643         onDestroy : function(){
21644
21645     },
21646
21647     /**
21648      * Returns the underlying {@link Roo.Element}.
21649      * @return {Roo.Element} The element
21650      */
21651     getEl : function(){
21652         return this.el;
21653     },
21654
21655     /**
21656      * Returns the id of this component.
21657      * @return {String}
21658      */
21659     getId : function(){
21660         return this.id;
21661     },
21662
21663     /**
21664      * Try to focus this component.
21665      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21666      * @return {Roo.Component} this
21667      */
21668     focus : function(selectText){
21669         if(this.rendered){
21670             this.el.focus();
21671             if(selectText === true){
21672                 this.el.dom.select();
21673             }
21674         }
21675         return this;
21676     },
21677
21678     // private
21679     blur : function(){
21680         if(this.rendered){
21681             this.el.blur();
21682         }
21683         return this;
21684     },
21685
21686     /**
21687      * Disable this component.
21688      * @return {Roo.Component} this
21689      */
21690     disable : function(){
21691         if(this.rendered){
21692             this.onDisable();
21693         }
21694         this.disabled = true;
21695         this.fireEvent("disable", this);
21696         return this;
21697     },
21698
21699         // private
21700     onDisable : function(){
21701         this.getActionEl().addClass(this.disabledClass);
21702         this.el.dom.disabled = true;
21703     },
21704
21705     /**
21706      * Enable this component.
21707      * @return {Roo.Component} this
21708      */
21709     enable : function(){
21710         if(this.rendered){
21711             this.onEnable();
21712         }
21713         this.disabled = false;
21714         this.fireEvent("enable", this);
21715         return this;
21716     },
21717
21718         // private
21719     onEnable : function(){
21720         this.getActionEl().removeClass(this.disabledClass);
21721         this.el.dom.disabled = false;
21722     },
21723
21724     /**
21725      * Convenience function for setting disabled/enabled by boolean.
21726      * @param {Boolean} disabled
21727      */
21728     setDisabled : function(disabled){
21729         this[disabled ? "disable" : "enable"]();
21730     },
21731
21732     /**
21733      * Show this component.
21734      * @return {Roo.Component} this
21735      */
21736     show: function(){
21737         if(this.fireEvent("beforeshow", this) !== false){
21738             this.hidden = false;
21739             if(this.rendered){
21740                 this.onShow();
21741             }
21742             this.fireEvent("show", this);
21743         }
21744         return this;
21745     },
21746
21747     // private
21748     onShow : function(){
21749         var ae = this.getActionEl();
21750         if(this.hideMode == 'visibility'){
21751             ae.dom.style.visibility = "visible";
21752         }else if(this.hideMode == 'offsets'){
21753             ae.removeClass('x-hidden');
21754         }else{
21755             ae.dom.style.display = "";
21756         }
21757     },
21758
21759     /**
21760      * Hide this component.
21761      * @return {Roo.Component} this
21762      */
21763     hide: function(){
21764         if(this.fireEvent("beforehide", this) !== false){
21765             this.hidden = true;
21766             if(this.rendered){
21767                 this.onHide();
21768             }
21769             this.fireEvent("hide", this);
21770         }
21771         return this;
21772     },
21773
21774     // private
21775     onHide : function(){
21776         var ae = this.getActionEl();
21777         if(this.hideMode == 'visibility'){
21778             ae.dom.style.visibility = "hidden";
21779         }else if(this.hideMode == 'offsets'){
21780             ae.addClass('x-hidden');
21781         }else{
21782             ae.dom.style.display = "none";
21783         }
21784     },
21785
21786     /**
21787      * Convenience function to hide or show this component by boolean.
21788      * @param {Boolean} visible True to show, false to hide
21789      * @return {Roo.Component} this
21790      */
21791     setVisible: function(visible){
21792         if(visible) {
21793             this.show();
21794         }else{
21795             this.hide();
21796         }
21797         return this;
21798     },
21799
21800     /**
21801      * Returns true if this component is visible.
21802      */
21803     isVisible : function(){
21804         return this.getActionEl().isVisible();
21805     },
21806
21807     cloneConfig : function(overrides){
21808         overrides = overrides || {};
21809         var id = overrides.id || Roo.id();
21810         var cfg = Roo.applyIf(overrides, this.initialConfig);
21811         cfg.id = id; // prevent dup id
21812         return new this.constructor(cfg);
21813     }
21814 });/*
21815  * Based on:
21816  * Ext JS Library 1.1.1
21817  * Copyright(c) 2006-2007, Ext JS, LLC.
21818  *
21819  * Originally Released Under LGPL - original licence link has changed is not relivant.
21820  *
21821  * Fork - LGPL
21822  * <script type="text/javascript">
21823  */
21824  (function(){ 
21825 /**
21826  * @class Roo.Layer
21827  * @extends Roo.Element
21828  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21829  * automatic maintaining of shadow/shim positions.
21830  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21831  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21832  * you can pass a string with a CSS class name. False turns off the shadow.
21833  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21834  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21835  * @cfg {String} cls CSS class to add to the element
21836  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21837  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21838  * @constructor
21839  * @param {Object} config An object with config options.
21840  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21841  */
21842
21843 Roo.Layer = function(config, existingEl){
21844     config = config || {};
21845     var dh = Roo.DomHelper;
21846     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21847     if(existingEl){
21848         this.dom = Roo.getDom(existingEl);
21849     }
21850     if(!this.dom){
21851         var o = config.dh || {tag: "div", cls: "x-layer"};
21852         this.dom = dh.append(pel, o);
21853     }
21854     if(config.cls){
21855         this.addClass(config.cls);
21856     }
21857     this.constrain = config.constrain !== false;
21858     this.visibilityMode = Roo.Element.VISIBILITY;
21859     if(config.id){
21860         this.id = this.dom.id = config.id;
21861     }else{
21862         this.id = Roo.id(this.dom);
21863     }
21864     this.zindex = config.zindex || this.getZIndex();
21865     this.position("absolute", this.zindex);
21866     if(config.shadow){
21867         this.shadowOffset = config.shadowOffset || 4;
21868         this.shadow = new Roo.Shadow({
21869             offset : this.shadowOffset,
21870             mode : config.shadow
21871         });
21872     }else{
21873         this.shadowOffset = 0;
21874     }
21875     this.useShim = config.shim !== false && Roo.useShims;
21876     this.useDisplay = config.useDisplay;
21877     this.hide();
21878 };
21879
21880 var supr = Roo.Element.prototype;
21881
21882 // shims are shared among layer to keep from having 100 iframes
21883 var shims = [];
21884
21885 Roo.extend(Roo.Layer, Roo.Element, {
21886
21887     getZIndex : function(){
21888         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21889     },
21890
21891     getShim : function(){
21892         if(!this.useShim){
21893             return null;
21894         }
21895         if(this.shim){
21896             return this.shim;
21897         }
21898         var shim = shims.shift();
21899         if(!shim){
21900             shim = this.createShim();
21901             shim.enableDisplayMode('block');
21902             shim.dom.style.display = 'none';
21903             shim.dom.style.visibility = 'visible';
21904         }
21905         var pn = this.dom.parentNode;
21906         if(shim.dom.parentNode != pn){
21907             pn.insertBefore(shim.dom, this.dom);
21908         }
21909         shim.setStyle('z-index', this.getZIndex()-2);
21910         this.shim = shim;
21911         return shim;
21912     },
21913
21914     hideShim : function(){
21915         if(this.shim){
21916             this.shim.setDisplayed(false);
21917             shims.push(this.shim);
21918             delete this.shim;
21919         }
21920     },
21921
21922     disableShadow : function(){
21923         if(this.shadow){
21924             this.shadowDisabled = true;
21925             this.shadow.hide();
21926             this.lastShadowOffset = this.shadowOffset;
21927             this.shadowOffset = 0;
21928         }
21929     },
21930
21931     enableShadow : function(show){
21932         if(this.shadow){
21933             this.shadowDisabled = false;
21934             this.shadowOffset = this.lastShadowOffset;
21935             delete this.lastShadowOffset;
21936             if(show){
21937                 this.sync(true);
21938             }
21939         }
21940     },
21941
21942     // private
21943     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21944     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21945     sync : function(doShow){
21946         var sw = this.shadow;
21947         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21948             var sh = this.getShim();
21949
21950             var w = this.getWidth(),
21951                 h = this.getHeight();
21952
21953             var l = this.getLeft(true),
21954                 t = this.getTop(true);
21955
21956             if(sw && !this.shadowDisabled){
21957                 if(doShow && !sw.isVisible()){
21958                     sw.show(this);
21959                 }else{
21960                     sw.realign(l, t, w, h);
21961                 }
21962                 if(sh){
21963                     if(doShow){
21964                        sh.show();
21965                     }
21966                     // fit the shim behind the shadow, so it is shimmed too
21967                     var a = sw.adjusts, s = sh.dom.style;
21968                     s.left = (Math.min(l, l+a.l))+"px";
21969                     s.top = (Math.min(t, t+a.t))+"px";
21970                     s.width = (w+a.w)+"px";
21971                     s.height = (h+a.h)+"px";
21972                 }
21973             }else if(sh){
21974                 if(doShow){
21975                    sh.show();
21976                 }
21977                 sh.setSize(w, h);
21978                 sh.setLeftTop(l, t);
21979             }
21980             
21981         }
21982     },
21983
21984     // private
21985     destroy : function(){
21986         this.hideShim();
21987         if(this.shadow){
21988             this.shadow.hide();
21989         }
21990         this.removeAllListeners();
21991         var pn = this.dom.parentNode;
21992         if(pn){
21993             pn.removeChild(this.dom);
21994         }
21995         Roo.Element.uncache(this.id);
21996     },
21997
21998     remove : function(){
21999         this.destroy();
22000     },
22001
22002     // private
22003     beginUpdate : function(){
22004         this.updating = true;
22005     },
22006
22007     // private
22008     endUpdate : function(){
22009         this.updating = false;
22010         this.sync(true);
22011     },
22012
22013     // private
22014     hideUnders : function(negOffset){
22015         if(this.shadow){
22016             this.shadow.hide();
22017         }
22018         this.hideShim();
22019     },
22020
22021     // private
22022     constrainXY : function(){
22023         if(this.constrain){
22024             var vw = Roo.lib.Dom.getViewWidth(),
22025                 vh = Roo.lib.Dom.getViewHeight();
22026             var s = Roo.get(document).getScroll();
22027
22028             var xy = this.getXY();
22029             var x = xy[0], y = xy[1];   
22030             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22031             // only move it if it needs it
22032             var moved = false;
22033             // first validate right/bottom
22034             if((x + w) > vw+s.left){
22035                 x = vw - w - this.shadowOffset;
22036                 moved = true;
22037             }
22038             if((y + h) > vh+s.top){
22039                 y = vh - h - this.shadowOffset;
22040                 moved = true;
22041             }
22042             // then make sure top/left isn't negative
22043             if(x < s.left){
22044                 x = s.left;
22045                 moved = true;
22046             }
22047             if(y < s.top){
22048                 y = s.top;
22049                 moved = true;
22050             }
22051             if(moved){
22052                 if(this.avoidY){
22053                     var ay = this.avoidY;
22054                     if(y <= ay && (y+h) >= ay){
22055                         y = ay-h-5;   
22056                     }
22057                 }
22058                 xy = [x, y];
22059                 this.storeXY(xy);
22060                 supr.setXY.call(this, xy);
22061                 this.sync();
22062             }
22063         }
22064     },
22065
22066     isVisible : function(){
22067         return this.visible;    
22068     },
22069
22070     // private
22071     showAction : function(){
22072         this.visible = true; // track visibility to prevent getStyle calls
22073         if(this.useDisplay === true){
22074             this.setDisplayed("");
22075         }else if(this.lastXY){
22076             supr.setXY.call(this, this.lastXY);
22077         }else if(this.lastLT){
22078             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22079         }
22080     },
22081
22082     // private
22083     hideAction : function(){
22084         this.visible = false;
22085         if(this.useDisplay === true){
22086             this.setDisplayed(false);
22087         }else{
22088             this.setLeftTop(-10000,-10000);
22089         }
22090     },
22091
22092     // overridden Element method
22093     setVisible : function(v, a, d, c, e){
22094         if(v){
22095             this.showAction();
22096         }
22097         if(a && v){
22098             var cb = function(){
22099                 this.sync(true);
22100                 if(c){
22101                     c();
22102                 }
22103             }.createDelegate(this);
22104             supr.setVisible.call(this, true, true, d, cb, e);
22105         }else{
22106             if(!v){
22107                 this.hideUnders(true);
22108             }
22109             var cb = c;
22110             if(a){
22111                 cb = function(){
22112                     this.hideAction();
22113                     if(c){
22114                         c();
22115                     }
22116                 }.createDelegate(this);
22117             }
22118             supr.setVisible.call(this, v, a, d, cb, e);
22119             if(v){
22120                 this.sync(true);
22121             }else if(!a){
22122                 this.hideAction();
22123             }
22124         }
22125     },
22126
22127     storeXY : function(xy){
22128         delete this.lastLT;
22129         this.lastXY = xy;
22130     },
22131
22132     storeLeftTop : function(left, top){
22133         delete this.lastXY;
22134         this.lastLT = [left, top];
22135     },
22136
22137     // private
22138     beforeFx : function(){
22139         this.beforeAction();
22140         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22141     },
22142
22143     // private
22144     afterFx : function(){
22145         Roo.Layer.superclass.afterFx.apply(this, arguments);
22146         this.sync(this.isVisible());
22147     },
22148
22149     // private
22150     beforeAction : function(){
22151         if(!this.updating && this.shadow){
22152             this.shadow.hide();
22153         }
22154     },
22155
22156     // overridden Element method
22157     setLeft : function(left){
22158         this.storeLeftTop(left, this.getTop(true));
22159         supr.setLeft.apply(this, arguments);
22160         this.sync();
22161     },
22162
22163     setTop : function(top){
22164         this.storeLeftTop(this.getLeft(true), top);
22165         supr.setTop.apply(this, arguments);
22166         this.sync();
22167     },
22168
22169     setLeftTop : function(left, top){
22170         this.storeLeftTop(left, top);
22171         supr.setLeftTop.apply(this, arguments);
22172         this.sync();
22173     },
22174
22175     setXY : function(xy, a, d, c, e){
22176         this.fixDisplay();
22177         this.beforeAction();
22178         this.storeXY(xy);
22179         var cb = this.createCB(c);
22180         supr.setXY.call(this, xy, a, d, cb, e);
22181         if(!a){
22182             cb();
22183         }
22184     },
22185
22186     // private
22187     createCB : function(c){
22188         var el = this;
22189         return function(){
22190             el.constrainXY();
22191             el.sync(true);
22192             if(c){
22193                 c();
22194             }
22195         };
22196     },
22197
22198     // overridden Element method
22199     setX : function(x, a, d, c, e){
22200         this.setXY([x, this.getY()], a, d, c, e);
22201     },
22202
22203     // overridden Element method
22204     setY : function(y, a, d, c, e){
22205         this.setXY([this.getX(), y], a, d, c, e);
22206     },
22207
22208     // overridden Element method
22209     setSize : function(w, h, a, d, c, e){
22210         this.beforeAction();
22211         var cb = this.createCB(c);
22212         supr.setSize.call(this, w, h, a, d, cb, e);
22213         if(!a){
22214             cb();
22215         }
22216     },
22217
22218     // overridden Element method
22219     setWidth : function(w, a, d, c, e){
22220         this.beforeAction();
22221         var cb = this.createCB(c);
22222         supr.setWidth.call(this, w, a, d, cb, e);
22223         if(!a){
22224             cb();
22225         }
22226     },
22227
22228     // overridden Element method
22229     setHeight : function(h, a, d, c, e){
22230         this.beforeAction();
22231         var cb = this.createCB(c);
22232         supr.setHeight.call(this, h, a, d, cb, e);
22233         if(!a){
22234             cb();
22235         }
22236     },
22237
22238     // overridden Element method
22239     setBounds : function(x, y, w, h, a, d, c, e){
22240         this.beforeAction();
22241         var cb = this.createCB(c);
22242         if(!a){
22243             this.storeXY([x, y]);
22244             supr.setXY.call(this, [x, y]);
22245             supr.setSize.call(this, w, h, a, d, cb, e);
22246             cb();
22247         }else{
22248             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22249         }
22250         return this;
22251     },
22252     
22253     /**
22254      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22255      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22256      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22257      * @param {Number} zindex The new z-index to set
22258      * @return {this} The Layer
22259      */
22260     setZIndex : function(zindex){
22261         this.zindex = zindex;
22262         this.setStyle("z-index", zindex + 2);
22263         if(this.shadow){
22264             this.shadow.setZIndex(zindex + 1);
22265         }
22266         if(this.shim){
22267             this.shim.setStyle("z-index", zindex);
22268         }
22269     }
22270 });
22271 })();/*
22272  * Based on:
22273  * Ext JS Library 1.1.1
22274  * Copyright(c) 2006-2007, Ext JS, LLC.
22275  *
22276  * Originally Released Under LGPL - original licence link has changed is not relivant.
22277  *
22278  * Fork - LGPL
22279  * <script type="text/javascript">
22280  */
22281
22282
22283 /**
22284  * @class Roo.Shadow
22285  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22286  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22287  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22288  * @constructor
22289  * Create a new Shadow
22290  * @param {Object} config The config object
22291  */
22292 Roo.Shadow = function(config){
22293     Roo.apply(this, config);
22294     if(typeof this.mode != "string"){
22295         this.mode = this.defaultMode;
22296     }
22297     var o = this.offset, a = {h: 0};
22298     var rad = Math.floor(this.offset/2);
22299     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22300         case "drop":
22301             a.w = 0;
22302             a.l = a.t = o;
22303             a.t -= 1;
22304             if(Roo.isIE){
22305                 a.l -= this.offset + rad;
22306                 a.t -= this.offset + rad;
22307                 a.w -= rad;
22308                 a.h -= rad;
22309                 a.t += 1;
22310             }
22311         break;
22312         case "sides":
22313             a.w = (o*2);
22314             a.l = -o;
22315             a.t = o-1;
22316             if(Roo.isIE){
22317                 a.l -= (this.offset - rad);
22318                 a.t -= this.offset + rad;
22319                 a.l += 1;
22320                 a.w -= (this.offset - rad)*2;
22321                 a.w -= rad + 1;
22322                 a.h -= 1;
22323             }
22324         break;
22325         case "frame":
22326             a.w = a.h = (o*2);
22327             a.l = a.t = -o;
22328             a.t += 1;
22329             a.h -= 2;
22330             if(Roo.isIE){
22331                 a.l -= (this.offset - rad);
22332                 a.t -= (this.offset - rad);
22333                 a.l += 1;
22334                 a.w -= (this.offset + rad + 1);
22335                 a.h -= (this.offset + rad);
22336                 a.h += 1;
22337             }
22338         break;
22339     };
22340
22341     this.adjusts = a;
22342 };
22343
22344 Roo.Shadow.prototype = {
22345     /**
22346      * @cfg {String} mode
22347      * The shadow display mode.  Supports the following options:<br />
22348      * sides: Shadow displays on both sides and bottom only<br />
22349      * frame: Shadow displays equally on all four sides<br />
22350      * drop: Traditional bottom-right drop shadow (default)
22351      */
22352     /**
22353      * @cfg {String} offset
22354      * The number of pixels to offset the shadow from the element (defaults to 4)
22355      */
22356     offset: 4,
22357
22358     // private
22359     defaultMode: "drop",
22360
22361     /**
22362      * Displays the shadow under the target element
22363      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22364      */
22365     show : function(target){
22366         target = Roo.get(target);
22367         if(!this.el){
22368             this.el = Roo.Shadow.Pool.pull();
22369             if(this.el.dom.nextSibling != target.dom){
22370                 this.el.insertBefore(target);
22371             }
22372         }
22373         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22374         if(Roo.isIE){
22375             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22376         }
22377         this.realign(
22378             target.getLeft(true),
22379             target.getTop(true),
22380             target.getWidth(),
22381             target.getHeight()
22382         );
22383         this.el.dom.style.display = "block";
22384     },
22385
22386     /**
22387      * Returns true if the shadow is visible, else false
22388      */
22389     isVisible : function(){
22390         return this.el ? true : false;  
22391     },
22392
22393     /**
22394      * Direct alignment when values are already available. Show must be called at least once before
22395      * calling this method to ensure it is initialized.
22396      * @param {Number} left The target element left position
22397      * @param {Number} top The target element top position
22398      * @param {Number} width The target element width
22399      * @param {Number} height The target element height
22400      */
22401     realign : function(l, t, w, h){
22402         if(!this.el){
22403             return;
22404         }
22405         var a = this.adjusts, d = this.el.dom, s = d.style;
22406         var iea = 0;
22407         s.left = (l+a.l)+"px";
22408         s.top = (t+a.t)+"px";
22409         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22410         if(s.width != sws || s.height != shs){
22411             s.width = sws;
22412             s.height = shs;
22413             if(!Roo.isIE){
22414                 var cn = d.childNodes;
22415                 var sww = Math.max(0, (sw-12))+"px";
22416                 cn[0].childNodes[1].style.width = sww;
22417                 cn[1].childNodes[1].style.width = sww;
22418                 cn[2].childNodes[1].style.width = sww;
22419                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22420             }
22421         }
22422     },
22423
22424     /**
22425      * Hides this shadow
22426      */
22427     hide : function(){
22428         if(this.el){
22429             this.el.dom.style.display = "none";
22430             Roo.Shadow.Pool.push(this.el);
22431             delete this.el;
22432         }
22433     },
22434
22435     /**
22436      * Adjust the z-index of this shadow
22437      * @param {Number} zindex The new z-index
22438      */
22439     setZIndex : function(z){
22440         this.zIndex = z;
22441         if(this.el){
22442             this.el.setStyle("z-index", z);
22443         }
22444     }
22445 };
22446
22447 // Private utility class that manages the internal Shadow cache
22448 Roo.Shadow.Pool = function(){
22449     var p = [];
22450     var markup = Roo.isIE ?
22451                  '<div class="x-ie-shadow"></div>' :
22452                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22453     return {
22454         pull : function(){
22455             var sh = p.shift();
22456             if(!sh){
22457                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22458                 sh.autoBoxAdjust = false;
22459             }
22460             return sh;
22461         },
22462
22463         push : function(sh){
22464             p.push(sh);
22465         }
22466     };
22467 }();/*
22468  * Based on:
22469  * Ext JS Library 1.1.1
22470  * Copyright(c) 2006-2007, Ext JS, LLC.
22471  *
22472  * Originally Released Under LGPL - original licence link has changed is not relivant.
22473  *
22474  * Fork - LGPL
22475  * <script type="text/javascript">
22476  */
22477
22478 /**
22479  * @class Roo.BoxComponent
22480  * @extends Roo.Component
22481  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22482  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22483  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22484  * layout containers.
22485  * @constructor
22486  * @param {Roo.Element/String/Object} config The configuration options.
22487  */
22488 Roo.BoxComponent = function(config){
22489     Roo.Component.call(this, config);
22490     this.addEvents({
22491         /**
22492          * @event resize
22493          * Fires after the component is resized.
22494              * @param {Roo.Component} this
22495              * @param {Number} adjWidth The box-adjusted width that was set
22496              * @param {Number} adjHeight The box-adjusted height that was set
22497              * @param {Number} rawWidth The width that was originally specified
22498              * @param {Number} rawHeight The height that was originally specified
22499              */
22500         resize : true,
22501         /**
22502          * @event move
22503          * Fires after the component is moved.
22504              * @param {Roo.Component} this
22505              * @param {Number} x The new x position
22506              * @param {Number} y The new y position
22507              */
22508         move : true
22509     });
22510 };
22511
22512 Roo.extend(Roo.BoxComponent, Roo.Component, {
22513     // private, set in afterRender to signify that the component has been rendered
22514     boxReady : false,
22515     // private, used to defer height settings to subclasses
22516     deferHeight: false,
22517     /** @cfg {Number} width
22518      * width (optional) size of component
22519      */
22520      /** @cfg {Number} height
22521      * height (optional) size of component
22522      */
22523      
22524     /**
22525      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22526      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22527      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22528      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22529      * @return {Roo.BoxComponent} this
22530      */
22531     setSize : function(w, h){
22532         // support for standard size objects
22533         if(typeof w == 'object'){
22534             h = w.height;
22535             w = w.width;
22536         }
22537         // not rendered
22538         if(!this.boxReady){
22539             this.width = w;
22540             this.height = h;
22541             return this;
22542         }
22543
22544         // prevent recalcs when not needed
22545         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22546             return this;
22547         }
22548         this.lastSize = {width: w, height: h};
22549
22550         var adj = this.adjustSize(w, h);
22551         var aw = adj.width, ah = adj.height;
22552         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22553             var rz = this.getResizeEl();
22554             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22555                 rz.setSize(aw, ah);
22556             }else if(!this.deferHeight && ah !== undefined){
22557                 rz.setHeight(ah);
22558             }else if(aw !== undefined){
22559                 rz.setWidth(aw);
22560             }
22561             this.onResize(aw, ah, w, h);
22562             this.fireEvent('resize', this, aw, ah, w, h);
22563         }
22564         return this;
22565     },
22566
22567     /**
22568      * Gets the current size of the component's underlying element.
22569      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22570      */
22571     getSize : function(){
22572         return this.el.getSize();
22573     },
22574
22575     /**
22576      * Gets the current XY position of the component's underlying element.
22577      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22578      * @return {Array} The XY position of the element (e.g., [100, 200])
22579      */
22580     getPosition : function(local){
22581         if(local === true){
22582             return [this.el.getLeft(true), this.el.getTop(true)];
22583         }
22584         return this.xy || this.el.getXY();
22585     },
22586
22587     /**
22588      * Gets the current box measurements of the component's underlying element.
22589      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22590      * @returns {Object} box An object in the format {x, y, width, height}
22591      */
22592     getBox : function(local){
22593         var s = this.el.getSize();
22594         if(local){
22595             s.x = this.el.getLeft(true);
22596             s.y = this.el.getTop(true);
22597         }else{
22598             var xy = this.xy || this.el.getXY();
22599             s.x = xy[0];
22600             s.y = xy[1];
22601         }
22602         return s;
22603     },
22604
22605     /**
22606      * Sets the current box measurements of the component's underlying element.
22607      * @param {Object} box An object in the format {x, y, width, height}
22608      * @returns {Roo.BoxComponent} this
22609      */
22610     updateBox : function(box){
22611         this.setSize(box.width, box.height);
22612         this.setPagePosition(box.x, box.y);
22613         return this;
22614     },
22615
22616     // protected
22617     getResizeEl : function(){
22618         return this.resizeEl || this.el;
22619     },
22620
22621     // protected
22622     getPositionEl : function(){
22623         return this.positionEl || this.el;
22624     },
22625
22626     /**
22627      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22628      * This method fires the move event.
22629      * @param {Number} left The new left
22630      * @param {Number} top The new top
22631      * @returns {Roo.BoxComponent} this
22632      */
22633     setPosition : function(x, y){
22634         this.x = x;
22635         this.y = y;
22636         if(!this.boxReady){
22637             return this;
22638         }
22639         var adj = this.adjustPosition(x, y);
22640         var ax = adj.x, ay = adj.y;
22641
22642         var el = this.getPositionEl();
22643         if(ax !== undefined || ay !== undefined){
22644             if(ax !== undefined && ay !== undefined){
22645                 el.setLeftTop(ax, ay);
22646             }else if(ax !== undefined){
22647                 el.setLeft(ax);
22648             }else if(ay !== undefined){
22649                 el.setTop(ay);
22650             }
22651             this.onPosition(ax, ay);
22652             this.fireEvent('move', this, ax, ay);
22653         }
22654         return this;
22655     },
22656
22657     /**
22658      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22659      * This method fires the move event.
22660      * @param {Number} x The new x position
22661      * @param {Number} y The new y position
22662      * @returns {Roo.BoxComponent} this
22663      */
22664     setPagePosition : function(x, y){
22665         this.pageX = x;
22666         this.pageY = y;
22667         if(!this.boxReady){
22668             return;
22669         }
22670         if(x === undefined || y === undefined){ // cannot translate undefined points
22671             return;
22672         }
22673         var p = this.el.translatePoints(x, y);
22674         this.setPosition(p.left, p.top);
22675         return this;
22676     },
22677
22678     // private
22679     onRender : function(ct, position){
22680         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22681         if(this.resizeEl){
22682             this.resizeEl = Roo.get(this.resizeEl);
22683         }
22684         if(this.positionEl){
22685             this.positionEl = Roo.get(this.positionEl);
22686         }
22687     },
22688
22689     // private
22690     afterRender : function(){
22691         Roo.BoxComponent.superclass.afterRender.call(this);
22692         this.boxReady = true;
22693         this.setSize(this.width, this.height);
22694         if(this.x || this.y){
22695             this.setPosition(this.x, this.y);
22696         }
22697         if(this.pageX || this.pageY){
22698             this.setPagePosition(this.pageX, this.pageY);
22699         }
22700     },
22701
22702     /**
22703      * Force the component's size to recalculate based on the underlying element's current height and width.
22704      * @returns {Roo.BoxComponent} this
22705      */
22706     syncSize : function(){
22707         delete this.lastSize;
22708         this.setSize(this.el.getWidth(), this.el.getHeight());
22709         return this;
22710     },
22711
22712     /**
22713      * Called after the component is resized, this method is empty by default but can be implemented by any
22714      * subclass that needs to perform custom logic after a resize occurs.
22715      * @param {Number} adjWidth The box-adjusted width that was set
22716      * @param {Number} adjHeight The box-adjusted height that was set
22717      * @param {Number} rawWidth The width that was originally specified
22718      * @param {Number} rawHeight The height that was originally specified
22719      */
22720     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22721
22722     },
22723
22724     /**
22725      * Called after the component is moved, this method is empty by default but can be implemented by any
22726      * subclass that needs to perform custom logic after a move occurs.
22727      * @param {Number} x The new x position
22728      * @param {Number} y The new y position
22729      */
22730     onPosition : function(x, y){
22731
22732     },
22733
22734     // private
22735     adjustSize : function(w, h){
22736         if(this.autoWidth){
22737             w = 'auto';
22738         }
22739         if(this.autoHeight){
22740             h = 'auto';
22741         }
22742         return {width : w, height: h};
22743     },
22744
22745     // private
22746     adjustPosition : function(x, y){
22747         return {x : x, y: y};
22748     }
22749 });/*
22750  * Based on:
22751  * Ext JS Library 1.1.1
22752  * Copyright(c) 2006-2007, Ext JS, LLC.
22753  *
22754  * Originally Released Under LGPL - original licence link has changed is not relivant.
22755  *
22756  * Fork - LGPL
22757  * <script type="text/javascript">
22758  */
22759
22760
22761 /**
22762  * @class Roo.SplitBar
22763  * @extends Roo.util.Observable
22764  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22765  * <br><br>
22766  * Usage:
22767  * <pre><code>
22768 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22769                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22770 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22771 split.minSize = 100;
22772 split.maxSize = 600;
22773 split.animate = true;
22774 split.on('moved', splitterMoved);
22775 </code></pre>
22776  * @constructor
22777  * Create a new SplitBar
22778  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22779  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22780  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22781  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22782                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22783                         position of the SplitBar).
22784  */
22785 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22786     
22787     /** @private */
22788     this.el = Roo.get(dragElement, true);
22789     this.el.dom.unselectable = "on";
22790     /** @private */
22791     this.resizingEl = Roo.get(resizingElement, true);
22792
22793     /**
22794      * @private
22795      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22797      * @type Number
22798      */
22799     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22800     
22801     /**
22802      * The minimum size of the resizing element. (Defaults to 0)
22803      * @type Number
22804      */
22805     this.minSize = 0;
22806     
22807     /**
22808      * The maximum size of the resizing element. (Defaults to 2000)
22809      * @type Number
22810      */
22811     this.maxSize = 2000;
22812     
22813     /**
22814      * Whether to animate the transition to the new size
22815      * @type Boolean
22816      */
22817     this.animate = false;
22818     
22819     /**
22820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22821      * @type Boolean
22822      */
22823     this.useShim = false;
22824     
22825     /** @private */
22826     this.shim = null;
22827     
22828     if(!existingProxy){
22829         /** @private */
22830         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22831     }else{
22832         this.proxy = Roo.get(existingProxy).dom;
22833     }
22834     /** @private */
22835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22836     
22837     /** @private */
22838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22839     
22840     /** @private */
22841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22842     
22843     /** @private */
22844     this.dragSpecs = {};
22845     
22846     /**
22847      * @private The adapter to use to positon and resize elements
22848      */
22849     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22850     this.adapter.init(this);
22851     
22852     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22853         /** @private */
22854         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22855         this.el.addClass("x-splitbar-h");
22856     }else{
22857         /** @private */
22858         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22859         this.el.addClass("x-splitbar-v");
22860     }
22861     
22862     this.addEvents({
22863         /**
22864          * @event resize
22865          * Fires when the splitter is moved (alias for {@link #event-moved})
22866          * @param {Roo.SplitBar} this
22867          * @param {Number} newSize the new width or height
22868          */
22869         "resize" : true,
22870         /**
22871          * @event moved
22872          * Fires when the splitter is moved
22873          * @param {Roo.SplitBar} this
22874          * @param {Number} newSize the new width or height
22875          */
22876         "moved" : true,
22877         /**
22878          * @event beforeresize
22879          * Fires before the splitter is dragged
22880          * @param {Roo.SplitBar} this
22881          */
22882         "beforeresize" : true,
22883
22884         "beforeapply" : true
22885     });
22886
22887     Roo.util.Observable.call(this);
22888 };
22889
22890 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22891     onStartProxyDrag : function(x, y){
22892         this.fireEvent("beforeresize", this);
22893         if(!this.overlay){
22894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22895             o.unselectable();
22896             o.enableDisplayMode("block");
22897             // all splitbars share the same overlay
22898             Roo.SplitBar.prototype.overlay = o;
22899         }
22900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22901         this.overlay.show();
22902         Roo.get(this.proxy).setDisplayed("block");
22903         var size = this.adapter.getElementSize(this);
22904         this.activeMinSize = this.getMinimumSize();;
22905         this.activeMaxSize = this.getMaximumSize();;
22906         var c1 = size - this.activeMinSize;
22907         var c2 = Math.max(this.activeMaxSize - size, 0);
22908         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22909             this.dd.resetConstraints();
22910             this.dd.setXConstraint(
22911                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22912                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22913             );
22914             this.dd.setYConstraint(0, 0);
22915         }else{
22916             this.dd.resetConstraints();
22917             this.dd.setXConstraint(0, 0);
22918             this.dd.setYConstraint(
22919                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22920                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22921             );
22922          }
22923         this.dragSpecs.startSize = size;
22924         this.dragSpecs.startPoint = [x, y];
22925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22926     },
22927     
22928     /** 
22929      * @private Called after the drag operation by the DDProxy
22930      */
22931     onEndProxyDrag : function(e){
22932         Roo.get(this.proxy).setDisplayed(false);
22933         var endPoint = Roo.lib.Event.getXY(e);
22934         if(this.overlay){
22935             this.overlay.hide();
22936         }
22937         var newSize;
22938         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22939             newSize = this.dragSpecs.startSize + 
22940                 (this.placement == Roo.SplitBar.LEFT ?
22941                     endPoint[0] - this.dragSpecs.startPoint[0] :
22942                     this.dragSpecs.startPoint[0] - endPoint[0]
22943                 );
22944         }else{
22945             newSize = this.dragSpecs.startSize + 
22946                 (this.placement == Roo.SplitBar.TOP ?
22947                     endPoint[1] - this.dragSpecs.startPoint[1] :
22948                     this.dragSpecs.startPoint[1] - endPoint[1]
22949                 );
22950         }
22951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22952         if(newSize != this.dragSpecs.startSize){
22953             if(this.fireEvent('beforeapply', this, newSize) !== false){
22954                 this.adapter.setElementSize(this, newSize);
22955                 this.fireEvent("moved", this, newSize);
22956                 this.fireEvent("resize", this, newSize);
22957             }
22958         }
22959     },
22960     
22961     /**
22962      * Get the adapter this SplitBar uses
22963      * @return The adapter object
22964      */
22965     getAdapter : function(){
22966         return this.adapter;
22967     },
22968     
22969     /**
22970      * Set the adapter this SplitBar uses
22971      * @param {Object} adapter A SplitBar adapter object
22972      */
22973     setAdapter : function(adapter){
22974         this.adapter = adapter;
22975         this.adapter.init(this);
22976     },
22977     
22978     /**
22979      * Gets the minimum size for the resizing element
22980      * @return {Number} The minimum size
22981      */
22982     getMinimumSize : function(){
22983         return this.minSize;
22984     },
22985     
22986     /**
22987      * Sets the minimum size for the resizing element
22988      * @param {Number} minSize The minimum size
22989      */
22990     setMinimumSize : function(minSize){
22991         this.minSize = minSize;
22992     },
22993     
22994     /**
22995      * Gets the maximum size for the resizing element
22996      * @return {Number} The maximum size
22997      */
22998     getMaximumSize : function(){
22999         return this.maxSize;
23000     },
23001     
23002     /**
23003      * Sets the maximum size for the resizing element
23004      * @param {Number} maxSize The maximum size
23005      */
23006     setMaximumSize : function(maxSize){
23007         this.maxSize = maxSize;
23008     },
23009     
23010     /**
23011      * Sets the initialize size for the resizing element
23012      * @param {Number} size The initial size
23013      */
23014     setCurrentSize : function(size){
23015         var oldAnimate = this.animate;
23016         this.animate = false;
23017         this.adapter.setElementSize(this, size);
23018         this.animate = oldAnimate;
23019     },
23020     
23021     /**
23022      * Destroy this splitbar. 
23023      * @param {Boolean} removeEl True to remove the element
23024      */
23025     destroy : function(removeEl){
23026         if(this.shim){
23027             this.shim.remove();
23028         }
23029         this.dd.unreg();
23030         this.proxy.parentNode.removeChild(this.proxy);
23031         if(removeEl){
23032             this.el.remove();
23033         }
23034     }
23035 });
23036
23037 /**
23038  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23039  */
23040 Roo.SplitBar.createProxy = function(dir){
23041     var proxy = new Roo.Element(document.createElement("div"));
23042     proxy.unselectable();
23043     var cls = 'x-splitbar-proxy';
23044     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23045     document.body.appendChild(proxy.dom);
23046     return proxy.dom;
23047 };
23048
23049 /** 
23050  * @class Roo.SplitBar.BasicLayoutAdapter
23051  * Default Adapter. It assumes the splitter and resizing element are not positioned
23052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23053  */
23054 Roo.SplitBar.BasicLayoutAdapter = function(){
23055 };
23056
23057 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23058     // do nothing for now
23059     init : function(s){
23060     
23061     },
23062     /**
23063      * Called before drag operations to get the current size of the resizing element. 
23064      * @param {Roo.SplitBar} s The SplitBar using this adapter
23065      */
23066      getElementSize : function(s){
23067         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23068             return s.resizingEl.getWidth();
23069         }else{
23070             return s.resizingEl.getHeight();
23071         }
23072     },
23073     
23074     /**
23075      * Called after drag operations to set the size of the resizing element.
23076      * @param {Roo.SplitBar} s The SplitBar using this adapter
23077      * @param {Number} newSize The new size to set
23078      * @param {Function} onComplete A function to be invoked when resizing is complete
23079      */
23080     setElementSize : function(s, newSize, onComplete){
23081         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23082             if(!s.animate){
23083                 s.resizingEl.setWidth(newSize);
23084                 if(onComplete){
23085                     onComplete(s, newSize);
23086                 }
23087             }else{
23088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23089             }
23090         }else{
23091             
23092             if(!s.animate){
23093                 s.resizingEl.setHeight(newSize);
23094                 if(onComplete){
23095                     onComplete(s, newSize);
23096                 }
23097             }else{
23098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23099             }
23100         }
23101     }
23102 };
23103
23104 /** 
23105  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23106  * @extends Roo.SplitBar.BasicLayoutAdapter
23107  * Adapter that  moves the splitter element to align with the resized sizing element. 
23108  * Used with an absolute positioned SplitBar.
23109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23110  * document.body, make sure you assign an id to the body element.
23111  */
23112 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23113     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23114     this.container = Roo.get(container);
23115 };
23116
23117 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23118     init : function(s){
23119         this.basic.init(s);
23120     },
23121     
23122     getElementSize : function(s){
23123         return this.basic.getElementSize(s);
23124     },
23125     
23126     setElementSize : function(s, newSize, onComplete){
23127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23128     },
23129     
23130     moveSplitter : function(s){
23131         var yes = Roo.SplitBar;
23132         switch(s.placement){
23133             case yes.LEFT:
23134                 s.el.setX(s.resizingEl.getRight());
23135                 break;
23136             case yes.RIGHT:
23137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23138                 break;
23139             case yes.TOP:
23140                 s.el.setY(s.resizingEl.getBottom());
23141                 break;
23142             case yes.BOTTOM:
23143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23144                 break;
23145         }
23146     }
23147 };
23148
23149 /**
23150  * Orientation constant - Create a vertical SplitBar
23151  * @static
23152  * @type Number
23153  */
23154 Roo.SplitBar.VERTICAL = 1;
23155
23156 /**
23157  * Orientation constant - Create a horizontal SplitBar
23158  * @static
23159  * @type Number
23160  */
23161 Roo.SplitBar.HORIZONTAL = 2;
23162
23163 /**
23164  * Placement constant - The resizing element is to the left of the splitter element
23165  * @static
23166  * @type Number
23167  */
23168 Roo.SplitBar.LEFT = 1;
23169
23170 /**
23171  * Placement constant - The resizing element is to the right of the splitter element
23172  * @static
23173  * @type Number
23174  */
23175 Roo.SplitBar.RIGHT = 2;
23176
23177 /**
23178  * Placement constant - The resizing element is positioned above the splitter element
23179  * @static
23180  * @type Number
23181  */
23182 Roo.SplitBar.TOP = 3;
23183
23184 /**
23185  * Placement constant - The resizing element is positioned under splitter element
23186  * @static
23187  * @type Number
23188  */
23189 Roo.SplitBar.BOTTOM = 4;
23190 /*
23191  * Based on:
23192  * Ext JS Library 1.1.1
23193  * Copyright(c) 2006-2007, Ext JS, LLC.
23194  *
23195  * Originally Released Under LGPL - original licence link has changed is not relivant.
23196  *
23197  * Fork - LGPL
23198  * <script type="text/javascript">
23199  */
23200
23201 /**
23202  * @class Roo.View
23203  * @extends Roo.util.Observable
23204  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23205  * This class also supports single and multi selection modes. <br>
23206  * Create a data model bound view:
23207  <pre><code>
23208  var store = new Roo.data.Store(...);
23209
23210  var view = new Roo.View("my-element",
23211  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23212  {
23213  singleSelect: true,
23214  selectedClass: "ydataview-selected",
23215  store: store
23216  });
23217
23218  // listen for node click?
23219  view.on("click", function(vw, index, node, e){
23220  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23221  });
23222
23223  // load XML data
23224  dataModel.load("foobar.xml");
23225  </code></pre>
23226  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23227  * <br><br>
23228  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23229  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23230  * @constructor
23231  * Create a new View
23232  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23233  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
23234  * @param {Object} config The config object
23235  */
23236 Roo.View = function(container, tpl, config){
23237     this.el = Roo.get(container);
23238     if(typeof tpl == "string"){
23239         tpl = new Roo.Template(tpl);
23240     }
23241     tpl.compile();
23242     /**
23243      * The template used by this View
23244      * @type {Roo.DomHelper.Template}
23245      */
23246     this.tpl = tpl;
23247
23248     Roo.apply(this, config);
23249
23250     /** @private */
23251     this.addEvents({
23252     /**
23253      * @event beforeclick
23254      * Fires before a click is processed. Returns false to cancel the default action.
23255      * @param {Roo.View} this
23256      * @param {Number} index The index of the target node
23257      * @param {HTMLElement} node The target node
23258      * @param {Roo.EventObject} e The raw event object
23259      */
23260         "beforeclick" : true,
23261     /**
23262      * @event click
23263      * Fires when a template node is clicked.
23264      * @param {Roo.View} this
23265      * @param {Number} index The index of the target node
23266      * @param {HTMLElement} node The target node
23267      * @param {Roo.EventObject} e The raw event object
23268      */
23269         "click" : true,
23270     /**
23271      * @event dblclick
23272      * Fires when a template node is double clicked.
23273      * @param {Roo.View} this
23274      * @param {Number} index The index of the target node
23275      * @param {HTMLElement} node The target node
23276      * @param {Roo.EventObject} e The raw event object
23277      */
23278         "dblclick" : true,
23279     /**
23280      * @event contextmenu
23281      * Fires when a template node is right clicked.
23282      * @param {Roo.View} this
23283      * @param {Number} index The index of the target node
23284      * @param {HTMLElement} node The target node
23285      * @param {Roo.EventObject} e The raw event object
23286      */
23287         "contextmenu" : true,
23288     /**
23289      * @event selectionchange
23290      * Fires when the selected nodes change.
23291      * @param {Roo.View} this
23292      * @param {Array} selections Array of the selected nodes
23293      */
23294         "selectionchange" : true,
23295
23296     /**
23297      * @event beforeselect
23298      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23299      * @param {Roo.View} this
23300      * @param {HTMLElement} node The node to be selected
23301      * @param {Array} selections Array of currently selected nodes
23302      */
23303         "beforeselect" : true
23304     });
23305
23306     this.el.on({
23307         "click": this.onClick,
23308         "dblclick": this.onDblClick,
23309         "contextmenu": this.onContextMenu,
23310         scope:this
23311     });
23312
23313     this.selections = [];
23314     this.nodes = [];
23315     this.cmp = new Roo.CompositeElementLite([]);
23316     if(this.store){
23317         this.store = Roo.factory(this.store, Roo.data);
23318         this.setStore(this.store, true);
23319     }
23320     Roo.View.superclass.constructor.call(this);
23321 };
23322
23323 Roo.extend(Roo.View, Roo.util.Observable, {
23324     /**
23325      * The css class to add to selected nodes
23326      * @type {Roo.DomHelper.Template}
23327      */
23328     selectedClass : "x-view-selected",
23329     
23330     emptyText : "",
23331     /**
23332      * Returns the element this view is bound to.
23333      * @return {Roo.Element}
23334      */
23335     getEl : function(){
23336         return this.el;
23337     },
23338
23339     /**
23340      * Refreshes the view.
23341      */
23342     refresh : function(){
23343         var t = this.tpl;
23344         this.clearSelections();
23345         this.el.update("");
23346         var html = [];
23347         var records = this.store.getRange();
23348         if(records.length < 1){
23349             this.el.update(this.emptyText);
23350             return;
23351         }
23352         for(var i = 0, len = records.length; i < len; i++){
23353             var data = this.prepareData(records[i].data, i, records[i]);
23354             html[html.length] = t.apply(data);
23355         }
23356         this.el.update(html.join(""));
23357         this.nodes = this.el.dom.childNodes;
23358         this.updateIndexes(0);
23359     },
23360
23361     /**
23362      * Function to override to reformat the data that is sent to
23363      * the template for each node.
23364      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23365      * a JSON object for an UpdateManager bound view).
23366      */
23367     prepareData : function(data){
23368         return data;
23369     },
23370
23371     onUpdate : function(ds, record){
23372         this.clearSelections();
23373         var index = this.store.indexOf(record);
23374         var n = this.nodes[index];
23375         this.tpl.insertBefore(n, this.prepareData(record.data));
23376         n.parentNode.removeChild(n);
23377         this.updateIndexes(index, index);
23378     },
23379
23380     onAdd : function(ds, records, index){
23381         this.clearSelections();
23382         if(this.nodes.length == 0){
23383             this.refresh();
23384             return;
23385         }
23386         var n = this.nodes[index];
23387         for(var i = 0, len = records.length; i < len; i++){
23388             var d = this.prepareData(records[i].data);
23389             if(n){
23390                 this.tpl.insertBefore(n, d);
23391             }else{
23392                 this.tpl.append(this.el, d);
23393             }
23394         }
23395         this.updateIndexes(index);
23396     },
23397
23398     onRemove : function(ds, record, index){
23399         this.clearSelections();
23400         this.el.dom.removeChild(this.nodes[index]);
23401         this.updateIndexes(index);
23402     },
23403
23404     /**
23405      * Refresh an individual node.
23406      * @param {Number} index
23407      */
23408     refreshNode : function(index){
23409         this.onUpdate(this.store, this.store.getAt(index));
23410     },
23411
23412     updateIndexes : function(startIndex, endIndex){
23413         var ns = this.nodes;
23414         startIndex = startIndex || 0;
23415         endIndex = endIndex || ns.length - 1;
23416         for(var i = startIndex; i <= endIndex; i++){
23417             ns[i].nodeIndex = i;
23418         }
23419     },
23420
23421     /**
23422      * Changes the data store this view uses and refresh the view.
23423      * @param {Store} store
23424      */
23425     setStore : function(store, initial){
23426         if(!initial && this.store){
23427             this.store.un("datachanged", this.refresh);
23428             this.store.un("add", this.onAdd);
23429             this.store.un("remove", this.onRemove);
23430             this.store.un("update", this.onUpdate);
23431             this.store.un("clear", this.refresh);
23432         }
23433         if(store){
23434           
23435             store.on("datachanged", this.refresh, this);
23436             store.on("add", this.onAdd, this);
23437             store.on("remove", this.onRemove, this);
23438             store.on("update", this.onUpdate, this);
23439             store.on("clear", this.refresh, this);
23440         }
23441         
23442         if(store){
23443             this.refresh();
23444         }
23445     },
23446
23447     /**
23448      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23449      * @param {HTMLElement} node
23450      * @return {HTMLElement} The template node
23451      */
23452     findItemFromChild : function(node){
23453         var el = this.el.dom;
23454         if(!node || node.parentNode == el){
23455                     return node;
23456             }
23457             var p = node.parentNode;
23458             while(p && p != el){
23459             if(p.parentNode == el){
23460                 return p;
23461             }
23462             p = p.parentNode;
23463         }
23464             return null;
23465     },
23466
23467     /** @ignore */
23468     onClick : function(e){
23469         var item = this.findItemFromChild(e.getTarget());
23470         if(item){
23471             var index = this.indexOf(item);
23472             if(this.onItemClick(item, index, e) !== false){
23473                 this.fireEvent("click", this, index, item, e);
23474             }
23475         }else{
23476             this.clearSelections();
23477         }
23478     },
23479
23480     /** @ignore */
23481     onContextMenu : function(e){
23482         var item = this.findItemFromChild(e.getTarget());
23483         if(item){
23484             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23485         }
23486     },
23487
23488     /** @ignore */
23489     onDblClick : function(e){
23490         var item = this.findItemFromChild(e.getTarget());
23491         if(item){
23492             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23493         }
23494     },
23495
23496     onItemClick : function(item, index, e){
23497         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23498             return false;
23499         }
23500         if(this.multiSelect || this.singleSelect){
23501             if(this.multiSelect && e.shiftKey && this.lastSelection){
23502                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23503             }else{
23504                 this.select(item, this.multiSelect && e.ctrlKey);
23505                 this.lastSelection = item;
23506             }
23507             e.preventDefault();
23508         }
23509         return true;
23510     },
23511
23512     /**
23513      * Get the number of selected nodes.
23514      * @return {Number}
23515      */
23516     getSelectionCount : function(){
23517         return this.selections.length;
23518     },
23519
23520     /**
23521      * Get the currently selected nodes.
23522      * @return {Array} An array of HTMLElements
23523      */
23524     getSelectedNodes : function(){
23525         return this.selections;
23526     },
23527
23528     /**
23529      * Get the indexes of the selected nodes.
23530      * @return {Array}
23531      */
23532     getSelectedIndexes : function(){
23533         var indexes = [], s = this.selections;
23534         for(var i = 0, len = s.length; i < len; i++){
23535             indexes.push(s[i].nodeIndex);
23536         }
23537         return indexes;
23538     },
23539
23540     /**
23541      * Clear all selections
23542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23543      */
23544     clearSelections : function(suppressEvent){
23545         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23546             this.cmp.elements = this.selections;
23547             this.cmp.removeClass(this.selectedClass);
23548             this.selections = [];
23549             if(!suppressEvent){
23550                 this.fireEvent("selectionchange", this, this.selections);
23551             }
23552         }
23553     },
23554
23555     /**
23556      * Returns true if the passed node is selected
23557      * @param {HTMLElement/Number} node The node or node index
23558      * @return {Boolean}
23559      */
23560     isSelected : function(node){
23561         var s = this.selections;
23562         if(s.length < 1){
23563             return false;
23564         }
23565         node = this.getNode(node);
23566         return s.indexOf(node) !== -1;
23567     },
23568
23569     /**
23570      * Selects nodes.
23571      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
23572      * @param {Boolean} keepExisting (optional) true to keep existing selections
23573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23574      */
23575     select : function(nodeInfo, keepExisting, suppressEvent){
23576         if(nodeInfo instanceof Array){
23577             if(!keepExisting){
23578                 this.clearSelections(true);
23579             }
23580             for(var i = 0, len = nodeInfo.length; i < len; i++){
23581                 this.select(nodeInfo[i], true, true);
23582             }
23583         } else{
23584             var node = this.getNode(nodeInfo);
23585             if(node && !this.isSelected(node)){
23586                 if(!keepExisting){
23587                     this.clearSelections(true);
23588                 }
23589                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23590                     Roo.fly(node).addClass(this.selectedClass);
23591                     this.selections.push(node);
23592                     if(!suppressEvent){
23593                         this.fireEvent("selectionchange", this, this.selections);
23594                     }
23595                 }
23596             }
23597         }
23598     },
23599
23600     /**
23601      * Gets a template node.
23602      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23603      * @return {HTMLElement} The node or null if it wasn't found
23604      */
23605     getNode : function(nodeInfo){
23606         if(typeof nodeInfo == "string"){
23607             return document.getElementById(nodeInfo);
23608         }else if(typeof nodeInfo == "number"){
23609             return this.nodes[nodeInfo];
23610         }
23611         return nodeInfo;
23612     },
23613
23614     /**
23615      * Gets a range template nodes.
23616      * @param {Number} startIndex
23617      * @param {Number} endIndex
23618      * @return {Array} An array of nodes
23619      */
23620     getNodes : function(start, end){
23621         var ns = this.nodes;
23622         start = start || 0;
23623         end = typeof end == "undefined" ? ns.length - 1 : end;
23624         var nodes = [];
23625         if(start <= end){
23626             for(var i = start; i <= end; i++){
23627                 nodes.push(ns[i]);
23628             }
23629         } else{
23630             for(var i = start; i >= end; i--){
23631                 nodes.push(ns[i]);
23632             }
23633         }
23634         return nodes;
23635     },
23636
23637     /**
23638      * Finds the index of the passed node
23639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23640      * @return {Number} The index of the node or -1
23641      */
23642     indexOf : function(node){
23643         node = this.getNode(node);
23644         if(typeof node.nodeIndex == "number"){
23645             return node.nodeIndex;
23646         }
23647         var ns = this.nodes;
23648         for(var i = 0, len = ns.length; i < len; i++){
23649             if(ns[i] == node){
23650                 return i;
23651             }
23652         }
23653         return -1;
23654     }
23655 });
23656 /*
23657  * Based on:
23658  * Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  *
23661  * Originally Released Under LGPL - original licence link has changed is not relivant.
23662  *
23663  * Fork - LGPL
23664  * <script type="text/javascript">
23665  */
23666
23667 /**
23668  * @class Roo.JsonView
23669  * @extends Roo.View
23670  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23671 <pre><code>
23672 var view = new Roo.JsonView("my-element",
23673     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23674     { multiSelect: true, jsonRoot: "data" }
23675 );
23676
23677 // listen for node click?
23678 view.on("click", function(vw, index, node, e){
23679     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23680 });
23681
23682 // direct load of JSON data
23683 view.load("foobar.php");
23684
23685 // Example from my blog list
23686 var tpl = new Roo.Template(
23687     '&lt;div class="entry"&gt;' +
23688     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23689     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23690     "&lt;/div&gt;&lt;hr /&gt;"
23691 );
23692
23693 var moreView = new Roo.JsonView("entry-list", tpl, {
23694     jsonRoot: "posts"
23695 });
23696 moreView.on("beforerender", this.sortEntries, this);
23697 moreView.load({
23698     url: "/blog/get-posts.php",
23699     params: "allposts=true",
23700     text: "Loading Blog Entries..."
23701 });
23702 </code></pre>
23703  * @constructor
23704  * Create a new JsonView
23705  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23706  * @param {Template} tpl The rendering template
23707  * @param {Object} config The config object
23708  */
23709 Roo.JsonView = function(container, tpl, config){
23710     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
23711
23712     var um = this.el.getUpdateManager();
23713     um.setRenderer(this);
23714     um.on("update", this.onLoad, this);
23715     um.on("failure", this.onLoadException, this);
23716
23717     /**
23718      * @event beforerender
23719      * Fires before rendering of the downloaded JSON data.
23720      * @param {Roo.JsonView} this
23721      * @param {Object} data The JSON data loaded
23722      */
23723     /**
23724      * @event load
23725      * Fires when data is loaded.
23726      * @param {Roo.JsonView} this
23727      * @param {Object} data The JSON data loaded
23728      * @param {Object} response The raw Connect response object
23729      */
23730     /**
23731      * @event loadexception
23732      * Fires when loading fails.
23733      * @param {Roo.JsonView} this
23734      * @param {Object} response The raw Connect response object
23735      */
23736     this.addEvents({
23737         'beforerender' : true,
23738         'load' : true,
23739         'loadexception' : true
23740     });
23741 };
23742 Roo.extend(Roo.JsonView, Roo.View, {
23743     /**
23744      * The root property in the loaded JSON object that contains the data
23745      * @type {String}
23746      */
23747     jsonRoot : "",
23748
23749     /**
23750      * Refreshes the view.
23751      */
23752     refresh : function(){
23753         this.clearSelections();
23754         this.el.update("");
23755         var html = [];
23756         var o = this.jsonData;
23757         if(o && o.length > 0){
23758             for(var i = 0, len = o.length; i < len; i++){
23759                 var data = this.prepareData(o[i], i, o);
23760                 html[html.length] = this.tpl.apply(data);
23761             }
23762         }else{
23763             html.push(this.emptyText);
23764         }
23765         this.el.update(html.join(""));
23766         this.nodes = this.el.dom.childNodes;
23767         this.updateIndexes(0);
23768     },
23769
23770     /**
23771      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
23772      * @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:
23773      <pre><code>
23774      view.load({
23775          url: "your-url.php",
23776          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23777          callback: yourFunction,
23778          scope: yourObject, //(optional scope)
23779          discardUrl: false,
23780          nocache: false,
23781          text: "Loading...",
23782          timeout: 30,
23783          scripts: false
23784      });
23785      </code></pre>
23786      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23787      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
23788      * @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}
23789      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23790      * @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.
23791      */
23792     load : function(){
23793         var um = this.el.getUpdateManager();
23794         um.update.apply(um, arguments);
23795     },
23796
23797     render : function(el, response){
23798         this.clearSelections();
23799         this.el.update("");
23800         var o;
23801         try{
23802             o = Roo.util.JSON.decode(response.responseText);
23803             if(this.jsonRoot){
23804                 
23805                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23806             }
23807         } catch(e){
23808         }
23809         /**
23810          * The current JSON data or null
23811          */
23812         this.jsonData = o;
23813         this.beforeRender();
23814         this.refresh();
23815     },
23816
23817 /**
23818  * Get the number of records in the current JSON dataset
23819  * @return {Number}
23820  */
23821     getCount : function(){
23822         return this.jsonData ? this.jsonData.length : 0;
23823     },
23824
23825 /**
23826  * Returns the JSON object for the specified node(s)
23827  * @param {HTMLElement/Array} node The node or an array of nodes
23828  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23829  * you get the JSON object for the node
23830  */
23831     getNodeData : function(node){
23832         if(node instanceof Array){
23833             var data = [];
23834             for(var i = 0, len = node.length; i < len; i++){
23835                 data.push(this.getNodeData(node[i]));
23836             }
23837             return data;
23838         }
23839         return this.jsonData[this.indexOf(node)] || null;
23840     },
23841
23842     beforeRender : function(){
23843         this.snapshot = this.jsonData;
23844         if(this.sortInfo){
23845             this.sort.apply(this, this.sortInfo);
23846         }
23847         this.fireEvent("beforerender", this, this.jsonData);
23848     },
23849
23850     onLoad : function(el, o){
23851         this.fireEvent("load", this, this.jsonData, o);
23852     },
23853
23854     onLoadException : function(el, o){
23855         this.fireEvent("loadexception", this, o);
23856     },
23857
23858 /**
23859  * Filter the data by a specific property.
23860  * @param {String} property A property on your JSON objects
23861  * @param {String/RegExp} value Either string that the property values
23862  * should start with, or a RegExp to test against the property
23863  */
23864     filter : function(property, value){
23865         if(this.jsonData){
23866             var data = [];
23867             var ss = this.snapshot;
23868             if(typeof value == "string"){
23869                 var vlen = value.length;
23870                 if(vlen == 0){
23871                     this.clearFilter();
23872                     return;
23873                 }
23874                 value = value.toLowerCase();
23875                 for(var i = 0, len = ss.length; i < len; i++){
23876                     var o = ss[i];
23877                     if(o[property].substr(0, vlen).toLowerCase() == value){
23878                         data.push(o);
23879                     }
23880                 }
23881             } else if(value.exec){ // regex?
23882                 for(var i = 0, len = ss.length; i < len; i++){
23883                     var o = ss[i];
23884                     if(value.test(o[property])){
23885                         data.push(o);
23886                     }
23887                 }
23888             } else{
23889                 return;
23890             }
23891             this.jsonData = data;
23892             this.refresh();
23893         }
23894     },
23895
23896 /**
23897  * Filter by a function. The passed function will be called with each
23898  * object in the current dataset. If the function returns true the value is kept,
23899  * otherwise it is filtered.
23900  * @param {Function} fn
23901  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23902  */
23903     filterBy : function(fn, scope){
23904         if(this.jsonData){
23905             var data = [];
23906             var ss = this.snapshot;
23907             for(var i = 0, len = ss.length; i < len; i++){
23908                 var o = ss[i];
23909                 if(fn.call(scope || this, o)){
23910                     data.push(o);
23911                 }
23912             }
23913             this.jsonData = data;
23914             this.refresh();
23915         }
23916     },
23917
23918 /**
23919  * Clears the current filter.
23920  */
23921     clearFilter : function(){
23922         if(this.snapshot && this.jsonData != this.snapshot){
23923             this.jsonData = this.snapshot;
23924             this.refresh();
23925         }
23926     },
23927
23928
23929 /**
23930  * Sorts the data for this view and refreshes it.
23931  * @param {String} property A property on your JSON objects to sort on
23932  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
23933  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
23934  */
23935     sort : function(property, dir, sortType){
23936         this.sortInfo = Array.prototype.slice.call(arguments, 0);
23937         if(this.jsonData){
23938             var p = property;
23939             var dsc = dir && dir.toLowerCase() == "desc";
23940             var f = function(o1, o2){
23941                 var v1 = sortType ? sortType(o1[p]) : o1[p];
23942                 var v2 = sortType ? sortType(o2[p]) : o2[p];
23943                 ;
23944                 if(v1 < v2){
23945                     return dsc ? +1 : -1;
23946                 } else if(v1 > v2){
23947                     return dsc ? -1 : +1;
23948                 } else{
23949                     return 0;
23950                 }
23951             };
23952             this.jsonData.sort(f);
23953             this.refresh();
23954             if(this.jsonData != this.snapshot){
23955                 this.snapshot.sort(f);
23956             }
23957         }
23958     }
23959 });/*
23960  * Based on:
23961  * Ext JS Library 1.1.1
23962  * Copyright(c) 2006-2007, Ext JS, LLC.
23963  *
23964  * Originally Released Under LGPL - original licence link has changed is not relivant.
23965  *
23966  * Fork - LGPL
23967  * <script type="text/javascript">
23968  */
23969  
23970
23971 /**
23972  * @class Roo.ColorPalette
23973  * @extends Roo.Component
23974  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
23975  * Here's an example of typical usage:
23976  * <pre><code>
23977 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
23978 cp.render('my-div');
23979
23980 cp.on('select', function(palette, selColor){
23981     // do something with selColor
23982 });
23983 </code></pre>
23984  * @constructor
23985  * Create a new ColorPalette
23986  * @param {Object} config The config object
23987  */
23988 Roo.ColorPalette = function(config){
23989     Roo.ColorPalette.superclass.constructor.call(this, config);
23990     this.addEvents({
23991         /**
23992              * @event select
23993              * Fires when a color is selected
23994              * @param {ColorPalette} this
23995              * @param {String} color The 6-digit color hex code (without the # symbol)
23996              */
23997         select: true
23998     });
23999
24000     if(this.handler){
24001         this.on("select", this.handler, this.scope, true);
24002     }
24003 };
24004 Roo.extend(Roo.ColorPalette, Roo.Component, {
24005     /**
24006      * @cfg {String} itemCls
24007      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24008      */
24009     itemCls : "x-color-palette",
24010     /**
24011      * @cfg {String} value
24012      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24013      * the hex codes are case-sensitive.
24014      */
24015     value : null,
24016     clickEvent:'click',
24017     // private
24018     ctype: "Roo.ColorPalette",
24019
24020     /**
24021      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24022      */
24023     allowReselect : false,
24024
24025     /**
24026      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24027      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24028      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24029      * of colors with the width setting until the box is symmetrical.</p>
24030      * <p>You can override individual colors if needed:</p>
24031      * <pre><code>
24032 var cp = new Roo.ColorPalette();
24033 cp.colors[0] = "FF0000";  // change the first box to red
24034 </code></pre>
24035
24036 Or you can provide a custom array of your own for complete control:
24037 <pre><code>
24038 var cp = new Roo.ColorPalette();
24039 cp.colors = ["000000", "993300", "333300"];
24040 </code></pre>
24041      * @type Array
24042      */
24043     colors : [
24044         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24045         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24046         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24047         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24048         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24049     ],
24050
24051     // private
24052     onRender : function(container, position){
24053         var t = new Roo.MasterTemplate(
24054             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24055         );
24056         var c = this.colors;
24057         for(var i = 0, len = c.length; i < len; i++){
24058             t.add([c[i]]);
24059         }
24060         var el = document.createElement("div");
24061         el.className = this.itemCls;
24062         t.overwrite(el);
24063         container.dom.insertBefore(el, position);
24064         this.el = Roo.get(el);
24065         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24066         if(this.clickEvent != 'click'){
24067             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24068         }
24069     },
24070
24071     // private
24072     afterRender : function(){
24073         Roo.ColorPalette.superclass.afterRender.call(this);
24074         if(this.value){
24075             var s = this.value;
24076             this.value = null;
24077             this.select(s);
24078         }
24079     },
24080
24081     // private
24082     handleClick : function(e, t){
24083         e.preventDefault();
24084         if(!this.disabled){
24085             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24086             this.select(c.toUpperCase());
24087         }
24088     },
24089
24090     /**
24091      * Selects the specified color in the palette (fires the select event)
24092      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24093      */
24094     select : function(color){
24095         color = color.replace("#", "");
24096         if(color != this.value || this.allowReselect){
24097             var el = this.el;
24098             if(this.value){
24099                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24100             }
24101             el.child("a.color-"+color).addClass("x-color-palette-sel");
24102             this.value = color;
24103             this.fireEvent("select", this, color);
24104         }
24105     }
24106 });/*
24107  * Based on:
24108  * Ext JS Library 1.1.1
24109  * Copyright(c) 2006-2007, Ext JS, LLC.
24110  *
24111  * Originally Released Under LGPL - original licence link has changed is not relivant.
24112  *
24113  * Fork - LGPL
24114  * <script type="text/javascript">
24115  */
24116  
24117 /**
24118  * @class Roo.DatePicker
24119  * @extends Roo.Component
24120  * Simple date picker class.
24121  * @constructor
24122  * Create a new DatePicker
24123  * @param {Object} config The config object
24124  */
24125 Roo.DatePicker = function(config){
24126     Roo.DatePicker.superclass.constructor.call(this, config);
24127
24128     this.value = config && config.value ?
24129                  config.value.clearTime() : new Date().clearTime();
24130
24131     this.addEvents({
24132         /**
24133              * @event select
24134              * Fires when a date is selected
24135              * @param {DatePicker} this
24136              * @param {Date} date The selected date
24137              */
24138         select: true
24139     });
24140
24141     if(this.handler){
24142         this.on("select", this.handler,  this.scope || this);
24143     }
24144     // build the disabledDatesRE
24145     if(!this.disabledDatesRE && this.disabledDates){
24146         var dd = this.disabledDates;
24147         var re = "(?:";
24148         for(var i = 0; i < dd.length; i++){
24149             re += dd[i];
24150             if(i != dd.length-1) re += "|";
24151         }
24152         this.disabledDatesRE = new RegExp(re + ")");
24153     }
24154 };
24155
24156 Roo.extend(Roo.DatePicker, Roo.Component, {
24157     /**
24158      * @cfg {String} todayText
24159      * The text to display on the button that selects the current date (defaults to "Today")
24160      */
24161     todayText : "Today",
24162     /**
24163      * @cfg {String} okText
24164      * The text to display on the ok button
24165      */
24166     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24167     /**
24168      * @cfg {String} cancelText
24169      * The text to display on the cancel button
24170      */
24171     cancelText : "Cancel",
24172     /**
24173      * @cfg {String} todayTip
24174      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24175      */
24176     todayTip : "{0} (Spacebar)",
24177     /**
24178      * @cfg {Date} minDate
24179      * Minimum allowable date (JavaScript date object, defaults to null)
24180      */
24181     minDate : null,
24182     /**
24183      * @cfg {Date} maxDate
24184      * Maximum allowable date (JavaScript date object, defaults to null)
24185      */
24186     maxDate : null,
24187     /**
24188      * @cfg {String} minText
24189      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24190      */
24191     minText : "This date is before the minimum date",
24192     /**
24193      * @cfg {String} maxText
24194      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24195      */
24196     maxText : "This date is after the maximum date",
24197     /**
24198      * @cfg {String} format
24199      * The default date format string which can be overriden for localization support.  The format must be
24200      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24201      */
24202     format : "m/d/y",
24203     /**
24204      * @cfg {Array} disabledDays
24205      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24206      */
24207     disabledDays : null,
24208     /**
24209      * @cfg {String} disabledDaysText
24210      * The tooltip to display when the date falls on a disabled day (defaults to "")
24211      */
24212     disabledDaysText : "",
24213     /**
24214      * @cfg {RegExp} disabledDatesRE
24215      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24216      */
24217     disabledDatesRE : null,
24218     /**
24219      * @cfg {String} disabledDatesText
24220      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24221      */
24222     disabledDatesText : "",
24223     /**
24224      * @cfg {Boolean} constrainToViewport
24225      * True to constrain the date picker to the viewport (defaults to true)
24226      */
24227     constrainToViewport : true,
24228     /**
24229      * @cfg {Array} monthNames
24230      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24231      */
24232     monthNames : Date.monthNames,
24233     /**
24234      * @cfg {Array} dayNames
24235      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24236      */
24237     dayNames : Date.dayNames,
24238     /**
24239      * @cfg {String} nextText
24240      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24241      */
24242     nextText: 'Next Month (Control+Right)',
24243     /**
24244      * @cfg {String} prevText
24245      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24246      */
24247     prevText: 'Previous Month (Control+Left)',
24248     /**
24249      * @cfg {String} monthYearText
24250      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24251      */
24252     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24253     /**
24254      * @cfg {Number} startDay
24255      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24256      */
24257     startDay : 0,
24258     /**
24259      * @cfg {Bool} showClear
24260      * Show a clear button (usefull for date form elements that can be blank.)
24261      */
24262     
24263     showClear: false,
24264     
24265     /**
24266      * Sets the value of the date field
24267      * @param {Date} value The date to set
24268      */
24269     setValue : function(value){
24270         var old = this.value;
24271         this.value = value.clearTime(true);
24272         if(this.el){
24273             this.update(this.value);
24274         }
24275     },
24276
24277     /**
24278      * Gets the current selected value of the date field
24279      * @return {Date} The selected date
24280      */
24281     getValue : function(){
24282         return this.value;
24283     },
24284
24285     // private
24286     focus : function(){
24287         if(this.el){
24288             this.update(this.activeDate);
24289         }
24290     },
24291
24292     // private
24293     onRender : function(container, position){
24294         var m = [
24295              '<table cellspacing="0">',
24296                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24297                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24298         var dn = this.dayNames;
24299         for(var i = 0; i < 7; i++){
24300             var d = this.startDay+i;
24301             if(d > 6){
24302                 d = d-7;
24303             }
24304             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24305         }
24306         m[m.length] = "</tr></thead><tbody><tr>";
24307         for(var i = 0; i < 42; i++) {
24308             if(i % 7 == 0 && i != 0){
24309                 m[m.length] = "</tr><tr>";
24310             }
24311             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24312         }
24313         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24314             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24315
24316         var el = document.createElement("div");
24317         el.className = "x-date-picker";
24318         el.innerHTML = m.join("");
24319
24320         container.dom.insertBefore(el, position);
24321
24322         this.el = Roo.get(el);
24323         this.eventEl = Roo.get(el.firstChild);
24324
24325         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24326             handler: this.showPrevMonth,
24327             scope: this,
24328             preventDefault:true,
24329             stopDefault:true
24330         });
24331
24332         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24333             handler: this.showNextMonth,
24334             scope: this,
24335             preventDefault:true,
24336             stopDefault:true
24337         });
24338
24339         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24340
24341         this.monthPicker = this.el.down('div.x-date-mp');
24342         this.monthPicker.enableDisplayMode('block');
24343         
24344         var kn = new Roo.KeyNav(this.eventEl, {
24345             "left" : function(e){
24346                 e.ctrlKey ?
24347                     this.showPrevMonth() :
24348                     this.update(this.activeDate.add("d", -1));
24349             },
24350
24351             "right" : function(e){
24352                 e.ctrlKey ?
24353                     this.showNextMonth() :
24354                     this.update(this.activeDate.add("d", 1));
24355             },
24356
24357             "up" : function(e){
24358                 e.ctrlKey ?
24359                     this.showNextYear() :
24360                     this.update(this.activeDate.add("d", -7));
24361             },
24362
24363             "down" : function(e){
24364                 e.ctrlKey ?
24365                     this.showPrevYear() :
24366                     this.update(this.activeDate.add("d", 7));
24367             },
24368
24369             "pageUp" : function(e){
24370                 this.showNextMonth();
24371             },
24372
24373             "pageDown" : function(e){
24374                 this.showPrevMonth();
24375             },
24376
24377             "enter" : function(e){
24378                 e.stopPropagation();
24379                 return true;
24380             },
24381
24382             scope : this
24383         });
24384
24385         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24386
24387         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24388
24389         this.el.unselectable();
24390         
24391         this.cells = this.el.select("table.x-date-inner tbody td");
24392         this.textNodes = this.el.query("table.x-date-inner tbody span");
24393
24394         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24395             text: "&#160;",
24396             tooltip: this.monthYearText
24397         });
24398
24399         this.mbtn.on('click', this.showMonthPicker, this);
24400         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24401
24402
24403         var today = (new Date()).dateFormat(this.format);
24404         
24405         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24406         baseTb.add({
24407             text: String.format(this.todayText, today),
24408             tooltip: String.format(this.todayTip, today),
24409             handler: this.selectToday,
24410             scope: this
24411         });
24412         
24413         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24414             
24415         //});
24416         if (this.showClear) {
24417             
24418             baseTb.add( new Roo.Toolbar.Fill());
24419             baseTb.add({
24420                 text: '&#160;',
24421                 cls: 'x-btn-icon x-btn-clear',
24422                 handler: function() {
24423                     //this.value = '';
24424                     this.fireEvent("select", this, '');
24425                 },
24426                 scope: this
24427             });
24428         }
24429         
24430         
24431         if(Roo.isIE){
24432             this.el.repaint();
24433         }
24434         this.update(this.value);
24435     },
24436
24437     createMonthPicker : function(){
24438         if(!this.monthPicker.dom.firstChild){
24439             var buf = ['<table border="0" cellspacing="0">'];
24440             for(var i = 0; i < 6; i++){
24441                 buf.push(
24442                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24443                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24444                     i == 0 ?
24445                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24446                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24447                 );
24448             }
24449             buf.push(
24450                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24451                     this.okText,
24452                     '</button><button type="button" class="x-date-mp-cancel">',
24453                     this.cancelText,
24454                     '</button></td></tr>',
24455                 '</table>'
24456             );
24457             this.monthPicker.update(buf.join(''));
24458             this.monthPicker.on('click', this.onMonthClick, this);
24459             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24460
24461             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24462             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24463
24464             this.mpMonths.each(function(m, a, i){
24465                 i += 1;
24466                 if((i%2) == 0){
24467                     m.dom.xmonth = 5 + Math.round(i * .5);
24468                 }else{
24469                     m.dom.xmonth = Math.round((i-1) * .5);
24470                 }
24471             });
24472         }
24473     },
24474
24475     showMonthPicker : function(){
24476         this.createMonthPicker();
24477         var size = this.el.getSize();
24478         this.monthPicker.setSize(size);
24479         this.monthPicker.child('table').setSize(size);
24480
24481         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24482         this.updateMPMonth(this.mpSelMonth);
24483         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24484         this.updateMPYear(this.mpSelYear);
24485
24486         this.monthPicker.slideIn('t', {duration:.2});
24487     },
24488
24489     updateMPYear : function(y){
24490         this.mpyear = y;
24491         var ys = this.mpYears.elements;
24492         for(var i = 1; i <= 10; i++){
24493             var td = ys[i-1], y2;
24494             if((i%2) == 0){
24495                 y2 = y + Math.round(i * .5);
24496                 td.firstChild.innerHTML = y2;
24497                 td.xyear = y2;
24498             }else{
24499                 y2 = y - (5-Math.round(i * .5));
24500                 td.firstChild.innerHTML = y2;
24501                 td.xyear = y2;
24502             }
24503             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24504         }
24505     },
24506
24507     updateMPMonth : function(sm){
24508         this.mpMonths.each(function(m, a, i){
24509             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24510         });
24511     },
24512
24513     selectMPMonth: function(m){
24514         
24515     },
24516
24517     onMonthClick : function(e, t){
24518         e.stopEvent();
24519         var el = new Roo.Element(t), pn;
24520         if(el.is('button.x-date-mp-cancel')){
24521             this.hideMonthPicker();
24522         }
24523         else if(el.is('button.x-date-mp-ok')){
24524             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24525             this.hideMonthPicker();
24526         }
24527         else if(pn = el.up('td.x-date-mp-month', 2)){
24528             this.mpMonths.removeClass('x-date-mp-sel');
24529             pn.addClass('x-date-mp-sel');
24530             this.mpSelMonth = pn.dom.xmonth;
24531         }
24532         else if(pn = el.up('td.x-date-mp-year', 2)){
24533             this.mpYears.removeClass('x-date-mp-sel');
24534             pn.addClass('x-date-mp-sel');
24535             this.mpSelYear = pn.dom.xyear;
24536         }
24537         else if(el.is('a.x-date-mp-prev')){
24538             this.updateMPYear(this.mpyear-10);
24539         }
24540         else if(el.is('a.x-date-mp-next')){
24541             this.updateMPYear(this.mpyear+10);
24542         }
24543     },
24544
24545     onMonthDblClick : function(e, t){
24546         e.stopEvent();
24547         var el = new Roo.Element(t), pn;
24548         if(pn = el.up('td.x-date-mp-month', 2)){
24549             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24550             this.hideMonthPicker();
24551         }
24552         else if(pn = el.up('td.x-date-mp-year', 2)){
24553             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24554             this.hideMonthPicker();
24555         }
24556     },
24557
24558     hideMonthPicker : function(disableAnim){
24559         if(this.monthPicker){
24560             if(disableAnim === true){
24561                 this.monthPicker.hide();
24562             }else{
24563                 this.monthPicker.slideOut('t', {duration:.2});
24564             }
24565         }
24566     },
24567
24568     // private
24569     showPrevMonth : function(e){
24570         this.update(this.activeDate.add("mo", -1));
24571     },
24572
24573     // private
24574     showNextMonth : function(e){
24575         this.update(this.activeDate.add("mo", 1));
24576     },
24577
24578     // private
24579     showPrevYear : function(){
24580         this.update(this.activeDate.add("y", -1));
24581     },
24582
24583     // private
24584     showNextYear : function(){
24585         this.update(this.activeDate.add("y", 1));
24586     },
24587
24588     // private
24589     handleMouseWheel : function(e){
24590         var delta = e.getWheelDelta();
24591         if(delta > 0){
24592             this.showPrevMonth();
24593             e.stopEvent();
24594         } else if(delta < 0){
24595             this.showNextMonth();
24596             e.stopEvent();
24597         }
24598     },
24599
24600     // private
24601     handleDateClick : function(e, t){
24602         e.stopEvent();
24603         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24604             this.setValue(new Date(t.dateValue));
24605             this.fireEvent("select", this, this.value);
24606         }
24607     },
24608
24609     // private
24610     selectToday : function(){
24611         this.setValue(new Date().clearTime());
24612         this.fireEvent("select", this, this.value);
24613     },
24614
24615     // private
24616     update : function(date){
24617         var vd = this.activeDate;
24618         this.activeDate = date;
24619         if(vd && this.el){
24620             var t = date.getTime();
24621             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24622                 this.cells.removeClass("x-date-selected");
24623                 this.cells.each(function(c){
24624                    if(c.dom.firstChild.dateValue == t){
24625                        c.addClass("x-date-selected");
24626                        setTimeout(function(){
24627                             try{c.dom.firstChild.focus();}catch(e){}
24628                        }, 50);
24629                        return false;
24630                    }
24631                 });
24632                 return;
24633             }
24634         }
24635         var days = date.getDaysInMonth();
24636         var firstOfMonth = date.getFirstDateOfMonth();
24637         var startingPos = firstOfMonth.getDay()-this.startDay;
24638
24639         if(startingPos <= this.startDay){
24640             startingPos += 7;
24641         }
24642
24643         var pm = date.add("mo", -1);
24644         var prevStart = pm.getDaysInMonth()-startingPos;
24645
24646         var cells = this.cells.elements;
24647         var textEls = this.textNodes;
24648         days += startingPos;
24649
24650         // convert everything to numbers so it's fast
24651         var day = 86400000;
24652         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24653         var today = new Date().clearTime().getTime();
24654         var sel = date.clearTime().getTime();
24655         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24656         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24657         var ddMatch = this.disabledDatesRE;
24658         var ddText = this.disabledDatesText;
24659         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24660         var ddaysText = this.disabledDaysText;
24661         var format = this.format;
24662
24663         var setCellClass = function(cal, cell){
24664             cell.title = "";
24665             var t = d.getTime();
24666             cell.firstChild.dateValue = t;
24667             if(t == today){
24668                 cell.className += " x-date-today";
24669                 cell.title = cal.todayText;
24670             }
24671             if(t == sel){
24672                 cell.className += " x-date-selected";
24673                 setTimeout(function(){
24674                     try{cell.firstChild.focus();}catch(e){}
24675                 }, 50);
24676             }
24677             // disabling
24678             if(t < min) {
24679                 cell.className = " x-date-disabled";
24680                 cell.title = cal.minText;
24681                 return;
24682             }
24683             if(t > max) {
24684                 cell.className = " x-date-disabled";
24685                 cell.title = cal.maxText;
24686                 return;
24687             }
24688             if(ddays){
24689                 if(ddays.indexOf(d.getDay()) != -1){
24690                     cell.title = ddaysText;
24691                     cell.className = " x-date-disabled";
24692                 }
24693             }
24694             if(ddMatch && format){
24695                 var fvalue = d.dateFormat(format);
24696                 if(ddMatch.test(fvalue)){
24697                     cell.title = ddText.replace("%0", fvalue);
24698                     cell.className = " x-date-disabled";
24699                 }
24700             }
24701         };
24702
24703         var i = 0;
24704         for(; i < startingPos; i++) {
24705             textEls[i].innerHTML = (++prevStart);
24706             d.setDate(d.getDate()+1);
24707             cells[i].className = "x-date-prevday";
24708             setCellClass(this, cells[i]);
24709         }
24710         for(; i < days; i++){
24711             intDay = i - startingPos + 1;
24712             textEls[i].innerHTML = (intDay);
24713             d.setDate(d.getDate()+1);
24714             cells[i].className = "x-date-active";
24715             setCellClass(this, cells[i]);
24716         }
24717         var extraDays = 0;
24718         for(; i < 42; i++) {
24719              textEls[i].innerHTML = (++extraDays);
24720              d.setDate(d.getDate()+1);
24721              cells[i].className = "x-date-nextday";
24722              setCellClass(this, cells[i]);
24723         }
24724
24725         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24726
24727         if(!this.internalRender){
24728             var main = this.el.dom.firstChild;
24729             var w = main.offsetWidth;
24730             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24731             Roo.fly(main).setWidth(w);
24732             this.internalRender = true;
24733             // opera does not respect the auto grow header center column
24734             // then, after it gets a width opera refuses to recalculate
24735             // without a second pass
24736             if(Roo.isOpera && !this.secondPass){
24737                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24738                 this.secondPass = true;
24739                 this.update.defer(10, this, [date]);
24740             }
24741         }
24742     }
24743 });/*
24744  * Based on:
24745  * Ext JS Library 1.1.1
24746  * Copyright(c) 2006-2007, Ext JS, LLC.
24747  *
24748  * Originally Released Under LGPL - original licence link has changed is not relivant.
24749  *
24750  * Fork - LGPL
24751  * <script type="text/javascript">
24752  */
24753 /**
24754  * @class Roo.TabPanel
24755  * @extends Roo.util.Observable
24756  * A lightweight tab container.
24757  * <br><br>
24758  * Usage:
24759  * <pre><code>
24760 // basic tabs 1, built from existing content
24761 var tabs = new Roo.TabPanel("tabs1");
24762 tabs.addTab("script", "View Script");
24763 tabs.addTab("markup", "View Markup");
24764 tabs.activate("script");
24765
24766 // more advanced tabs, built from javascript
24767 var jtabs = new Roo.TabPanel("jtabs");
24768 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24769
24770 // set up the UpdateManager
24771 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24772 var updater = tab2.getUpdateManager();
24773 updater.setDefaultUrl("ajax1.htm");
24774 tab2.on('activate', updater.refresh, updater, true);
24775
24776 // Use setUrl for Ajax loading
24777 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24778 tab3.setUrl("ajax2.htm", null, true);
24779
24780 // Disabled tab
24781 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24782 tab4.disable();
24783
24784 jtabs.activate("jtabs-1");
24785  * </code></pre>
24786  * @constructor
24787  * Create a new TabPanel.
24788  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24789  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24790  */
24791 Roo.TabPanel = function(container, config){
24792     /**
24793     * The container element for this TabPanel.
24794     * @type Roo.Element
24795     */
24796     this.el = Roo.get(container, true);
24797     if(config){
24798         if(typeof config == "boolean"){
24799             this.tabPosition = config ? "bottom" : "top";
24800         }else{
24801             Roo.apply(this, config);
24802         }
24803     }
24804     if(this.tabPosition == "bottom"){
24805         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24806         this.el.addClass("x-tabs-bottom");
24807     }
24808     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24809     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24810     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24811     if(Roo.isIE){
24812         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24813     }
24814     if(this.tabPosition != "bottom"){
24815     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24816      * @type Roo.Element
24817      */
24818       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24819       this.el.addClass("x-tabs-top");
24820     }
24821     this.items = [];
24822
24823     this.bodyEl.setStyle("position", "relative");
24824
24825     this.active = null;
24826     this.activateDelegate = this.activate.createDelegate(this);
24827
24828     this.addEvents({
24829         /**
24830          * @event tabchange
24831          * Fires when the active tab changes
24832          * @param {Roo.TabPanel} this
24833          * @param {Roo.TabPanelItem} activePanel The new active tab
24834          */
24835         "tabchange": true,
24836         /**
24837          * @event beforetabchange
24838          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24839          * @param {Roo.TabPanel} this
24840          * @param {Object} e Set cancel to true on this object to cancel the tab change
24841          * @param {Roo.TabPanelItem} tab The tab being changed to
24842          */
24843         "beforetabchange" : true
24844     });
24845
24846     Roo.EventManager.onWindowResize(this.onResize, this);
24847     this.cpad = this.el.getPadding("lr");
24848     this.hiddenCount = 0;
24849
24850     Roo.TabPanel.superclass.constructor.call(this);
24851 };
24852
24853 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24854         /*
24855          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24856          */
24857     tabPosition : "top",
24858         /*
24859          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24860          */
24861     currentTabWidth : 0,
24862         /*
24863          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24864          */
24865     minTabWidth : 40,
24866         /*
24867          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24868          */
24869     maxTabWidth : 250,
24870         /*
24871          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24872          */
24873     preferredTabWidth : 175,
24874         /*
24875          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24876          */
24877     resizeTabs : false,
24878         /*
24879          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24880          */
24881     monitorResize : true,
24882
24883     /**
24884      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24885      * @param {String} id The id of the div to use <b>or create</b>
24886      * @param {String} text The text for the tab
24887      * @param {String} content (optional) Content to put in the TabPanelItem body
24888      * @param {Boolean} closable (optional) True to create a close icon on the tab
24889      * @return {Roo.TabPanelItem} The created TabPanelItem
24890      */
24891     addTab : function(id, text, content, closable){
24892         var item = new Roo.TabPanelItem(this, id, text, closable);
24893         this.addTabItem(item);
24894         if(content){
24895             item.setContent(content);
24896         }
24897         return item;
24898     },
24899
24900     /**
24901      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24902      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24903      * @return {Roo.TabPanelItem}
24904      */
24905     getTab : function(id){
24906         return this.items[id];
24907     },
24908
24909     /**
24910      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24911      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24912      */
24913     hideTab : function(id){
24914         var t = this.items[id];
24915         if(!t.isHidden()){
24916            t.setHidden(true);
24917            this.hiddenCount++;
24918            this.autoSizeTabs();
24919         }
24920     },
24921
24922     /**
24923      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24924      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24925      */
24926     unhideTab : function(id){
24927         var t = this.items[id];
24928         if(t.isHidden()){
24929            t.setHidden(false);
24930            this.hiddenCount--;
24931            this.autoSizeTabs();
24932         }
24933     },
24934
24935     /**
24936      * Adds an existing {@link Roo.TabPanelItem}.
24937      * @param {Roo.TabPanelItem} item The TabPanelItem to add
24938      */
24939     addTabItem : function(item){
24940         this.items[item.id] = item;
24941         this.items.push(item);
24942         if(this.resizeTabs){
24943            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
24944            this.autoSizeTabs();
24945         }else{
24946             item.autoSize();
24947         }
24948     },
24949
24950     /**
24951      * Removes a {@link Roo.TabPanelItem}.
24952      * @param {String/Number} id The id or index of the TabPanelItem to remove.
24953      */
24954     removeTab : function(id){
24955         var items = this.items;
24956         var tab = items[id];
24957         if(!tab) return;
24958         var index = items.indexOf(tab);
24959         if(this.active == tab && items.length > 1){
24960             var newTab = this.getNextAvailable(index);
24961             if(newTab)newTab.activate();
24962         }
24963         this.stripEl.dom.removeChild(tab.pnode.dom);
24964         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
24965             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
24966         }
24967         items.splice(index, 1);
24968         delete this.items[tab.id];
24969         tab.fireEvent("close", tab);
24970         tab.purgeListeners();
24971         this.autoSizeTabs();
24972     },
24973
24974     getNextAvailable : function(start){
24975         var items = this.items;
24976         var index = start;
24977         // look for a next tab that will slide over to
24978         // replace the one being removed
24979         while(index < items.length){
24980             var item = items[++index];
24981             if(item && !item.isHidden()){
24982                 return item;
24983             }
24984         }
24985         // if one isn't found select the previous tab (on the left)
24986         index = start;
24987         while(index >= 0){
24988             var item = items[--index];
24989             if(item && !item.isHidden()){
24990                 return item;
24991             }
24992         }
24993         return null;
24994     },
24995
24996     /**
24997      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
24998      * @param {String/Number} id The id or index of the TabPanelItem to disable.
24999      */
25000     disableTab : function(id){
25001         var tab = this.items[id];
25002         if(tab && this.active != tab){
25003             tab.disable();
25004         }
25005     },
25006
25007     /**
25008      * Enables a {@link Roo.TabPanelItem} that is disabled.
25009      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25010      */
25011     enableTab : function(id){
25012         var tab = this.items[id];
25013         tab.enable();
25014     },
25015
25016     /**
25017      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25018      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25019      * @return {Roo.TabPanelItem} The TabPanelItem.
25020      */
25021     activate : function(id){
25022         var tab = this.items[id];
25023         if(!tab){
25024             return null;
25025         }
25026         if(tab == this.active || tab.disabled){
25027             return tab;
25028         }
25029         var e = {};
25030         this.fireEvent("beforetabchange", this, e, tab);
25031         if(e.cancel !== true && !tab.disabled){
25032             if(this.active){
25033                 this.active.hide();
25034             }
25035             this.active = this.items[id];
25036             this.active.show();
25037             this.fireEvent("tabchange", this, this.active);
25038         }
25039         return tab;
25040     },
25041
25042     /**
25043      * Gets the active {@link Roo.TabPanelItem}.
25044      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25045      */
25046     getActiveTab : function(){
25047         return this.active;
25048     },
25049
25050     /**
25051      * Updates the tab body element to fit the height of the container element
25052      * for overflow scrolling
25053      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25054      */
25055     syncHeight : function(targetHeight){
25056         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25057         var bm = this.bodyEl.getMargins();
25058         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25059         this.bodyEl.setHeight(newHeight);
25060         return newHeight;
25061     },
25062
25063     onResize : function(){
25064         if(this.monitorResize){
25065             this.autoSizeTabs();
25066         }
25067     },
25068
25069     /**
25070      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25071      */
25072     beginUpdate : function(){
25073         this.updating = true;
25074     },
25075
25076     /**
25077      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25078      */
25079     endUpdate : function(){
25080         this.updating = false;
25081         this.autoSizeTabs();
25082     },
25083
25084     /**
25085      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25086      */
25087     autoSizeTabs : function(){
25088         var count = this.items.length;
25089         var vcount = count - this.hiddenCount;
25090         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25091         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25092         var availWidth = Math.floor(w / vcount);
25093         var b = this.stripBody;
25094         if(b.getWidth() > w){
25095             var tabs = this.items;
25096             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25097             if(availWidth < this.minTabWidth){
25098                 /*if(!this.sleft){    // incomplete scrolling code
25099                     this.createScrollButtons();
25100                 }
25101                 this.showScroll();
25102                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25103             }
25104         }else{
25105             if(this.currentTabWidth < this.preferredTabWidth){
25106                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25107             }
25108         }
25109     },
25110
25111     /**
25112      * Returns the number of tabs in this TabPanel.
25113      * @return {Number}
25114      */
25115      getCount : function(){
25116          return this.items.length;
25117      },
25118
25119     /**
25120      * Resizes all the tabs to the passed width
25121      * @param {Number} The new width
25122      */
25123     setTabWidth : function(width){
25124         this.currentTabWidth = width;
25125         for(var i = 0, len = this.items.length; i < len; i++) {
25126                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25127         }
25128     },
25129
25130     /**
25131      * Destroys this TabPanel
25132      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25133      */
25134     destroy : function(removeEl){
25135         Roo.EventManager.removeResizeListener(this.onResize, this);
25136         for(var i = 0, len = this.items.length; i < len; i++){
25137             this.items[i].purgeListeners();
25138         }
25139         if(removeEl === true){
25140             this.el.update("");
25141             this.el.remove();
25142         }
25143     }
25144 });
25145
25146 /**
25147  * @class Roo.TabPanelItem
25148  * @extends Roo.util.Observable
25149  * Represents an individual item (tab plus body) in a TabPanel.
25150  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25151  * @param {String} id The id of this TabPanelItem
25152  * @param {String} text The text for the tab of this TabPanelItem
25153  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25154  */
25155 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25156     /**
25157      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25158      * @type Roo.TabPanel
25159      */
25160     this.tabPanel = tabPanel;
25161     /**
25162      * The id for this TabPanelItem
25163      * @type String
25164      */
25165     this.id = id;
25166     /** @private */
25167     this.disabled = false;
25168     /** @private */
25169     this.text = text;
25170     /** @private */
25171     this.loaded = false;
25172     this.closable = closable;
25173
25174     /**
25175      * The body element for this TabPanelItem.
25176      * @type Roo.Element
25177      */
25178     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25179     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25180     this.bodyEl.setStyle("display", "block");
25181     this.bodyEl.setStyle("zoom", "1");
25182     this.hideAction();
25183
25184     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25185     /** @private */
25186     this.el = Roo.get(els.el, true);
25187     this.inner = Roo.get(els.inner, true);
25188     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25189     this.pnode = Roo.get(els.el.parentNode, true);
25190     this.el.on("mousedown", this.onTabMouseDown, this);
25191     this.el.on("click", this.onTabClick, this);
25192     /** @private */
25193     if(closable){
25194         var c = Roo.get(els.close, true);
25195         c.dom.title = this.closeText;
25196         c.addClassOnOver("close-over");
25197         c.on("click", this.closeClick, this);
25198      }
25199
25200     this.addEvents({
25201          /**
25202          * @event activate
25203          * Fires when this tab becomes the active tab.
25204          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25205          * @param {Roo.TabPanelItem} this
25206          */
25207         "activate": true,
25208         /**
25209          * @event beforeclose
25210          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25211          * @param {Roo.TabPanelItem} this
25212          * @param {Object} e Set cancel to true on this object to cancel the close.
25213          */
25214         "beforeclose": true,
25215         /**
25216          * @event close
25217          * Fires when this tab is closed.
25218          * @param {Roo.TabPanelItem} this
25219          */
25220          "close": true,
25221         /**
25222          * @event deactivate
25223          * Fires when this tab is no longer the active tab.
25224          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25225          * @param {Roo.TabPanelItem} this
25226          */
25227          "deactivate" : true
25228     });
25229     this.hidden = false;
25230
25231     Roo.TabPanelItem.superclass.constructor.call(this);
25232 };
25233
25234 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25235     purgeListeners : function(){
25236        Roo.util.Observable.prototype.purgeListeners.call(this);
25237        this.el.removeAllListeners();
25238     },
25239     /**
25240      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25241      */
25242     show : function(){
25243         this.pnode.addClass("on");
25244         this.showAction();
25245         if(Roo.isOpera){
25246             this.tabPanel.stripWrap.repaint();
25247         }
25248         this.fireEvent("activate", this.tabPanel, this);
25249     },
25250
25251     /**
25252      * Returns true if this tab is the active tab.
25253      * @return {Boolean}
25254      */
25255     isActive : function(){
25256         return this.tabPanel.getActiveTab() == this;
25257     },
25258
25259     /**
25260      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25261      */
25262     hide : function(){
25263         this.pnode.removeClass("on");
25264         this.hideAction();
25265         this.fireEvent("deactivate", this.tabPanel, this);
25266     },
25267
25268     hideAction : function(){
25269         this.bodyEl.hide();
25270         this.bodyEl.setStyle("position", "absolute");
25271         this.bodyEl.setLeft("-20000px");
25272         this.bodyEl.setTop("-20000px");
25273     },
25274
25275     showAction : function(){
25276         this.bodyEl.setStyle("position", "relative");
25277         this.bodyEl.setTop("");
25278         this.bodyEl.setLeft("");
25279         this.bodyEl.show();
25280     },
25281
25282     /**
25283      * Set the tooltip for the tab.
25284      * @param {String} tooltip The tab's tooltip
25285      */
25286     setTooltip : function(text){
25287         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25288             this.textEl.dom.qtip = text;
25289             this.textEl.dom.removeAttribute('title');
25290         }else{
25291             this.textEl.dom.title = text;
25292         }
25293     },
25294
25295     onTabClick : function(e){
25296         e.preventDefault();
25297         this.tabPanel.activate(this.id);
25298     },
25299
25300     onTabMouseDown : function(e){
25301         e.preventDefault();
25302         this.tabPanel.activate(this.id);
25303     },
25304
25305     getWidth : function(){
25306         return this.inner.getWidth();
25307     },
25308
25309     setWidth : function(width){
25310         var iwidth = width - this.pnode.getPadding("lr");
25311         this.inner.setWidth(iwidth);
25312         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25313         this.pnode.setWidth(width);
25314     },
25315
25316     /**
25317      * Show or hide the tab
25318      * @param {Boolean} hidden True to hide or false to show.
25319      */
25320     setHidden : function(hidden){
25321         this.hidden = hidden;
25322         this.pnode.setStyle("display", hidden ? "none" : "");
25323     },
25324
25325     /**
25326      * Returns true if this tab is "hidden"
25327      * @return {Boolean}
25328      */
25329     isHidden : function(){
25330         return this.hidden;
25331     },
25332
25333     /**
25334      * Returns the text for this tab
25335      * @return {String}
25336      */
25337     getText : function(){
25338         return this.text;
25339     },
25340
25341     autoSize : function(){
25342         //this.el.beginMeasure();
25343         this.textEl.setWidth(1);
25344         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25345         //this.el.endMeasure();
25346     },
25347
25348     /**
25349      * Sets the text for the tab (Note: this also sets the tooltip text)
25350      * @param {String} text The tab's text and tooltip
25351      */
25352     setText : function(text){
25353         this.text = text;
25354         this.textEl.update(text);
25355         this.setTooltip(text);
25356         if(!this.tabPanel.resizeTabs){
25357             this.autoSize();
25358         }
25359     },
25360     /**
25361      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25362      */
25363     activate : function(){
25364         this.tabPanel.activate(this.id);
25365     },
25366
25367     /**
25368      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25369      */
25370     disable : function(){
25371         if(this.tabPanel.active != this){
25372             this.disabled = true;
25373             this.pnode.addClass("disabled");
25374         }
25375     },
25376
25377     /**
25378      * Enables this TabPanelItem if it was previously disabled.
25379      */
25380     enable : function(){
25381         this.disabled = false;
25382         this.pnode.removeClass("disabled");
25383     },
25384
25385     /**
25386      * Sets the content for this TabPanelItem.
25387      * @param {String} content The content
25388      * @param {Boolean} loadScripts true to look for and load scripts
25389      */
25390     setContent : function(content, loadScripts){
25391         this.bodyEl.update(content, loadScripts);
25392     },
25393
25394     /**
25395      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25396      * @return {Roo.UpdateManager} The UpdateManager
25397      */
25398     getUpdateManager : function(){
25399         return this.bodyEl.getUpdateManager();
25400     },
25401
25402     /**
25403      * Set a URL to be used to load the content for this TabPanelItem.
25404      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25405      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25406      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25407      * @return {Roo.UpdateManager} The UpdateManager
25408      */
25409     setUrl : function(url, params, loadOnce){
25410         if(this.refreshDelegate){
25411             this.un('activate', this.refreshDelegate);
25412         }
25413         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25414         this.on("activate", this.refreshDelegate);
25415         return this.bodyEl.getUpdateManager();
25416     },
25417
25418     /** @private */
25419     _handleRefresh : function(url, params, loadOnce){
25420         if(!loadOnce || !this.loaded){
25421             var updater = this.bodyEl.getUpdateManager();
25422             updater.update(url, params, this._setLoaded.createDelegate(this));
25423         }
25424     },
25425
25426     /**
25427      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25428      *   Will fail silently if the setUrl method has not been called.
25429      *   This does not activate the panel, just updates its content.
25430      */
25431     refresh : function(){
25432         if(this.refreshDelegate){
25433            this.loaded = false;
25434            this.refreshDelegate();
25435         }
25436     },
25437
25438     /** @private */
25439     _setLoaded : function(){
25440         this.loaded = true;
25441     },
25442
25443     /** @private */
25444     closeClick : function(e){
25445         var o = {};
25446         e.stopEvent();
25447         this.fireEvent("beforeclose", this, o);
25448         if(o.cancel !== true){
25449             this.tabPanel.removeTab(this.id);
25450         }
25451     },
25452     /**
25453      * The text displayed in the tooltip for the close icon.
25454      * @type String
25455      */
25456     closeText : "Close this tab"
25457 });
25458
25459 /** @private */
25460 Roo.TabPanel.prototype.createStrip = function(container){
25461     var strip = document.createElement("div");
25462     strip.className = "x-tabs-wrap";
25463     container.appendChild(strip);
25464     return strip;
25465 };
25466 /** @private */
25467 Roo.TabPanel.prototype.createStripList = function(strip){
25468     // div wrapper for retard IE
25469     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25470     return strip.firstChild.firstChild.firstChild.firstChild;
25471 };
25472 /** @private */
25473 Roo.TabPanel.prototype.createBody = function(container){
25474     var body = document.createElement("div");
25475     Roo.id(body, "tab-body");
25476     Roo.fly(body).addClass("x-tabs-body");
25477     container.appendChild(body);
25478     return body;
25479 };
25480 /** @private */
25481 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25482     var body = Roo.getDom(id);
25483     if(!body){
25484         body = document.createElement("div");
25485         body.id = id;
25486     }
25487     Roo.fly(body).addClass("x-tabs-item-body");
25488     bodyEl.insertBefore(body, bodyEl.firstChild);
25489     return body;
25490 };
25491 /** @private */
25492 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25493     var td = document.createElement("td");
25494     stripEl.appendChild(td);
25495     if(closable){
25496         td.className = "x-tabs-closable";
25497         if(!this.closeTpl){
25498             this.closeTpl = new Roo.Template(
25499                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25500                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25501                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25502             );
25503         }
25504         var el = this.closeTpl.overwrite(td, {"text": text});
25505         var close = el.getElementsByTagName("div")[0];
25506         var inner = el.getElementsByTagName("em")[0];
25507         return {"el": el, "close": close, "inner": inner};
25508     } else {
25509         if(!this.tabTpl){
25510             this.tabTpl = new Roo.Template(
25511                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25512                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25513             );
25514         }
25515         var el = this.tabTpl.overwrite(td, {"text": text});
25516         var inner = el.getElementsByTagName("em")[0];
25517         return {"el": el, "inner": inner};
25518     }
25519 };/*
25520  * Based on:
25521  * Ext JS Library 1.1.1
25522  * Copyright(c) 2006-2007, Ext JS, LLC.
25523  *
25524  * Originally Released Under LGPL - original licence link has changed is not relivant.
25525  *
25526  * Fork - LGPL
25527  * <script type="text/javascript">
25528  */
25529
25530 /**
25531  * @class Roo.Button
25532  * @extends Roo.util.Observable
25533  * Simple Button class
25534  * @cfg {String} text The button text
25535  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25536  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25537  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25538  * @cfg {Object} scope The scope of the handler
25539  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25540  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25541  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25542  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25543  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25544  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25545    applies if enableToggle = true)
25546  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25547  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25548   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25549  * @constructor
25550  * Create a new button
25551  * @param {Object} config The config object
25552  */
25553 Roo.Button = function(renderTo, config)
25554 {
25555     if (!config) {
25556         config = renderTo;
25557         renderTo = config.renderTo || false;
25558     }
25559     
25560     Roo.apply(this, config);
25561     this.addEvents({
25562         /**
25563              * @event click
25564              * Fires when this button is clicked
25565              * @param {Button} this
25566              * @param {EventObject} e The click event
25567              */
25568             "click" : true,
25569         /**
25570              * @event toggle
25571              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25572              * @param {Button} this
25573              * @param {Boolean} pressed
25574              */
25575             "toggle" : true,
25576         /**
25577              * @event mouseover
25578              * Fires when the mouse hovers over the button
25579              * @param {Button} this
25580              * @param {Event} e The event object
25581              */
25582         'mouseover' : true,
25583         /**
25584              * @event mouseout
25585              * Fires when the mouse exits the button
25586              * @param {Button} this
25587              * @param {Event} e The event object
25588              */
25589         'mouseout': true,
25590          /**
25591              * @event render
25592              * Fires when the button is rendered
25593              * @param {Button} this
25594              */
25595         'render': true
25596     });
25597     if(this.menu){
25598         this.menu = Roo.menu.MenuMgr.get(this.menu);
25599     }
25600     if(renderTo){
25601         this.render(renderTo);
25602     }
25603     
25604     Roo.util.Observable.call(this);
25605 };
25606
25607 Roo.extend(Roo.Button, Roo.util.Observable, {
25608     /**
25609      * 
25610      */
25611     
25612     /**
25613      * Read-only. True if this button is hidden
25614      * @type Boolean
25615      */
25616     hidden : false,
25617     /**
25618      * Read-only. True if this button is disabled
25619      * @type Boolean
25620      */
25621     disabled : false,
25622     /**
25623      * Read-only. True if this button is pressed (only if enableToggle = true)
25624      * @type Boolean
25625      */
25626     pressed : false,
25627
25628     /**
25629      * @cfg {Number} tabIndex 
25630      * The DOM tabIndex for this button (defaults to undefined)
25631      */
25632     tabIndex : undefined,
25633
25634     /**
25635      * @cfg {Boolean} enableToggle
25636      * True to enable pressed/not pressed toggling (defaults to false)
25637      */
25638     enableToggle: false,
25639     /**
25640      * @cfg {Mixed} menu
25641      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25642      */
25643     menu : undefined,
25644     /**
25645      * @cfg {String} menuAlign
25646      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25647      */
25648     menuAlign : "tl-bl?",
25649
25650     /**
25651      * @cfg {String} iconCls
25652      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25653      */
25654     iconCls : undefined,
25655     /**
25656      * @cfg {String} type
25657      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25658      */
25659     type : 'button',
25660
25661     // private
25662     menuClassTarget: 'tr',
25663
25664     /**
25665      * @cfg {String} clickEvent
25666      * The type of event to map to the button's event handler (defaults to 'click')
25667      */
25668     clickEvent : 'click',
25669
25670     /**
25671      * @cfg {Boolean} handleMouseEvents
25672      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25673      */
25674     handleMouseEvents : true,
25675
25676     /**
25677      * @cfg {String} tooltipType
25678      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25679      */
25680     tooltipType : 'qtip',
25681
25682     /**
25683      * @cfg {String} cls
25684      * A CSS class to apply to the button's main element.
25685      */
25686     
25687     /**
25688      * @cfg {Roo.Template} template (Optional)
25689      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25690      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25691      * require code modifications if required elements (e.g. a button) aren't present.
25692      */
25693
25694     // private
25695     render : function(renderTo){
25696         var btn;
25697         if(this.hideParent){
25698             this.parentEl = Roo.get(renderTo);
25699         }
25700         if(!this.dhconfig){
25701             if(!this.template){
25702                 if(!Roo.Button.buttonTemplate){
25703                     // hideous table template
25704                     Roo.Button.buttonTemplate = new Roo.Template(
25705                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25706                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
25707                         "</tr></tbody></table>");
25708                 }
25709                 this.template = Roo.Button.buttonTemplate;
25710             }
25711             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25712             var btnEl = btn.child("button:first");
25713             btnEl.on('focus', this.onFocus, this);
25714             btnEl.on('blur', this.onBlur, this);
25715             if(this.cls){
25716                 btn.addClass(this.cls);
25717             }
25718             if(this.icon){
25719                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25720             }
25721             if(this.iconCls){
25722                 btnEl.addClass(this.iconCls);
25723                 if(!this.cls){
25724                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25725                 }
25726             }
25727             if(this.tabIndex !== undefined){
25728                 btnEl.dom.tabIndex = this.tabIndex;
25729             }
25730             if(this.tooltip){
25731                 if(typeof this.tooltip == 'object'){
25732                     Roo.QuickTips.tips(Roo.apply({
25733                           target: btnEl.id
25734                     }, this.tooltip));
25735                 } else {
25736                     btnEl.dom[this.tooltipType] = this.tooltip;
25737                 }
25738             }
25739         }else{
25740             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25741         }
25742         this.el = btn;
25743         if(this.id){
25744             this.el.dom.id = this.el.id = this.id;
25745         }
25746         if(this.menu){
25747             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25748             this.menu.on("show", this.onMenuShow, this);
25749             this.menu.on("hide", this.onMenuHide, this);
25750         }
25751         btn.addClass("x-btn");
25752         if(Roo.isIE && !Roo.isIE7){
25753             this.autoWidth.defer(1, this);
25754         }else{
25755             this.autoWidth();
25756         }
25757         if(this.handleMouseEvents){
25758             btn.on("mouseover", this.onMouseOver, this);
25759             btn.on("mouseout", this.onMouseOut, this);
25760             btn.on("mousedown", this.onMouseDown, this);
25761         }
25762         btn.on(this.clickEvent, this.onClick, this);
25763         //btn.on("mouseup", this.onMouseUp, this);
25764         if(this.hidden){
25765             this.hide();
25766         }
25767         if(this.disabled){
25768             this.disable();
25769         }
25770         Roo.ButtonToggleMgr.register(this);
25771         if(this.pressed){
25772             this.el.addClass("x-btn-pressed");
25773         }
25774         if(this.repeat){
25775             var repeater = new Roo.util.ClickRepeater(btn,
25776                 typeof this.repeat == "object" ? this.repeat : {}
25777             );
25778             repeater.on("click", this.onClick,  this);
25779         }
25780         this.fireEvent('render', this);
25781         
25782     },
25783     /**
25784      * Returns the button's underlying element
25785      * @return {Roo.Element} The element
25786      */
25787     getEl : function(){
25788         return this.el;  
25789     },
25790     
25791     /**
25792      * Destroys this Button and removes any listeners.
25793      */
25794     destroy : function(){
25795         Roo.ButtonToggleMgr.unregister(this);
25796         this.el.removeAllListeners();
25797         this.purgeListeners();
25798         this.el.remove();
25799     },
25800
25801     // private
25802     autoWidth : function(){
25803         if(this.el){
25804             this.el.setWidth("auto");
25805             if(Roo.isIE7 && Roo.isStrict){
25806                 var ib = this.el.child('button');
25807                 if(ib && ib.getWidth() > 20){
25808                     ib.clip();
25809                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25810                 }
25811             }
25812             if(this.minWidth){
25813                 if(this.hidden){
25814                     this.el.beginMeasure();
25815                 }
25816                 if(this.el.getWidth() < this.minWidth){
25817                     this.el.setWidth(this.minWidth);
25818                 }
25819                 if(this.hidden){
25820                     this.el.endMeasure();
25821                 }
25822             }
25823         }
25824     },
25825
25826     /**
25827      * Assigns this button's click handler
25828      * @param {Function} handler The function to call when the button is clicked
25829      * @param {Object} scope (optional) Scope for the function passed in
25830      */
25831     setHandler : function(handler, scope){
25832         this.handler = handler;
25833         this.scope = scope;  
25834     },
25835     
25836     /**
25837      * Sets this button's text
25838      * @param {String} text The button text
25839      */
25840     setText : function(text){
25841         this.text = text;
25842         if(this.el){
25843             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25844         }
25845         this.autoWidth();
25846     },
25847     
25848     /**
25849      * Gets the text for this button
25850      * @return {String} The button text
25851      */
25852     getText : function(){
25853         return this.text;  
25854     },
25855     
25856     /**
25857      * Show this button
25858      */
25859     show: function(){
25860         this.hidden = false;
25861         if(this.el){
25862             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25863         }
25864     },
25865     
25866     /**
25867      * Hide this button
25868      */
25869     hide: function(){
25870         this.hidden = true;
25871         if(this.el){
25872             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25873         }
25874     },
25875     
25876     /**
25877      * Convenience function for boolean show/hide
25878      * @param {Boolean} visible True to show, false to hide
25879      */
25880     setVisible: function(visible){
25881         if(visible) {
25882             this.show();
25883         }else{
25884             this.hide();
25885         }
25886     },
25887     
25888     /**
25889      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25890      * @param {Boolean} state (optional) Force a particular state
25891      */
25892     toggle : function(state){
25893         state = state === undefined ? !this.pressed : state;
25894         if(state != this.pressed){
25895             if(state){
25896                 this.el.addClass("x-btn-pressed");
25897                 this.pressed = true;
25898                 this.fireEvent("toggle", this, true);
25899             }else{
25900                 this.el.removeClass("x-btn-pressed");
25901                 this.pressed = false;
25902                 this.fireEvent("toggle", this, false);
25903             }
25904             if(this.toggleHandler){
25905                 this.toggleHandler.call(this.scope || this, this, state);
25906             }
25907         }
25908     },
25909     
25910     /**
25911      * Focus the button
25912      */
25913     focus : function(){
25914         this.el.child('button:first').focus();
25915     },
25916     
25917     /**
25918      * Disable this button
25919      */
25920     disable : function(){
25921         if(this.el){
25922             this.el.addClass("x-btn-disabled");
25923         }
25924         this.disabled = true;
25925     },
25926     
25927     /**
25928      * Enable this button
25929      */
25930     enable : function(){
25931         if(this.el){
25932             this.el.removeClass("x-btn-disabled");
25933         }
25934         this.disabled = false;
25935     },
25936
25937     /**
25938      * Convenience function for boolean enable/disable
25939      * @param {Boolean} enabled True to enable, false to disable
25940      */
25941     setDisabled : function(v){
25942         this[v !== true ? "enable" : "disable"]();
25943     },
25944
25945     // private
25946     onClick : function(e){
25947         if(e){
25948             e.preventDefault();
25949         }
25950         if(e.button != 0){
25951             return;
25952         }
25953         if(!this.disabled){
25954             if(this.enableToggle){
25955                 this.toggle();
25956             }
25957             if(this.menu && !this.menu.isVisible()){
25958                 this.menu.show(this.el, this.menuAlign);
25959             }
25960             this.fireEvent("click", this, e);
25961             if(this.handler){
25962                 this.el.removeClass("x-btn-over");
25963                 this.handler.call(this.scope || this, this, e);
25964             }
25965         }
25966     },
25967     // private
25968     onMouseOver : function(e){
25969         if(!this.disabled){
25970             this.el.addClass("x-btn-over");
25971             this.fireEvent('mouseover', this, e);
25972         }
25973     },
25974     // private
25975     onMouseOut : function(e){
25976         if(!e.within(this.el,  true)){
25977             this.el.removeClass("x-btn-over");
25978             this.fireEvent('mouseout', this, e);
25979         }
25980     },
25981     // private
25982     onFocus : function(e){
25983         if(!this.disabled){
25984             this.el.addClass("x-btn-focus");
25985         }
25986     },
25987     // private
25988     onBlur : function(e){
25989         this.el.removeClass("x-btn-focus");
25990     },
25991     // private
25992     onMouseDown : function(e){
25993         if(!this.disabled && e.button == 0){
25994             this.el.addClass("x-btn-click");
25995             Roo.get(document).on('mouseup', this.onMouseUp, this);
25996         }
25997     },
25998     // private
25999     onMouseUp : function(e){
26000         if(e.button == 0){
26001             this.el.removeClass("x-btn-click");
26002             Roo.get(document).un('mouseup', this.onMouseUp, this);
26003         }
26004     },
26005     // private
26006     onMenuShow : function(e){
26007         this.el.addClass("x-btn-menu-active");
26008     },
26009     // private
26010     onMenuHide : function(e){
26011         this.el.removeClass("x-btn-menu-active");
26012     }   
26013 });
26014
26015 // Private utility class used by Button
26016 Roo.ButtonToggleMgr = function(){
26017    var groups = {};
26018    
26019    function toggleGroup(btn, state){
26020        if(state){
26021            var g = groups[btn.toggleGroup];
26022            for(var i = 0, l = g.length; i < l; i++){
26023                if(g[i] != btn){
26024                    g[i].toggle(false);
26025                }
26026            }
26027        }
26028    }
26029    
26030    return {
26031        register : function(btn){
26032            if(!btn.toggleGroup){
26033                return;
26034            }
26035            var g = groups[btn.toggleGroup];
26036            if(!g){
26037                g = groups[btn.toggleGroup] = [];
26038            }
26039            g.push(btn);
26040            btn.on("toggle", toggleGroup);
26041        },
26042        
26043        unregister : function(btn){
26044            if(!btn.toggleGroup){
26045                return;
26046            }
26047            var g = groups[btn.toggleGroup];
26048            if(g){
26049                g.remove(btn);
26050                btn.un("toggle", toggleGroup);
26051            }
26052        }
26053    };
26054 }();/*
26055  * Based on:
26056  * Ext JS Library 1.1.1
26057  * Copyright(c) 2006-2007, Ext JS, LLC.
26058  *
26059  * Originally Released Under LGPL - original licence link has changed is not relivant.
26060  *
26061  * Fork - LGPL
26062  * <script type="text/javascript">
26063  */
26064  
26065 /**
26066  * @class Roo.SplitButton
26067  * @extends Roo.Button
26068  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26069  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26070  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26071  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26072  * @cfg {String} arrowTooltip The title attribute of the arrow
26073  * @constructor
26074  * Create a new menu button
26075  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26076  * @param {Object} config The config object
26077  */
26078 Roo.SplitButton = function(renderTo, config){
26079     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26080     /**
26081      * @event arrowclick
26082      * Fires when this button's arrow is clicked
26083      * @param {SplitButton} this
26084      * @param {EventObject} e The click event
26085      */
26086     this.addEvents({"arrowclick":true});
26087 };
26088
26089 Roo.extend(Roo.SplitButton, Roo.Button, {
26090     render : function(renderTo){
26091         // this is one sweet looking template!
26092         var tpl = new Roo.Template(
26093             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26094             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26095             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26096             "</tbody></table></td><td>",
26097             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26098             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26099             "</tbody></table></td></tr></table>"
26100         );
26101         var btn = tpl.append(renderTo, [this.text, this.type], true);
26102         var btnEl = btn.child("button");
26103         if(this.cls){
26104             btn.addClass(this.cls);
26105         }
26106         if(this.icon){
26107             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26108         }
26109         if(this.iconCls){
26110             btnEl.addClass(this.iconCls);
26111             if(!this.cls){
26112                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26113             }
26114         }
26115         this.el = btn;
26116         if(this.handleMouseEvents){
26117             btn.on("mouseover", this.onMouseOver, this);
26118             btn.on("mouseout", this.onMouseOut, this);
26119             btn.on("mousedown", this.onMouseDown, this);
26120             btn.on("mouseup", this.onMouseUp, this);
26121         }
26122         btn.on(this.clickEvent, this.onClick, this);
26123         if(this.tooltip){
26124             if(typeof this.tooltip == 'object'){
26125                 Roo.QuickTips.tips(Roo.apply({
26126                       target: btnEl.id
26127                 }, this.tooltip));
26128             } else {
26129                 btnEl.dom[this.tooltipType] = this.tooltip;
26130             }
26131         }
26132         if(this.arrowTooltip){
26133             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26134         }
26135         if(this.hidden){
26136             this.hide();
26137         }
26138         if(this.disabled){
26139             this.disable();
26140         }
26141         if(this.pressed){
26142             this.el.addClass("x-btn-pressed");
26143         }
26144         if(Roo.isIE && !Roo.isIE7){
26145             this.autoWidth.defer(1, this);
26146         }else{
26147             this.autoWidth();
26148         }
26149         if(this.menu){
26150             this.menu.on("show", this.onMenuShow, this);
26151             this.menu.on("hide", this.onMenuHide, this);
26152         }
26153         this.fireEvent('render', this);
26154     },
26155
26156     // private
26157     autoWidth : function(){
26158         if(this.el){
26159             var tbl = this.el.child("table:first");
26160             var tbl2 = this.el.child("table:last");
26161             this.el.setWidth("auto");
26162             tbl.setWidth("auto");
26163             if(Roo.isIE7 && Roo.isStrict){
26164                 var ib = this.el.child('button:first');
26165                 if(ib && ib.getWidth() > 20){
26166                     ib.clip();
26167                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26168                 }
26169             }
26170             if(this.minWidth){
26171                 if(this.hidden){
26172                     this.el.beginMeasure();
26173                 }
26174                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26175                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26176                 }
26177                 if(this.hidden){
26178                     this.el.endMeasure();
26179                 }
26180             }
26181             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26182         } 
26183     },
26184     /**
26185      * Sets this button's click handler
26186      * @param {Function} handler The function to call when the button is clicked
26187      * @param {Object} scope (optional) Scope for the function passed above
26188      */
26189     setHandler : function(handler, scope){
26190         this.handler = handler;
26191         this.scope = scope;  
26192     },
26193     
26194     /**
26195      * Sets this button's arrow click handler
26196      * @param {Function} handler The function to call when the arrow is clicked
26197      * @param {Object} scope (optional) Scope for the function passed above
26198      */
26199     setArrowHandler : function(handler, scope){
26200         this.arrowHandler = handler;
26201         this.scope = scope;  
26202     },
26203     
26204     /**
26205      * Focus the button
26206      */
26207     focus : function(){
26208         if(this.el){
26209             this.el.child("button:first").focus();
26210         }
26211     },
26212
26213     // private
26214     onClick : function(e){
26215         e.preventDefault();
26216         if(!this.disabled){
26217             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26218                 if(this.menu && !this.menu.isVisible()){
26219                     this.menu.show(this.el, this.menuAlign);
26220                 }
26221                 this.fireEvent("arrowclick", this, e);
26222                 if(this.arrowHandler){
26223                     this.arrowHandler.call(this.scope || this, this, e);
26224                 }
26225             }else{
26226                 this.fireEvent("click", this, e);
26227                 if(this.handler){
26228                     this.handler.call(this.scope || this, this, e);
26229                 }
26230             }
26231         }
26232     },
26233     // private
26234     onMouseDown : function(e){
26235         if(!this.disabled){
26236             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26237         }
26238     },
26239     // private
26240     onMouseUp : function(e){
26241         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26242     }   
26243 });
26244
26245
26246 // backwards compat
26247 Roo.MenuButton = Roo.SplitButton;/*
26248  * Based on:
26249  * Ext JS Library 1.1.1
26250  * Copyright(c) 2006-2007, Ext JS, LLC.
26251  *
26252  * Originally Released Under LGPL - original licence link has changed is not relivant.
26253  *
26254  * Fork - LGPL
26255  * <script type="text/javascript">
26256  */
26257
26258 /**
26259  * @class Roo.Toolbar
26260  * Basic Toolbar class.
26261  * @constructor
26262  * Creates a new Toolbar
26263  * @param {Object} config The config object
26264  */ 
26265 Roo.Toolbar = function(container, buttons, config)
26266 {
26267     /// old consturctor format still supported..
26268     if(container instanceof Array){ // omit the container for later rendering
26269         buttons = container;
26270         config = buttons;
26271         container = null;
26272     }
26273     if (typeof(container) == 'object' && container.xtype) {
26274         config = container;
26275         container = config.container;
26276         buttons = config.buttons; // not really - use items!!
26277     }
26278     var xitems = [];
26279     if (config && config.items) {
26280         xitems = config.items;
26281         delete config.items;
26282     }
26283     Roo.apply(this, config);
26284     this.buttons = buttons;
26285     
26286     if(container){
26287         this.render(container);
26288     }
26289     Roo.each(xitems, function(b) {
26290         this.add(b);
26291     }, this);
26292     
26293 };
26294
26295 Roo.Toolbar.prototype = {
26296     /**
26297      * @cfg {Roo.data.Store} items
26298      * array of button configs or elements to add
26299      */
26300     
26301     /**
26302      * @cfg {String/HTMLElement/Element} container
26303      * The id or element that will contain the toolbar
26304      */
26305     // private
26306     render : function(ct){
26307         this.el = Roo.get(ct);
26308         if(this.cls){
26309             this.el.addClass(this.cls);
26310         }
26311         // using a table allows for vertical alignment
26312         // 100% width is needed by Safari...
26313         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26314         this.tr = this.el.child("tr", true);
26315         var autoId = 0;
26316         this.items = new Roo.util.MixedCollection(false, function(o){
26317             return o.id || ("item" + (++autoId));
26318         });
26319         if(this.buttons){
26320             this.add.apply(this, this.buttons);
26321             delete this.buttons;
26322         }
26323     },
26324
26325     /**
26326      * Adds element(s) to the toolbar -- this function takes a variable number of 
26327      * arguments of mixed type and adds them to the toolbar.
26328      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26329      * <ul>
26330      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26331      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26332      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26333      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26334      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26335      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26336      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26337      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26338      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26339      * </ul>
26340      * @param {Mixed} arg2
26341      * @param {Mixed} etc.
26342      */
26343     add : function(){
26344         var a = arguments, l = a.length;
26345         for(var i = 0; i < l; i++){
26346             this._add(a[i]);
26347         }
26348     },
26349     // private..
26350     _add : function(el) {
26351         
26352         if (el.xtype) {
26353             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26354         }
26355         
26356         if (el.applyTo){ // some kind of form field
26357             return this.addField(el);
26358         } 
26359         if (el.render){ // some kind of Toolbar.Item
26360             return this.addItem(el);
26361         }
26362         if (typeof el == "string"){ // string
26363             if(el == "separator" || el == "-"){
26364                 return this.addSeparator();
26365             }
26366             if (el == " "){
26367                 return this.addSpacer();
26368             }
26369             if(el == "->"){
26370                 return this.addFill();
26371             }
26372             return this.addText(el);
26373             
26374         }
26375         if(el.tagName){ // element
26376             return this.addElement(el);
26377         }
26378         if(typeof el == "object"){ // must be button config?
26379             return this.addButton(el);
26380         }
26381         // and now what?!?!
26382         return false;
26383         
26384     },
26385     
26386     /**
26387      * Add an Xtype element
26388      * @param {Object} xtype Xtype Object
26389      * @return {Object} created Object
26390      */
26391     addxtype : function(e){
26392         return this.add(e);  
26393     },
26394     
26395     /**
26396      * Returns the Element for this toolbar.
26397      * @return {Roo.Element}
26398      */
26399     getEl : function(){
26400         return this.el;  
26401     },
26402     
26403     /**
26404      * Adds a separator
26405      * @return {Roo.Toolbar.Item} The separator item
26406      */
26407     addSeparator : function(){
26408         return this.addItem(new Roo.Toolbar.Separator());
26409     },
26410
26411     /**
26412      * Adds a spacer element
26413      * @return {Roo.Toolbar.Spacer} The spacer item
26414      */
26415     addSpacer : function(){
26416         return this.addItem(new Roo.Toolbar.Spacer());
26417     },
26418
26419     /**
26420      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26421      * @return {Roo.Toolbar.Fill} The fill item
26422      */
26423     addFill : function(){
26424         return this.addItem(new Roo.Toolbar.Fill());
26425     },
26426
26427     /**
26428      * Adds any standard HTML element to the toolbar
26429      * @param {String/HTMLElement/Element} el The element or id of the element to add
26430      * @return {Roo.Toolbar.Item} The element's item
26431      */
26432     addElement : function(el){
26433         return this.addItem(new Roo.Toolbar.Item(el));
26434     },
26435     /**
26436      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26437      * @type Roo.util.MixedCollection  
26438      */
26439     items : false,
26440      
26441     /**
26442      * Adds any Toolbar.Item or subclass
26443      * @param {Roo.Toolbar.Item} item
26444      * @return {Roo.Toolbar.Item} The item
26445      */
26446     addItem : function(item){
26447         var td = this.nextBlock();
26448         item.render(td);
26449         this.items.add(item);
26450         return item;
26451     },
26452     
26453     /**
26454      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26455      * @param {Object/Array} config A button config or array of configs
26456      * @return {Roo.Toolbar.Button/Array}
26457      */
26458     addButton : function(config){
26459         if(config instanceof Array){
26460             var buttons = [];
26461             for(var i = 0, len = config.length; i < len; i++) {
26462                 buttons.push(this.addButton(config[i]));
26463             }
26464             return buttons;
26465         }
26466         var b = config;
26467         if(!(config instanceof Roo.Toolbar.Button)){
26468             b = config.split ?
26469                 new Roo.Toolbar.SplitButton(config) :
26470                 new Roo.Toolbar.Button(config);
26471         }
26472         var td = this.nextBlock();
26473         b.render(td);
26474         this.items.add(b);
26475         return b;
26476     },
26477     
26478     /**
26479      * Adds text to the toolbar
26480      * @param {String} text The text to add
26481      * @return {Roo.Toolbar.Item} The element's item
26482      */
26483     addText : function(text){
26484         return this.addItem(new Roo.Toolbar.TextItem(text));
26485     },
26486     
26487     /**
26488      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26489      * @param {Number} index The index where the item is to be inserted
26490      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26491      * @return {Roo.Toolbar.Button/Item}
26492      */
26493     insertButton : function(index, item){
26494         if(item instanceof Array){
26495             var buttons = [];
26496             for(var i = 0, len = item.length; i < len; i++) {
26497                buttons.push(this.insertButton(index + i, item[i]));
26498             }
26499             return buttons;
26500         }
26501         if (!(item instanceof Roo.Toolbar.Button)){
26502            item = new Roo.Toolbar.Button(item);
26503         }
26504         var td = document.createElement("td");
26505         this.tr.insertBefore(td, this.tr.childNodes[index]);
26506         item.render(td);
26507         this.items.insert(index, item);
26508         return item;
26509     },
26510     
26511     /**
26512      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26513      * @param {Object} config
26514      * @return {Roo.Toolbar.Item} The element's item
26515      */
26516     addDom : function(config, returnEl){
26517         var td = this.nextBlock();
26518         Roo.DomHelper.overwrite(td, config);
26519         var ti = new Roo.Toolbar.Item(td.firstChild);
26520         ti.render(td);
26521         this.items.add(ti);
26522         return ti;
26523     },
26524
26525     /**
26526      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26527      * @type Roo.util.MixedCollection  
26528      */
26529     fields : false,
26530     
26531     /**
26532      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26533      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26534      * @param {Roo.form.Field} field
26535      * @return {Roo.ToolbarItem}
26536      */
26537      
26538       
26539     addField : function(field) {
26540         if (!this.fields) {
26541             var autoId = 0;
26542             this.fields = new Roo.util.MixedCollection(false, function(o){
26543                 return o.id || ("item" + (++autoId));
26544             });
26545
26546         }
26547         
26548         var td = this.nextBlock();
26549         field.render(td);
26550         var ti = new Roo.Toolbar.Item(td.firstChild);
26551         ti.render(td);
26552         this.items.add(ti);
26553         this.fields.add(field);
26554         return ti;
26555     },
26556     /**
26557      * Hide the toolbar
26558      * @method hide
26559      */
26560      
26561       
26562     hide : function()
26563     {
26564         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26565         this.el.child('div').hide();
26566     },
26567     /**
26568      * Show the toolbar
26569      * @method show
26570      */
26571     show : function()
26572     {
26573         this.el.child('div').show();
26574     },
26575       
26576     // private
26577     nextBlock : function(){
26578         var td = document.createElement("td");
26579         this.tr.appendChild(td);
26580         return td;
26581     },
26582
26583     // private
26584     destroy : function(){
26585         if(this.items){ // rendered?
26586             Roo.destroy.apply(Roo, this.items.items);
26587         }
26588         if(this.fields){ // rendered?
26589             Roo.destroy.apply(Roo, this.fields.items);
26590         }
26591         Roo.Element.uncache(this.el, this.tr);
26592     }
26593 };
26594
26595 /**
26596  * @class Roo.Toolbar.Item
26597  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26598  * @constructor
26599  * Creates a new Item
26600  * @param {HTMLElement} el 
26601  */
26602 Roo.Toolbar.Item = function(el){
26603     this.el = Roo.getDom(el);
26604     this.id = Roo.id(this.el);
26605     this.hidden = false;
26606 };
26607
26608 Roo.Toolbar.Item.prototype = {
26609     
26610     /**
26611      * Get this item's HTML Element
26612      * @return {HTMLElement}
26613      */
26614     getEl : function(){
26615        return this.el;  
26616     },
26617
26618     // private
26619     render : function(td){
26620         this.td = td;
26621         td.appendChild(this.el);
26622     },
26623     
26624     /**
26625      * Removes and destroys this item.
26626      */
26627     destroy : function(){
26628         this.td.parentNode.removeChild(this.td);
26629     },
26630     
26631     /**
26632      * Shows this item.
26633      */
26634     show: function(){
26635         this.hidden = false;
26636         this.td.style.display = "";
26637     },
26638     
26639     /**
26640      * Hides this item.
26641      */
26642     hide: function(){
26643         this.hidden = true;
26644         this.td.style.display = "none";
26645     },
26646     
26647     /**
26648      * Convenience function for boolean show/hide.
26649      * @param {Boolean} visible true to show/false to hide
26650      */
26651     setVisible: function(visible){
26652         if(visible) {
26653             this.show();
26654         }else{
26655             this.hide();
26656         }
26657     },
26658     
26659     /**
26660      * Try to focus this item.
26661      */
26662     focus : function(){
26663         Roo.fly(this.el).focus();
26664     },
26665     
26666     /**
26667      * Disables this item.
26668      */
26669     disable : function(){
26670         Roo.fly(this.td).addClass("x-item-disabled");
26671         this.disabled = true;
26672         this.el.disabled = true;
26673     },
26674     
26675     /**
26676      * Enables this item.
26677      */
26678     enable : function(){
26679         Roo.fly(this.td).removeClass("x-item-disabled");
26680         this.disabled = false;
26681         this.el.disabled = false;
26682     }
26683 };
26684
26685
26686 /**
26687  * @class Roo.Toolbar.Separator
26688  * @extends Roo.Toolbar.Item
26689  * A simple toolbar separator class
26690  * @constructor
26691  * Creates a new Separator
26692  */
26693 Roo.Toolbar.Separator = function(){
26694     var s = document.createElement("span");
26695     s.className = "ytb-sep";
26696     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26697 };
26698 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26699     enable:Roo.emptyFn,
26700     disable:Roo.emptyFn,
26701     focus:Roo.emptyFn
26702 });
26703
26704 /**
26705  * @class Roo.Toolbar.Spacer
26706  * @extends Roo.Toolbar.Item
26707  * A simple element that adds extra horizontal space to a toolbar.
26708  * @constructor
26709  * Creates a new Spacer
26710  */
26711 Roo.Toolbar.Spacer = function(){
26712     var s = document.createElement("div");
26713     s.className = "ytb-spacer";
26714     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26715 };
26716 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26717     enable:Roo.emptyFn,
26718     disable:Roo.emptyFn,
26719     focus:Roo.emptyFn
26720 });
26721
26722 /**
26723  * @class Roo.Toolbar.Fill
26724  * @extends Roo.Toolbar.Spacer
26725  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26726  * @constructor
26727  * Creates a new Spacer
26728  */
26729 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26730     // private
26731     render : function(td){
26732         td.style.width = '100%';
26733         Roo.Toolbar.Fill.superclass.render.call(this, td);
26734     }
26735 });
26736
26737 /**
26738  * @class Roo.Toolbar.TextItem
26739  * @extends Roo.Toolbar.Item
26740  * A simple class that renders text directly into a toolbar.
26741  * @constructor
26742  * Creates a new TextItem
26743  * @param {String} text
26744  */
26745 Roo.Toolbar.TextItem = function(text){
26746     if (typeof(text) == 'object') {
26747         text = text.text;
26748     }
26749     var s = document.createElement("span");
26750     s.className = "ytb-text";
26751     s.innerHTML = text;
26752     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26753 };
26754 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26755     enable:Roo.emptyFn,
26756     disable:Roo.emptyFn,
26757     focus:Roo.emptyFn
26758 });
26759
26760 /**
26761  * @class Roo.Toolbar.Button
26762  * @extends Roo.Button
26763  * A button that renders into a toolbar.
26764  * @constructor
26765  * Creates a new Button
26766  * @param {Object} config A standard {@link Roo.Button} config object
26767  */
26768 Roo.Toolbar.Button = function(config){
26769     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26770 };
26771 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26772     render : function(td){
26773         this.td = td;
26774         Roo.Toolbar.Button.superclass.render.call(this, td);
26775     },
26776     
26777     /**
26778      * Removes and destroys this button
26779      */
26780     destroy : function(){
26781         Roo.Toolbar.Button.superclass.destroy.call(this);
26782         this.td.parentNode.removeChild(this.td);
26783     },
26784     
26785     /**
26786      * Shows this button
26787      */
26788     show: function(){
26789         this.hidden = false;
26790         this.td.style.display = "";
26791     },
26792     
26793     /**
26794      * Hides this button
26795      */
26796     hide: function(){
26797         this.hidden = true;
26798         this.td.style.display = "none";
26799     },
26800
26801     /**
26802      * Disables this item
26803      */
26804     disable : function(){
26805         Roo.fly(this.td).addClass("x-item-disabled");
26806         this.disabled = true;
26807     },
26808
26809     /**
26810      * Enables this item
26811      */
26812     enable : function(){
26813         Roo.fly(this.td).removeClass("x-item-disabled");
26814         this.disabled = false;
26815     }
26816 });
26817 // backwards compat
26818 Roo.ToolbarButton = Roo.Toolbar.Button;
26819
26820 /**
26821  * @class Roo.Toolbar.SplitButton
26822  * @extends Roo.SplitButton
26823  * A menu button that renders into a toolbar.
26824  * @constructor
26825  * Creates a new SplitButton
26826  * @param {Object} config A standard {@link Roo.SplitButton} config object
26827  */
26828 Roo.Toolbar.SplitButton = function(config){
26829     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26830 };
26831 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26832     render : function(td){
26833         this.td = td;
26834         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26835     },
26836     
26837     /**
26838      * Removes and destroys this button
26839      */
26840     destroy : function(){
26841         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26842         this.td.parentNode.removeChild(this.td);
26843     },
26844     
26845     /**
26846      * Shows this button
26847      */
26848     show: function(){
26849         this.hidden = false;
26850         this.td.style.display = "";
26851     },
26852     
26853     /**
26854      * Hides this button
26855      */
26856     hide: function(){
26857         this.hidden = true;
26858         this.td.style.display = "none";
26859     }
26860 });
26861
26862 // backwards compat
26863 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26864  * Based on:
26865  * Ext JS Library 1.1.1
26866  * Copyright(c) 2006-2007, Ext JS, LLC.
26867  *
26868  * Originally Released Under LGPL - original licence link has changed is not relivant.
26869  *
26870  * Fork - LGPL
26871  * <script type="text/javascript">
26872  */
26873  
26874 /**
26875  * @class Roo.PagingToolbar
26876  * @extends Roo.Toolbar
26877  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26878  * @constructor
26879  * Create a new PagingToolbar
26880  * @param {Object} config The config object
26881  */
26882 Roo.PagingToolbar = function(el, ds, config)
26883 {
26884     // old args format still supported... - xtype is prefered..
26885     if (typeof(el) == 'object' && el.xtype) {
26886         // created from xtype...
26887         config = el;
26888         ds = el.dataSource;
26889         el = config.container;
26890     }
26891     
26892     
26893     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26894     this.ds = ds;
26895     this.cursor = 0;
26896     this.renderButtons(this.el);
26897     this.bind(ds);
26898 };
26899
26900 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26901     /**
26902      * @cfg {Roo.data.Store} dataSource
26903      * The underlying data store providing the paged data
26904      */
26905     /**
26906      * @cfg {String/HTMLElement/Element} container
26907      * container The id or element that will contain the toolbar
26908      */
26909     /**
26910      * @cfg {Boolean} displayInfo
26911      * True to display the displayMsg (defaults to false)
26912      */
26913     /**
26914      * @cfg {Number} pageSize
26915      * The number of records to display per page (defaults to 20)
26916      */
26917     pageSize: 20,
26918     /**
26919      * @cfg {String} displayMsg
26920      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26921      */
26922     displayMsg : 'Displaying {0} - {1} of {2}',
26923     /**
26924      * @cfg {String} emptyMsg
26925      * The message to display when no records are found (defaults to "No data to display")
26926      */
26927     emptyMsg : 'No data to display',
26928     /**
26929      * Customizable piece of the default paging text (defaults to "Page")
26930      * @type String
26931      */
26932     beforePageText : "Page",
26933     /**
26934      * Customizable piece of the default paging text (defaults to "of %0")
26935      * @type String
26936      */
26937     afterPageText : "of {0}",
26938     /**
26939      * Customizable piece of the default paging text (defaults to "First Page")
26940      * @type String
26941      */
26942     firstText : "First Page",
26943     /**
26944      * Customizable piece of the default paging text (defaults to "Previous Page")
26945      * @type String
26946      */
26947     prevText : "Previous Page",
26948     /**
26949      * Customizable piece of the default paging text (defaults to "Next Page")
26950      * @type String
26951      */
26952     nextText : "Next Page",
26953     /**
26954      * Customizable piece of the default paging text (defaults to "Last Page")
26955      * @type String
26956      */
26957     lastText : "Last Page",
26958     /**
26959      * Customizable piece of the default paging text (defaults to "Refresh")
26960      * @type String
26961      */
26962     refreshText : "Refresh",
26963
26964     // private
26965     renderButtons : function(el){
26966         Roo.PagingToolbar.superclass.render.call(this, el);
26967         this.first = this.addButton({
26968             tooltip: this.firstText,
26969             cls: "x-btn-icon x-grid-page-first",
26970             disabled: true,
26971             handler: this.onClick.createDelegate(this, ["first"])
26972         });
26973         this.prev = this.addButton({
26974             tooltip: this.prevText,
26975             cls: "x-btn-icon x-grid-page-prev",
26976             disabled: true,
26977             handler: this.onClick.createDelegate(this, ["prev"])
26978         });
26979         this.addSeparator();
26980         this.add(this.beforePageText);
26981         this.field = Roo.get(this.addDom({
26982            tag: "input",
26983            type: "text",
26984            size: "3",
26985            value: "1",
26986            cls: "x-grid-page-number"
26987         }).el);
26988         this.field.on("keydown", this.onPagingKeydown, this);
26989         this.field.on("focus", function(){this.dom.select();});
26990         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
26991         this.field.setHeight(18);
26992         this.addSeparator();
26993         this.next = this.addButton({
26994             tooltip: this.nextText,
26995             cls: "x-btn-icon x-grid-page-next",
26996             disabled: true,
26997             handler: this.onClick.createDelegate(this, ["next"])
26998         });
26999         this.last = this.addButton({
27000             tooltip: this.lastText,
27001             cls: "x-btn-icon x-grid-page-last",
27002             disabled: true,
27003             handler: this.onClick.createDelegate(this, ["last"])
27004         });
27005         this.addSeparator();
27006         this.loading = this.addButton({
27007             tooltip: this.refreshText,
27008             cls: "x-btn-icon x-grid-loading",
27009             handler: this.onClick.createDelegate(this, ["refresh"])
27010         });
27011
27012         if(this.displayInfo){
27013             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27014         }
27015     },
27016
27017     // private
27018     updateInfo : function(){
27019         if(this.displayEl){
27020             var count = this.ds.getCount();
27021             var msg = count == 0 ?
27022                 this.emptyMsg :
27023                 String.format(
27024                     this.displayMsg,
27025                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27026                 );
27027             this.displayEl.update(msg);
27028         }
27029     },
27030
27031     // private
27032     onLoad : function(ds, r, o){
27033        this.cursor = o.params ? o.params.start : 0;
27034        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27035
27036        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27037        this.field.dom.value = ap;
27038        this.first.setDisabled(ap == 1);
27039        this.prev.setDisabled(ap == 1);
27040        this.next.setDisabled(ap == ps);
27041        this.last.setDisabled(ap == ps);
27042        this.loading.enable();
27043        this.updateInfo();
27044     },
27045
27046     // private
27047     getPageData : function(){
27048         var total = this.ds.getTotalCount();
27049         return {
27050             total : total,
27051             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27052             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27053         };
27054     },
27055
27056     // private
27057     onLoadError : function(){
27058         this.loading.enable();
27059     },
27060
27061     // private
27062     onPagingKeydown : function(e){
27063         var k = e.getKey();
27064         var d = this.getPageData();
27065         if(k == e.RETURN){
27066             var v = this.field.dom.value, pageNum;
27067             if(!v || isNaN(pageNum = parseInt(v, 10))){
27068                 this.field.dom.value = d.activePage;
27069                 return;
27070             }
27071             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27072             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27073             e.stopEvent();
27074         }
27075         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27076         {
27077           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27078           this.field.dom.value = pageNum;
27079           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27080           e.stopEvent();
27081         }
27082         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27083         {
27084           var v = this.field.dom.value, pageNum; 
27085           var increment = (e.shiftKey) ? 10 : 1;
27086           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27087             increment *= -1;
27088           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27089             this.field.dom.value = d.activePage;
27090             return;
27091           }
27092           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27093           {
27094             this.field.dom.value = parseInt(v, 10) + increment;
27095             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27096             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27097           }
27098           e.stopEvent();
27099         }
27100     },
27101
27102     // private
27103     beforeLoad : function(){
27104         if(this.loading){
27105             this.loading.disable();
27106         }
27107     },
27108
27109     // private
27110     onClick : function(which){
27111         var ds = this.ds;
27112         switch(which){
27113             case "first":
27114                 ds.load({params:{start: 0, limit: this.pageSize}});
27115             break;
27116             case "prev":
27117                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27118             break;
27119             case "next":
27120                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27121             break;
27122             case "last":
27123                 var total = ds.getTotalCount();
27124                 var extra = total % this.pageSize;
27125                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27126                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27127             break;
27128             case "refresh":
27129                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27130             break;
27131         }
27132     },
27133
27134     /**
27135      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27136      * @param {Roo.data.Store} store The data store to unbind
27137      */
27138     unbind : function(ds){
27139         ds.un("beforeload", this.beforeLoad, this);
27140         ds.un("load", this.onLoad, this);
27141         ds.un("loadexception", this.onLoadError, this);
27142         ds.un("remove", this.updateInfo, this);
27143         ds.un("add", this.updateInfo, this);
27144         this.ds = undefined;
27145     },
27146
27147     /**
27148      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27149      * @param {Roo.data.Store} store The data store to bind
27150      */
27151     bind : function(ds){
27152         ds.on("beforeload", this.beforeLoad, this);
27153         ds.on("load", this.onLoad, this);
27154         ds.on("loadexception", this.onLoadError, this);
27155         ds.on("remove", this.updateInfo, this);
27156         ds.on("add", this.updateInfo, this);
27157         this.ds = ds;
27158     }
27159 });