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  */
1107 Date.createParser = function(format) {
1108     var funcName = "parse" + Date.parseFunctions.count++;
1109     var regexNum = Date.parseRegexes.length;
1110     var currentGroup = 1;
1111     Date.parseFunctions[format] = funcName;
1112
1113     var code = "Date." + funcName + " = function(input){\n"
1114         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1115         + "var d = new Date();\n"
1116         + "y = d.getFullYear();\n"
1117         + "m = d.getMonth();\n"
1118         + "d = d.getDate();\n"
1119         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1120         + "if (results && results.length > 0) {";
1121     var regex = "";
1122
1123     var special = false;
1124     var ch = '';
1125     for (var i = 0; i < format.length; ++i) {
1126         ch = format.charAt(i);
1127         if (!special && ch == "\\") {
1128             special = true;
1129         }
1130         else if (special) {
1131             special = false;
1132             regex += String.escape(ch);
1133         }
1134         else {
1135             var obj = Date.formatCodeToRegex(ch, currentGroup);
1136             currentGroup += obj.g;
1137             regex += obj.s;
1138             if (obj.g && obj.c) {
1139                 code += obj.c;
1140             }
1141         }
1142     }
1143
1144     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1145         + "{v = new Date(y, m, d, h, i, s);}\n"
1146         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1147         + "{v = new Date(y, m, d, h, i);}\n"
1148         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1149         + "{v = new Date(y, m, d, h);}\n"
1150         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1151         + "{v = new Date(y, m, d);}\n"
1152         + "else if (y >= 0 && m >= 0)\n"
1153         + "{v = new Date(y, m);}\n"
1154         + "else if (y >= 0)\n"
1155         + "{v = new Date(y);}\n"
1156         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1157         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1158         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1159         + ";}";
1160
1161     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1162     /** eval:var:zzzzzzzzzzzzz */
1163     eval(code);
1164 };
1165
1166 // private
1167 Date.formatCodeToRegex = function(character, currentGroup) {
1168     switch (character) {
1169     case "D":
1170         return {g:0,
1171         c:null,
1172         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1173     case "j":
1174         return {g:1,
1175             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1176             s:"(\\d{1,2})"}; // day of month without leading zeroes
1177     case "d":
1178         return {g:1,
1179             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1180             s:"(\\d{2})"}; // day of month with leading zeroes
1181     case "l":
1182         return {g:0,
1183             c:null,
1184             s:"(?:" + Date.dayNames.join("|") + ")"};
1185     case "S":
1186         return {g:0,
1187             c:null,
1188             s:"(?:st|nd|rd|th)"};
1189     case "w":
1190         return {g:0,
1191             c:null,
1192             s:"\\d"};
1193     case "z":
1194         return {g:0,
1195             c:null,
1196             s:"(?:\\d{1,3})"};
1197     case "W":
1198         return {g:0,
1199             c:null,
1200             s:"(?:\\d{2})"};
1201     case "F":
1202         return {g:1,
1203             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1204             s:"(" + Date.monthNames.join("|") + ")"};
1205     case "M":
1206         return {g:1,
1207             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1208             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1209     case "n":
1210         return {g:1,
1211             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1212             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1213     case "m":
1214         return {g:1,
1215             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1216             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1217     case "t":
1218         return {g:0,
1219             c:null,
1220             s:"\\d{1,2}"};
1221     case "L":
1222         return {g:0,
1223             c:null,
1224             s:"(?:1|0)"};
1225     case "Y":
1226         return {g:1,
1227             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1228             s:"(\\d{4})"};
1229     case "y":
1230         return {g:1,
1231             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1232                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1233             s:"(\\d{1,2})"};
1234     case "a":
1235         return {g:1,
1236             c:"if (results[" + currentGroup + "] == 'am') {\n"
1237                 + "if (h == 12) { h = 0; }\n"
1238                 + "} else { if (h < 12) { h += 12; }}",
1239             s:"(am|pm)"};
1240     case "A":
1241         return {g:1,
1242             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1243                 + "if (h == 12) { h = 0; }\n"
1244                 + "} else { if (h < 12) { h += 12; }}",
1245             s:"(AM|PM)"};
1246     case "g":
1247     case "G":
1248         return {g:1,
1249             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1250             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1251     case "h":
1252     case "H":
1253         return {g:1,
1254             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1255             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1256     case "i":
1257         return {g:1,
1258             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1259             s:"(\\d{2})"};
1260     case "s":
1261         return {g:1,
1262             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1263             s:"(\\d{2})"};
1264     case "O":
1265         return {g:1,
1266             c:[
1267                 "o = results[", currentGroup, "];\n",
1268                 "var sn = o.substring(0,1);\n", // get + / - sign
1269                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1270                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1271                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1272                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1273             ].join(""),
1274             s:"([+\-]\\d{4})"};
1275     case "T":
1276         return {g:0,
1277             c:null,
1278             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1279     case "Z":
1280         return {g:1,
1281             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1282                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1283             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1284     default:
1285         return {g:0,
1286             c:null,
1287             s:String.escape(character)};
1288     }
1289 };
1290
1291 /**
1292  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1293  * @return {String} The abbreviated timezone name (e.g. 'CST')
1294  */
1295 Date.prototype.getTimezone = function() {
1296     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1297 };
1298
1299 /**
1300  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1301  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1302  */
1303 Date.prototype.getGMTOffset = function() {
1304     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1305         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1306         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1307 };
1308
1309 /**
1310  * Get the numeric day number of the year, adjusted for leap year.
1311  * @return {Number} 0 through 364 (365 in leap years)
1312  */
1313 Date.prototype.getDayOfYear = function() {
1314     var num = 0;
1315     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1316     for (var i = 0; i < this.getMonth(); ++i) {
1317         num += Date.daysInMonth[i];
1318     }
1319     return num + this.getDate() - 1;
1320 };
1321
1322 /**
1323  * Get the string representation of the numeric week number of the year
1324  * (equivalent to the format specifier 'W').
1325  * @return {String} '00' through '52'
1326  */
1327 Date.prototype.getWeekOfYear = function() {
1328     // Skip to Thursday of this week
1329     var now = this.getDayOfYear() + (4 - this.getDay());
1330     // Find the first Thursday of the year
1331     var jan1 = new Date(this.getFullYear(), 0, 1);
1332     var then = (7 - jan1.getDay() + 4);
1333     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1334 };
1335
1336 /**
1337  * Whether or not the current date is in a leap year.
1338  * @return {Boolean} True if the current date is in a leap year, else false
1339  */
1340 Date.prototype.isLeapYear = function() {
1341     var year = this.getFullYear();
1342     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1343 };
1344
1345 /**
1346  * Get the first day of the current month, adjusted for leap year.  The returned value
1347  * is the numeric day index within the week (0-6) which can be used in conjunction with
1348  * the {@link #monthNames} array to retrieve the textual day name.
1349  * Example:
1350  *<pre><code>
1351 var dt = new Date('1/10/2007');
1352 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1353 </code></pre>
1354  * @return {Number} The day number (0-6)
1355  */
1356 Date.prototype.getFirstDayOfMonth = function() {
1357     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1358     return (day < 0) ? (day + 7) : day;
1359 };
1360
1361 /**
1362  * Get the last day of the current month, adjusted for leap year.  The returned value
1363  * is the numeric day index within the week (0-6) which can be used in conjunction with
1364  * the {@link #monthNames} array to retrieve the textual day name.
1365  * Example:
1366  *<pre><code>
1367 var dt = new Date('1/10/2007');
1368 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1369 </code></pre>
1370  * @return {Number} The day number (0-6)
1371  */
1372 Date.prototype.getLastDayOfMonth = function() {
1373     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1374     return (day < 0) ? (day + 7) : day;
1375 };
1376
1377
1378 /**
1379  * Get the first date of this date's month
1380  * @return {Date}
1381  */
1382 Date.prototype.getFirstDateOfMonth = function() {
1383     return new Date(this.getFullYear(), this.getMonth(), 1);
1384 };
1385
1386 /**
1387  * Get the last date of this date's month
1388  * @return {Date}
1389  */
1390 Date.prototype.getLastDateOfMonth = function() {
1391     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1392 };
1393 /**
1394  * Get the number of days in the current month, adjusted for leap year.
1395  * @return {Number} The number of days in the month
1396  */
1397 Date.prototype.getDaysInMonth = function() {
1398     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1399     return Date.daysInMonth[this.getMonth()];
1400 };
1401
1402 /**
1403  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1404  * @return {String} 'st, 'nd', 'rd' or 'th'
1405  */
1406 Date.prototype.getSuffix = function() {
1407     switch (this.getDate()) {
1408         case 1:
1409         case 21:
1410         case 31:
1411             return "st";
1412         case 2:
1413         case 22:
1414             return "nd";
1415         case 3:
1416         case 23:
1417             return "rd";
1418         default:
1419             return "th";
1420     }
1421 };
1422
1423 // private
1424 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1425
1426 /**
1427  * An array of textual month names.
1428  * Override these values for international dates, for example...
1429  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1430  * @type Array
1431  * @static
1432  */
1433 Date.monthNames =
1434    ["January",
1435     "February",
1436     "March",
1437     "April",
1438     "May",
1439     "June",
1440     "July",
1441     "August",
1442     "September",
1443     "October",
1444     "November",
1445     "December"];
1446
1447 /**
1448  * An array of textual day names.
1449  * Override these values for international dates, for example...
1450  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1451  * @type Array
1452  * @static
1453  */
1454 Date.dayNames =
1455    ["Sunday",
1456     "Monday",
1457     "Tuesday",
1458     "Wednesday",
1459     "Thursday",
1460     "Friday",
1461     "Saturday"];
1462
1463 // private
1464 Date.y2kYear = 50;
1465 // private
1466 Date.monthNumbers = {
1467     Jan:0,
1468     Feb:1,
1469     Mar:2,
1470     Apr:3,
1471     May:4,
1472     Jun:5,
1473     Jul:6,
1474     Aug:7,
1475     Sep:8,
1476     Oct:9,
1477     Nov:10,
1478     Dec:11};
1479
1480 /**
1481  * Creates and returns a new Date instance with the exact same date value as the called instance.
1482  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1483  * variable will also be changed.  When the intention is to create a new variable that will not
1484  * modify the original instance, you should create a clone.
1485  *
1486  * Example of correctly cloning a date:
1487  * <pre><code>
1488 //wrong way:
1489 var orig = new Date('10/1/2006');
1490 var copy = orig;
1491 copy.setDate(5);
1492 document.write(orig);  //returns 'Thu Oct 05 2006'!
1493
1494 //correct way:
1495 var orig = new Date('10/1/2006');
1496 var copy = orig.clone();
1497 copy.setDate(5);
1498 document.write(orig);  //returns 'Thu Oct 01 2006'
1499 </code></pre>
1500  * @return {Date} The new Date instance
1501  */
1502 Date.prototype.clone = function() {
1503         return new Date(this.getTime());
1504 };
1505
1506 /**
1507  * Clears any time information from this date
1508  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1509  @return {Date} this or the clone
1510  */
1511 Date.prototype.clearTime = function(clone){
1512     if(clone){
1513         return this.clone().clearTime();
1514     }
1515     this.setHours(0);
1516     this.setMinutes(0);
1517     this.setSeconds(0);
1518     this.setMilliseconds(0);
1519     return this;
1520 };
1521
1522 // private
1523 // safari setMonth is broken
1524 if(Roo.isSafari){
1525     Date.brokenSetMonth = Date.prototype.setMonth;
1526         Date.prototype.setMonth = function(num){
1527                 if(num <= -1){
1528                         var n = Math.ceil(-num);
1529                         var back_year = Math.ceil(n/12);
1530                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1531                         this.setFullYear(this.getFullYear() - back_year);
1532                         return Date.brokenSetMonth.call(this, month);
1533                 } else {
1534                         return Date.brokenSetMonth.apply(this, arguments);
1535                 }
1536         };
1537 }
1538
1539 /** Date interval constant 
1540 * @static 
1541 * @type String */
1542 Date.MILLI = "ms";
1543 /** Date interval constant 
1544 * @static 
1545 * @type String */
1546 Date.SECOND = "s";
1547 /** Date interval constant 
1548 * @static 
1549 * @type String */
1550 Date.MINUTE = "mi";
1551 /** Date interval constant 
1552 * @static 
1553 * @type String */
1554 Date.HOUR = "h";
1555 /** Date interval constant 
1556 * @static 
1557 * @type String */
1558 Date.DAY = "d";
1559 /** Date interval constant 
1560 * @static 
1561 * @type String */
1562 Date.MONTH = "mo";
1563 /** Date interval constant 
1564 * @static 
1565 * @type String */
1566 Date.YEAR = "y";
1567
1568 /**
1569  * Provides a convenient method of performing basic date arithmetic.  This method
1570  * does not modify the Date instance being called - it creates and returns
1571  * a new Date instance containing the resulting date value.
1572  *
1573  * Examples:
1574  * <pre><code>
1575 //Basic usage:
1576 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1577 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1578
1579 //Negative values will subtract correctly:
1580 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1581 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1582
1583 //You can even chain several calls together in one line!
1584 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1585 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1586  </code></pre>
1587  *
1588  * @param {String} interval   A valid date interval enum value
1589  * @param {Number} value      The amount to add to the current date
1590  * @return {Date} The new Date instance
1591  */
1592 Date.prototype.add = function(interval, value){
1593   var d = this.clone();
1594   if (!interval || value === 0) return d;
1595   switch(interval.toLowerCase()){
1596     case Date.MILLI:
1597       d.setMilliseconds(this.getMilliseconds() + value);
1598       break;
1599     case Date.SECOND:
1600       d.setSeconds(this.getSeconds() + value);
1601       break;
1602     case Date.MINUTE:
1603       d.setMinutes(this.getMinutes() + value);
1604       break;
1605     case Date.HOUR:
1606       d.setHours(this.getHours() + value);
1607       break;
1608     case Date.DAY:
1609       d.setDate(this.getDate() + value);
1610       break;
1611     case Date.MONTH:
1612       var day = this.getDate();
1613       if(day > 28){
1614           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1615       }
1616       d.setDate(day);
1617       d.setMonth(this.getMonth() + value);
1618       break;
1619     case Date.YEAR:
1620       d.setFullYear(this.getFullYear() + value);
1621       break;
1622   }
1623   return d;
1624 };/*
1625  * Based on:
1626  * Ext JS Library 1.1.1
1627  * Copyright(c) 2006-2007, Ext JS, LLC.
1628  *
1629  * Originally Released Under LGPL - original licence link has changed is not relivant.
1630  *
1631  * Fork - LGPL
1632  * <script type="text/javascript">
1633  */
1634
1635 Roo.lib.Dom = {
1636     getViewWidth : function(full) {
1637         return full ? this.getDocumentWidth() : this.getViewportWidth();
1638     },
1639
1640     getViewHeight : function(full) {
1641         return full ? this.getDocumentHeight() : this.getViewportHeight();
1642     },
1643
1644     getDocumentHeight: function() {
1645         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1646         return Math.max(scrollHeight, this.getViewportHeight());
1647     },
1648
1649     getDocumentWidth: function() {
1650         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1651         return Math.max(scrollWidth, this.getViewportWidth());
1652     },
1653
1654     getViewportHeight: function() {
1655         var height = self.innerHeight;
1656         var mode = document.compatMode;
1657
1658         if ((mode || Roo.isIE) && !Roo.isOpera) {
1659             height = (mode == "CSS1Compat") ?
1660                      document.documentElement.clientHeight :
1661                      document.body.clientHeight;
1662         }
1663
1664         return height;
1665     },
1666
1667     getViewportWidth: function() {
1668         var width = self.innerWidth;
1669         var mode = document.compatMode;
1670
1671         if (mode || Roo.isIE) {
1672             width = (mode == "CSS1Compat") ?
1673                     document.documentElement.clientWidth :
1674                     document.body.clientWidth;
1675         }
1676         return width;
1677     },
1678
1679     isAncestor : function(p, c) {
1680         p = Roo.getDom(p);
1681         c = Roo.getDom(c);
1682         if (!p || !c) {
1683             return false;
1684         }
1685
1686         if (p.contains && !Roo.isSafari) {
1687             return p.contains(c);
1688         } else if (p.compareDocumentPosition) {
1689             return !!(p.compareDocumentPosition(c) & 16);
1690         } else {
1691             var parent = c.parentNode;
1692             while (parent) {
1693                 if (parent == p) {
1694                     return true;
1695                 }
1696                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1697                     return false;
1698                 }
1699                 parent = parent.parentNode;
1700             }
1701             return false;
1702         }
1703     },
1704
1705     getRegion : function(el) {
1706         return Roo.lib.Region.getRegion(el);
1707     },
1708
1709     getY : function(el) {
1710         return this.getXY(el)[1];
1711     },
1712
1713     getX : function(el) {
1714         return this.getXY(el)[0];
1715     },
1716
1717     getXY : function(el) {
1718         var p, pe, b, scroll, bd = document.body;
1719         el = Roo.getDom(el);
1720         var fly = Roo.lib.AnimBase.fly;
1721         if (el.getBoundingClientRect) {
1722             b = el.getBoundingClientRect();
1723             scroll = fly(document).getScroll();
1724             return [b.left + scroll.left, b.top + scroll.top];
1725         }
1726         var x = 0, y = 0;
1727
1728         p = el;
1729
1730         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1731
1732         while (p) {
1733
1734             x += p.offsetLeft;
1735             y += p.offsetTop;
1736
1737             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1738                 hasAbsolute = true;
1739             }
1740
1741             if (Roo.isGecko) {
1742                 pe = fly(p);
1743
1744                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1745                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1746
1747
1748                 x += bl;
1749                 y += bt;
1750
1751
1752                 if (p != el && pe.getStyle('overflow') != 'visible') {
1753                     x += bl;
1754                     y += bt;
1755                 }
1756             }
1757             p = p.offsetParent;
1758         }
1759
1760         if (Roo.isSafari && hasAbsolute) {
1761             x -= bd.offsetLeft;
1762             y -= bd.offsetTop;
1763         }
1764
1765         if (Roo.isGecko && !hasAbsolute) {
1766             var dbd = fly(bd);
1767             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1768             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1769         }
1770
1771         p = el.parentNode;
1772         while (p && p != bd) {
1773             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1774                 x -= p.scrollLeft;
1775                 y -= p.scrollTop;
1776             }
1777             p = p.parentNode;
1778         }
1779         return [x, y];
1780     },
1781  
1782   
1783
1784
1785     setXY : function(el, xy) {
1786         el = Roo.fly(el, '_setXY');
1787         el.position();
1788         var pts = el.translatePoints(xy);
1789         if (xy[0] !== false) {
1790             el.dom.style.left = pts.left + "px";
1791         }
1792         if (xy[1] !== false) {
1793             el.dom.style.top = pts.top + "px";
1794         }
1795     },
1796
1797     setX : function(el, x) {
1798         this.setXY(el, [x, false]);
1799     },
1800
1801     setY : function(el, y) {
1802         this.setXY(el, [false, y]);
1803     }
1804 };
1805 /*
1806  * Portions of this file are based on pieces of Yahoo User Interface Library
1807  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1808  * YUI licensed under the BSD License:
1809  * http://developer.yahoo.net/yui/license.txt
1810  * <script type="text/javascript">
1811  *
1812  */
1813
1814 Roo.lib.Event = function() {
1815     var loadComplete = false;
1816     var listeners = [];
1817     var unloadListeners = [];
1818     var retryCount = 0;
1819     var onAvailStack = [];
1820     var counter = 0;
1821     var lastError = null;
1822
1823     return {
1824         POLL_RETRYS: 200,
1825         POLL_INTERVAL: 20,
1826         EL: 0,
1827         TYPE: 1,
1828         FN: 2,
1829         WFN: 3,
1830         OBJ: 3,
1831         ADJ_SCOPE: 4,
1832         _interval: null,
1833
1834         startInterval: function() {
1835             if (!this._interval) {
1836                 var self = this;
1837                 var callback = function() {
1838                     self._tryPreloadAttach();
1839                 };
1840                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1841
1842             }
1843         },
1844
1845         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1846             onAvailStack.push({ id:         p_id,
1847                 fn:         p_fn,
1848                 obj:        p_obj,
1849                 override:   p_override,
1850                 checkReady: false    });
1851
1852             retryCount = this.POLL_RETRYS;
1853             this.startInterval();
1854         },
1855
1856
1857         addListener: function(el, eventName, fn) {
1858             el = Roo.getDom(el);
1859             if (!el || !fn) {
1860                 return false;
1861             }
1862
1863             if ("unload" == eventName) {
1864                 unloadListeners[unloadListeners.length] =
1865                 [el, eventName, fn];
1866                 return true;
1867             }
1868
1869             var wrappedFn = function(e) {
1870                 return fn(Roo.lib.Event.getEvent(e));
1871             };
1872
1873             var li = [el, eventName, fn, wrappedFn];
1874
1875             var index = listeners.length;
1876             listeners[index] = li;
1877
1878             this.doAdd(el, eventName, wrappedFn, false);
1879             return true;
1880
1881         },
1882
1883
1884         removeListener: function(el, eventName, fn) {
1885             var i, len;
1886
1887             el = Roo.getDom(el);
1888
1889             if(!fn) {
1890                 return this.purgeElement(el, false, eventName);
1891             }
1892
1893
1894             if ("unload" == eventName) {
1895
1896                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1897                     var li = unloadListeners[i];
1898                     if (li &&
1899                         li[0] == el &&
1900                         li[1] == eventName &&
1901                         li[2] == fn) {
1902                         unloadListeners.splice(i, 1);
1903                         return true;
1904                     }
1905                 }
1906
1907                 return false;
1908             }
1909
1910             var cacheItem = null;
1911
1912
1913             var index = arguments[3];
1914
1915             if ("undefined" == typeof index) {
1916                 index = this._getCacheIndex(el, eventName, fn);
1917             }
1918
1919             if (index >= 0) {
1920                 cacheItem = listeners[index];
1921             }
1922
1923             if (!el || !cacheItem) {
1924                 return false;
1925             }
1926
1927             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1928
1929             delete listeners[index][this.WFN];
1930             delete listeners[index][this.FN];
1931             listeners.splice(index, 1);
1932
1933             return true;
1934
1935         },
1936
1937
1938         getTarget: function(ev, resolveTextNode) {
1939             ev = ev.browserEvent || ev;
1940             var t = ev.target || ev.srcElement;
1941             return this.resolveTextNode(t);
1942         },
1943
1944
1945         resolveTextNode: function(node) {
1946             if (Roo.isSafari && node && 3 == node.nodeType) {
1947                 return node.parentNode;
1948             } else {
1949                 return node;
1950             }
1951         },
1952
1953
1954         getPageX: function(ev) {
1955             ev = ev.browserEvent || ev;
1956             var x = ev.pageX;
1957             if (!x && 0 !== x) {
1958                 x = ev.clientX || 0;
1959
1960                 if (Roo.isIE) {
1961                     x += this.getScroll()[1];
1962                 }
1963             }
1964
1965             return x;
1966         },
1967
1968
1969         getPageY: function(ev) {
1970             ev = ev.browserEvent || ev;
1971             var y = ev.pageY;
1972             if (!y && 0 !== y) {
1973                 y = ev.clientY || 0;
1974
1975                 if (Roo.isIE) {
1976                     y += this.getScroll()[0];
1977                 }
1978             }
1979
1980
1981             return y;
1982         },
1983
1984
1985         getXY: function(ev) {
1986             ev = ev.browserEvent || ev;
1987             return [this.getPageX(ev), this.getPageY(ev)];
1988         },
1989
1990
1991         getRelatedTarget: function(ev) {
1992             ev = ev.browserEvent || ev;
1993             var t = ev.relatedTarget;
1994             if (!t) {
1995                 if (ev.type == "mouseout") {
1996                     t = ev.toElement;
1997                 } else if (ev.type == "mouseover") {
1998                     t = ev.fromElement;
1999                 }
2000             }
2001
2002             return this.resolveTextNode(t);
2003         },
2004
2005
2006         getTime: function(ev) {
2007             ev = ev.browserEvent || ev;
2008             if (!ev.time) {
2009                 var t = new Date().getTime();
2010                 try {
2011                     ev.time = t;
2012                 } catch(ex) {
2013                     this.lastError = ex;
2014                     return t;
2015                 }
2016             }
2017
2018             return ev.time;
2019         },
2020
2021
2022         stopEvent: function(ev) {
2023             this.stopPropagation(ev);
2024             this.preventDefault(ev);
2025         },
2026
2027
2028         stopPropagation: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             if (ev.stopPropagation) {
2031                 ev.stopPropagation();
2032             } else {
2033                 ev.cancelBubble = true;
2034             }
2035         },
2036
2037
2038         preventDefault: function(ev) {
2039             ev = ev.browserEvent || ev;
2040             if(ev.preventDefault) {
2041                 ev.preventDefault();
2042             } else {
2043                 ev.returnValue = false;
2044             }
2045         },
2046
2047
2048         getEvent: function(e) {
2049             var ev = e || window.event;
2050             if (!ev) {
2051                 var c = this.getEvent.caller;
2052                 while (c) {
2053                     ev = c.arguments[0];
2054                     if (ev && Event == ev.constructor) {
2055                         break;
2056                     }
2057                     c = c.caller;
2058                 }
2059             }
2060             return ev;
2061         },
2062
2063
2064         getCharCode: function(ev) {
2065             ev = ev.browserEvent || ev;
2066             return ev.charCode || ev.keyCode || 0;
2067         },
2068
2069
2070         _getCacheIndex: function(el, eventName, fn) {
2071             for (var i = 0,len = listeners.length; i < len; ++i) {
2072                 var li = listeners[i];
2073                 if (li &&
2074                     li[this.FN] == fn &&
2075                     li[this.EL] == el &&
2076                     li[this.TYPE] == eventName) {
2077                     return i;
2078                 }
2079             }
2080
2081             return -1;
2082         },
2083
2084
2085         elCache: {},
2086
2087
2088         getEl: function(id) {
2089             return document.getElementById(id);
2090         },
2091
2092
2093         clearCache: function() {
2094         },
2095
2096
2097         _load: function(e) {
2098             loadComplete = true;
2099             var EU = Roo.lib.Event;
2100
2101
2102             if (Roo.isIE) {
2103                 EU.doRemove(window, "load", EU._load);
2104             }
2105         },
2106
2107
2108         _tryPreloadAttach: function() {
2109
2110             if (this.locked) {
2111                 return false;
2112             }
2113
2114             this.locked = true;
2115
2116
2117             var tryAgain = !loadComplete;
2118             if (!tryAgain) {
2119                 tryAgain = (retryCount > 0);
2120             }
2121
2122
2123             var notAvail = [];
2124             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2125                 var item = onAvailStack[i];
2126                 if (item) {
2127                     var el = this.getEl(item.id);
2128
2129                     if (el) {
2130                         if (!item.checkReady ||
2131                             loadComplete ||
2132                             el.nextSibling ||
2133                             (document && document.body)) {
2134
2135                             var scope = el;
2136                             if (item.override) {
2137                                 if (item.override === true) {
2138                                     scope = item.obj;
2139                                 } else {
2140                                     scope = item.override;
2141                                 }
2142                             }
2143                             item.fn.call(scope, item.obj);
2144                             onAvailStack[i] = null;
2145                         }
2146                     } else {
2147                         notAvail.push(item);
2148                     }
2149                 }
2150             }
2151
2152             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2153
2154             if (tryAgain) {
2155
2156                 this.startInterval();
2157             } else {
2158                 clearInterval(this._interval);
2159                 this._interval = null;
2160             }
2161
2162             this.locked = false;
2163
2164             return true;
2165
2166         },
2167
2168
2169         purgeElement: function(el, recurse, eventName) {
2170             var elListeners = this.getListeners(el, eventName);
2171             if (elListeners) {
2172                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2173                     var l = elListeners[i];
2174                     this.removeListener(el, l.type, l.fn);
2175                 }
2176             }
2177
2178             if (recurse && el && el.childNodes) {
2179                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2180                     this.purgeElement(el.childNodes[i], recurse, eventName);
2181                 }
2182             }
2183         },
2184
2185
2186         getListeners: function(el, eventName) {
2187             var results = [], searchLists;
2188             if (!eventName) {
2189                 searchLists = [listeners, unloadListeners];
2190             } else if (eventName == "unload") {
2191                 searchLists = [unloadListeners];
2192             } else {
2193                 searchLists = [listeners];
2194             }
2195
2196             for (var j = 0; j < searchLists.length; ++j) {
2197                 var searchList = searchLists[j];
2198                 if (searchList && searchList.length > 0) {
2199                     for (var i = 0,len = searchList.length; i < len; ++i) {
2200                         var l = searchList[i];
2201                         if (l && l[this.EL] === el &&
2202                             (!eventName || eventName === l[this.TYPE])) {
2203                             results.push({
2204                                 type:   l[this.TYPE],
2205                                 fn:     l[this.FN],
2206                                 obj:    l[this.OBJ],
2207                                 adjust: l[this.ADJ_SCOPE],
2208                                 index:  i
2209                             });
2210                         }
2211                     }
2212                 }
2213             }
2214
2215             return (results.length) ? results : null;
2216         },
2217
2218
2219         _unload: function(e) {
2220
2221             var EU = Roo.lib.Event, i, j, l, len, index;
2222
2223             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2224                 l = unloadListeners[i];
2225                 if (l) {
2226                     var scope = window;
2227                     if (l[EU.ADJ_SCOPE]) {
2228                         if (l[EU.ADJ_SCOPE] === true) {
2229                             scope = l[EU.OBJ];
2230                         } else {
2231                             scope = l[EU.ADJ_SCOPE];
2232                         }
2233                     }
2234                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2235                     unloadListeners[i] = null;
2236                     l = null;
2237                     scope = null;
2238                 }
2239             }
2240
2241             unloadListeners = null;
2242
2243             if (listeners && listeners.length > 0) {
2244                 j = listeners.length;
2245                 while (j) {
2246                     index = j - 1;
2247                     l = listeners[index];
2248                     if (l) {
2249                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2250                                 l[EU.FN], index);
2251                     }
2252                     j = j - 1;
2253                 }
2254                 l = null;
2255
2256                 EU.clearCache();
2257             }
2258
2259             EU.doRemove(window, "unload", EU._unload);
2260
2261         },
2262
2263
2264         getScroll: function() {
2265             var dd = document.documentElement, db = document.body;
2266             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2267                 return [dd.scrollTop, dd.scrollLeft];
2268             } else if (db) {
2269                 return [db.scrollTop, db.scrollLeft];
2270             } else {
2271                 return [0, 0];
2272             }
2273         },
2274
2275
2276         doAdd: function () {
2277             if (window.addEventListener) {
2278                 return function(el, eventName, fn, capture) {
2279                     el.addEventListener(eventName, fn, (capture));
2280                 };
2281             } else if (window.attachEvent) {
2282                 return function(el, eventName, fn, capture) {
2283                     el.attachEvent("on" + eventName, fn);
2284                 };
2285             } else {
2286                 return function() {
2287                 };
2288             }
2289         }(),
2290
2291
2292         doRemove: function() {
2293             if (window.removeEventListener) {
2294                 return function (el, eventName, fn, capture) {
2295                     el.removeEventListener(eventName, fn, (capture));
2296                 };
2297             } else if (window.detachEvent) {
2298                 return function (el, eventName, fn) {
2299                     el.detachEvent("on" + eventName, fn);
2300                 };
2301             } else {
2302                 return function() {
2303                 };
2304             }
2305         }()
2306     };
2307     
2308 }();
2309 (function() {     
2310    
2311     var E = Roo.lib.Event;
2312     E.on = E.addListener;
2313     E.un = E.removeListener;
2314
2315     if (document && document.body) {
2316         E._load();
2317     } else {
2318         E.doAdd(window, "load", E._load);
2319     }
2320     E.doAdd(window, "unload", E._unload);
2321     E._tryPreloadAttach();
2322 })();
2323
2324 /*
2325  * Portions of this file are based on pieces of Yahoo User Interface Library
2326  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2327  * YUI licensed under the BSD License:
2328  * http://developer.yahoo.net/yui/license.txt
2329  * <script type="text/javascript">
2330  *
2331  */
2332
2333 (function() {
2334     
2335     Roo.lib.Ajax = {
2336         request : function(method, uri, cb, data, options) {
2337             if(options){
2338                 var hs = options.headers;
2339                 if(hs){
2340                     for(var h in hs){
2341                         if(hs.hasOwnProperty(h)){
2342                             this.initHeader(h, hs[h], false);
2343                         }
2344                     }
2345                 }
2346                 if(options.xmlData){
2347                     this.initHeader('Content-Type', 'text/xml', false);
2348                     method = 'POST';
2349                     data = options.xmlData;
2350                 }
2351             }
2352
2353             return this.asyncRequest(method, uri, cb, data);
2354         },
2355
2356         serializeForm : function(form) {
2357             if(typeof form == 'string') {
2358                 form = (document.getElementById(form) || document.forms[form]);
2359             }
2360
2361             var el, name, val, disabled, data = '', hasSubmit = false;
2362             for (var i = 0; i < form.elements.length; i++) {
2363                 el = form.elements[i];
2364                 disabled = form.elements[i].disabled;
2365                 name = form.elements[i].name;
2366                 val = form.elements[i].value;
2367
2368                 if (!disabled && name){
2369                     switch (el.type)
2370                             {
2371                         case 'select-one':
2372                         case 'select-multiple':
2373                             for (var j = 0; j < el.options.length; j++) {
2374                                 if (el.options[j].selected) {
2375                                     if (Roo.isIE) {
2376                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2377                                     }
2378                                     else {
2379                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2380                                     }
2381                                 }
2382                             }
2383                             break;
2384                         case 'radio':
2385                         case 'checkbox':
2386                             if (el.checked) {
2387                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2388                             }
2389                             break;
2390                         case 'file':
2391
2392                         case undefined:
2393
2394                         case 'reset':
2395
2396                         case 'button':
2397
2398                             break;
2399                         case 'submit':
2400                             if(hasSubmit == false) {
2401                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2402                                 hasSubmit = true;
2403                             }
2404                             break;
2405                         default:
2406                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2407                             break;
2408                     }
2409                 }
2410             }
2411             data = data.substr(0, data.length - 1);
2412             return data;
2413         },
2414
2415         headers:{},
2416
2417         hasHeaders:false,
2418
2419         useDefaultHeader:true,
2420
2421         defaultPostHeader:'application/x-www-form-urlencoded',
2422
2423         useDefaultXhrHeader:true,
2424
2425         defaultXhrHeader:'XMLHttpRequest',
2426
2427         hasDefaultHeaders:true,
2428
2429         defaultHeaders:{},
2430
2431         poll:{},
2432
2433         timeout:{},
2434
2435         pollInterval:50,
2436
2437         transactionId:0,
2438
2439         setProgId:function(id)
2440         {
2441             this.activeX.unshift(id);
2442         },
2443
2444         setDefaultPostHeader:function(b)
2445         {
2446             this.useDefaultHeader = b;
2447         },
2448
2449         setDefaultXhrHeader:function(b)
2450         {
2451             this.useDefaultXhrHeader = b;
2452         },
2453
2454         setPollingInterval:function(i)
2455         {
2456             if (typeof i == 'number' && isFinite(i)) {
2457                 this.pollInterval = i;
2458             }
2459         },
2460
2461         createXhrObject:function(transactionId)
2462         {
2463             var obj,http;
2464             try
2465             {
2466
2467                 http = new XMLHttpRequest();
2468
2469                 obj = { conn:http, tId:transactionId };
2470             }
2471             catch(e)
2472             {
2473                 for (var i = 0; i < this.activeX.length; ++i) {
2474                     try
2475                     {
2476
2477                         http = new ActiveXObject(this.activeX[i]);
2478
2479                         obj = { conn:http, tId:transactionId };
2480                         break;
2481                     }
2482                     catch(e) {
2483                     }
2484                 }
2485             }
2486             finally
2487             {
2488                 return obj;
2489             }
2490         },
2491
2492         getConnectionObject:function()
2493         {
2494             var o;
2495             var tId = this.transactionId;
2496
2497             try
2498             {
2499                 o = this.createXhrObject(tId);
2500                 if (o) {
2501                     this.transactionId++;
2502                 }
2503             }
2504             catch(e) {
2505             }
2506             finally
2507             {
2508                 return o;
2509             }
2510         },
2511
2512         asyncRequest:function(method, uri, callback, postData)
2513         {
2514             var o = this.getConnectionObject();
2515
2516             if (!o) {
2517                 return null;
2518             }
2519             else {
2520                 o.conn.open(method, uri, true);
2521
2522                 if (this.useDefaultXhrHeader) {
2523                     if (!this.defaultHeaders['X-Requested-With']) {
2524                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2525                     }
2526                 }
2527
2528                 if(postData && this.useDefaultHeader){
2529                     this.initHeader('Content-Type', this.defaultPostHeader);
2530                 }
2531
2532                  if (this.hasDefaultHeaders || this.hasHeaders) {
2533                     this.setHeader(o);
2534                 }
2535
2536                 this.handleReadyState(o, callback);
2537                 o.conn.send(postData || null);
2538
2539                 return o;
2540             }
2541         },
2542
2543         handleReadyState:function(o, callback)
2544         {
2545             var oConn = this;
2546
2547             if (callback && callback.timeout) {
2548                 this.timeout[o.tId] = window.setTimeout(function() {
2549                     oConn.abort(o, callback, true);
2550                 }, callback.timeout);
2551             }
2552
2553             this.poll[o.tId] = window.setInterval(
2554                     function() {
2555                         if (o.conn && o.conn.readyState == 4) {
2556                             window.clearInterval(oConn.poll[o.tId]);
2557                             delete oConn.poll[o.tId];
2558
2559                             if(callback && callback.timeout) {
2560                                 window.clearTimeout(oConn.timeout[o.tId]);
2561                                 delete oConn.timeout[o.tId];
2562                             }
2563
2564                             oConn.handleTransactionResponse(o, callback);
2565                         }
2566                     }
2567                     , this.pollInterval);
2568         },
2569
2570         handleTransactionResponse:function(o, callback, isAbort)
2571         {
2572
2573             if (!callback) {
2574                 this.releaseObject(o);
2575                 return;
2576             }
2577
2578             var httpStatus, responseObject;
2579
2580             try
2581             {
2582                 if (o.conn.status !== undefined && o.conn.status != 0) {
2583                     httpStatus = o.conn.status;
2584                 }
2585                 else {
2586                     httpStatus = 13030;
2587                 }
2588             }
2589             catch(e) {
2590
2591
2592                 httpStatus = 13030;
2593             }
2594
2595             if (httpStatus >= 200 && httpStatus < 300) {
2596                 responseObject = this.createResponseObject(o, callback.argument);
2597                 if (callback.success) {
2598                     if (!callback.scope) {
2599                         callback.success(responseObject);
2600                     }
2601                     else {
2602
2603
2604                         callback.success.apply(callback.scope, [responseObject]);
2605                     }
2606                 }
2607             }
2608             else {
2609                 switch (httpStatus) {
2610
2611                     case 12002:
2612                     case 12029:
2613                     case 12030:
2614                     case 12031:
2615                     case 12152:
2616                     case 13030:
2617                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2618                         if (callback.failure) {
2619                             if (!callback.scope) {
2620                                 callback.failure(responseObject);
2621                             }
2622                             else {
2623                                 callback.failure.apply(callback.scope, [responseObject]);
2624                             }
2625                         }
2626                         break;
2627                     default:
2628                         responseObject = this.createResponseObject(o, callback.argument);
2629                         if (callback.failure) {
2630                             if (!callback.scope) {
2631                                 callback.failure(responseObject);
2632                             }
2633                             else {
2634                                 callback.failure.apply(callback.scope, [responseObject]);
2635                             }
2636                         }
2637                 }
2638             }
2639
2640             this.releaseObject(o);
2641             responseObject = null;
2642         },
2643
2644         createResponseObject:function(o, callbackArg)
2645         {
2646             var obj = {};
2647             var headerObj = {};
2648
2649             try
2650             {
2651                 var headerStr = o.conn.getAllResponseHeaders();
2652                 var header = headerStr.split('\n');
2653                 for (var i = 0; i < header.length; i++) {
2654                     var delimitPos = header[i].indexOf(':');
2655                     if (delimitPos != -1) {
2656                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2657                     }
2658                 }
2659             }
2660             catch(e) {
2661             }
2662
2663             obj.tId = o.tId;
2664             obj.status = o.conn.status;
2665             obj.statusText = o.conn.statusText;
2666             obj.getResponseHeader = headerObj;
2667             obj.getAllResponseHeaders = headerStr;
2668             obj.responseText = o.conn.responseText;
2669             obj.responseXML = o.conn.responseXML;
2670
2671             if (typeof callbackArg !== undefined) {
2672                 obj.argument = callbackArg;
2673             }
2674
2675             return obj;
2676         },
2677
2678         createExceptionObject:function(tId, callbackArg, isAbort)
2679         {
2680             var COMM_CODE = 0;
2681             var COMM_ERROR = 'communication failure';
2682             var ABORT_CODE = -1;
2683             var ABORT_ERROR = 'transaction aborted';
2684
2685             var obj = {};
2686
2687             obj.tId = tId;
2688             if (isAbort) {
2689                 obj.status = ABORT_CODE;
2690                 obj.statusText = ABORT_ERROR;
2691             }
2692             else {
2693                 obj.status = COMM_CODE;
2694                 obj.statusText = COMM_ERROR;
2695             }
2696
2697             if (callbackArg) {
2698                 obj.argument = callbackArg;
2699             }
2700
2701             return obj;
2702         },
2703
2704         initHeader:function(label, value, isDefault)
2705         {
2706             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2707
2708             if (headerObj[label] === undefined) {
2709                 headerObj[label] = value;
2710             }
2711             else {
2712
2713
2714                 headerObj[label] = value + "," + headerObj[label];
2715             }
2716
2717             if (isDefault) {
2718                 this.hasDefaultHeaders = true;
2719             }
2720             else {
2721                 this.hasHeaders = true;
2722             }
2723         },
2724
2725
2726         setHeader:function(o)
2727         {
2728             if (this.hasDefaultHeaders) {
2729                 for (var prop in this.defaultHeaders) {
2730                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2731                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2732                     }
2733                 }
2734             }
2735
2736             if (this.hasHeaders) {
2737                 for (var prop in this.headers) {
2738                     if (this.headers.hasOwnProperty(prop)) {
2739                         o.conn.setRequestHeader(prop, this.headers[prop]);
2740                     }
2741                 }
2742                 this.headers = {};
2743                 this.hasHeaders = false;
2744             }
2745         },
2746
2747         resetDefaultHeaders:function() {
2748             delete this.defaultHeaders;
2749             this.defaultHeaders = {};
2750             this.hasDefaultHeaders = false;
2751         },
2752
2753         abort:function(o, callback, isTimeout)
2754         {
2755             if(this.isCallInProgress(o)) {
2756                 o.conn.abort();
2757                 window.clearInterval(this.poll[o.tId]);
2758                 delete this.poll[o.tId];
2759                 if (isTimeout) {
2760                     delete this.timeout[o.tId];
2761                 }
2762
2763                 this.handleTransactionResponse(o, callback, true);
2764
2765                 return true;
2766             }
2767             else {
2768                 return false;
2769             }
2770         },
2771
2772
2773         isCallInProgress:function(o)
2774         {
2775             if (o && o.conn) {
2776                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2777             }
2778             else {
2779
2780                 return false;
2781             }
2782         },
2783
2784
2785         releaseObject:function(o)
2786         {
2787
2788             o.conn = null;
2789
2790             o = null;
2791         },
2792
2793         activeX:[
2794         'MSXML2.XMLHTTP.3.0',
2795         'MSXML2.XMLHTTP',
2796         'Microsoft.XMLHTTP'
2797         ]
2798
2799
2800     };
2801 })();/*
2802  * Portions of this file are based on pieces of Yahoo User Interface Library
2803  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2804  * YUI licensed under the BSD License:
2805  * http://developer.yahoo.net/yui/license.txt
2806  * <script type="text/javascript">
2807  *
2808  */
2809
2810 Roo.lib.Region = function(t, r, b, l) {
2811     this.top = t;
2812     this[1] = t;
2813     this.right = r;
2814     this.bottom = b;
2815     this.left = l;
2816     this[0] = l;
2817 };
2818
2819
2820 Roo.lib.Region.prototype = {
2821     contains : function(region) {
2822         return ( region.left >= this.left &&
2823                  region.right <= this.right &&
2824                  region.top >= this.top &&
2825                  region.bottom <= this.bottom    );
2826
2827     },
2828
2829     getArea : function() {
2830         return ( (this.bottom - this.top) * (this.right - this.left) );
2831     },
2832
2833     intersect : function(region) {
2834         var t = Math.max(this.top, region.top);
2835         var r = Math.min(this.right, region.right);
2836         var b = Math.min(this.bottom, region.bottom);
2837         var l = Math.max(this.left, region.left);
2838
2839         if (b >= t && r >= l) {
2840             return new Roo.lib.Region(t, r, b, l);
2841         } else {
2842             return null;
2843         }
2844     },
2845     union : function(region) {
2846         var t = Math.min(this.top, region.top);
2847         var r = Math.max(this.right, region.right);
2848         var b = Math.max(this.bottom, region.bottom);
2849         var l = Math.min(this.left, region.left);
2850
2851         return new Roo.lib.Region(t, r, b, l);
2852     },
2853
2854     adjust : function(t, l, b, r) {
2855         this.top += t;
2856         this.left += l;
2857         this.right += r;
2858         this.bottom += b;
2859         return this;
2860     }
2861 };
2862
2863 Roo.lib.Region.getRegion = function(el) {
2864     var p = Roo.lib.Dom.getXY(el);
2865
2866     var t = p[1];
2867     var r = p[0] + el.offsetWidth;
2868     var b = p[1] + el.offsetHeight;
2869     var l = p[0];
2870
2871     return new Roo.lib.Region(t, r, b, l);
2872 };
2873 /*
2874  * Portions of this file are based on pieces of Yahoo User Interface Library
2875  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2876  * YUI licensed under the BSD License:
2877  * http://developer.yahoo.net/yui/license.txt
2878  * <script type="text/javascript">
2879  *
2880  */
2881 //@@dep Roo.lib.Region
2882
2883
2884 Roo.lib.Point = function(x, y) {
2885     if (x instanceof Array) {
2886         y = x[1];
2887         x = x[0];
2888     }
2889     this.x = this.right = this.left = this[0] = x;
2890     this.y = this.top = this.bottom = this[1] = y;
2891 };
2892
2893 Roo.lib.Point.prototype = new Roo.lib.Region();
2894 /*
2895  * Portions of this file are based on pieces of Yahoo User Interface Library
2896  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2897  * YUI licensed under the BSD License:
2898  * http://developer.yahoo.net/yui/license.txt
2899  * <script type="text/javascript">
2900  *
2901  */
2902  
2903 (function() {   
2904
2905     Roo.lib.Anim = {
2906         scroll : function(el, args, duration, easing, cb, scope) {
2907             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2908         },
2909
2910         motion : function(el, args, duration, easing, cb, scope) {
2911             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2912         },
2913
2914         color : function(el, args, duration, easing, cb, scope) {
2915             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2916         },
2917
2918         run : function(el, args, duration, easing, cb, scope, type) {
2919             type = type || Roo.lib.AnimBase;
2920             if (typeof easing == "string") {
2921                 easing = Roo.lib.Easing[easing];
2922             }
2923             var anim = new type(el, args, duration, easing);
2924             anim.animateX(function() {
2925                 Roo.callback(cb, scope);
2926             });
2927             return anim;
2928         }
2929     };
2930 })();/*
2931  * Portions of this file are based on pieces of Yahoo User Interface Library
2932  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2933  * YUI licensed under the BSD License:
2934  * http://developer.yahoo.net/yui/license.txt
2935  * <script type="text/javascript">
2936  *
2937  */
2938
2939 (function() {    
2940     var libFlyweight;
2941     
2942     function fly(el) {
2943         if (!libFlyweight) {
2944             libFlyweight = new Roo.Element.Flyweight();
2945         }
2946         libFlyweight.dom = el;
2947         return libFlyweight;
2948     }
2949
2950     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
2951     
2952    
2953     
2954     Roo.lib.AnimBase = function(el, attributes, duration, method) {
2955         if (el) {
2956             this.init(el, attributes, duration, method);
2957         }
2958     };
2959
2960     Roo.lib.AnimBase.fly = fly;
2961     
2962     
2963     
2964     Roo.lib.AnimBase.prototype = {
2965
2966         toString: function() {
2967             var el = this.getEl();
2968             var id = el.id || el.tagName;
2969             return ("Anim " + id);
2970         },
2971
2972         patterns: {
2973             noNegatives:        /width|height|opacity|padding/i,
2974             offsetAttribute:  /^((width|height)|(top|left))$/,
2975             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
2976             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
2977         },
2978
2979
2980         doMethod: function(attr, start, end) {
2981             return this.method(this.currentFrame, start, end - start, this.totalFrames);
2982         },
2983
2984
2985         setAttribute: function(attr, val, unit) {
2986             if (this.patterns.noNegatives.test(attr)) {
2987                 val = (val > 0) ? val : 0;
2988             }
2989
2990             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
2991         },
2992
2993
2994         getAttribute: function(attr) {
2995             var el = this.getEl();
2996             var val = fly(el).getStyle(attr);
2997
2998             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
2999                 return parseFloat(val);
3000             }
3001
3002             var a = this.patterns.offsetAttribute.exec(attr) || [];
3003             var pos = !!( a[3] );
3004             var box = !!( a[2] );
3005
3006
3007             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3008                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3009             } else {
3010                 val = 0;
3011             }
3012
3013             return val;
3014         },
3015
3016
3017         getDefaultUnit: function(attr) {
3018             if (this.patterns.defaultUnit.test(attr)) {
3019                 return 'px';
3020             }
3021
3022             return '';
3023         },
3024
3025         animateX : function(callback, scope) {
3026             var f = function() {
3027                 this.onComplete.removeListener(f);
3028                 if (typeof callback == "function") {
3029                     callback.call(scope || this, this);
3030                 }
3031             };
3032             this.onComplete.addListener(f, this);
3033             this.animate();
3034         },
3035
3036
3037         setRuntimeAttribute: function(attr) {
3038             var start;
3039             var end;
3040             var attributes = this.attributes;
3041
3042             this.runtimeAttributes[attr] = {};
3043
3044             var isset = function(prop) {
3045                 return (typeof prop !== 'undefined');
3046             };
3047
3048             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3049                 return false;
3050             }
3051
3052             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3053
3054
3055             if (isset(attributes[attr]['to'])) {
3056                 end = attributes[attr]['to'];
3057             } else if (isset(attributes[attr]['by'])) {
3058                 if (start.constructor == Array) {
3059                     end = [];
3060                     for (var i = 0, len = start.length; i < len; ++i) {
3061                         end[i] = start[i] + attributes[attr]['by'][i];
3062                     }
3063                 } else {
3064                     end = start + attributes[attr]['by'];
3065                 }
3066             }
3067
3068             this.runtimeAttributes[attr].start = start;
3069             this.runtimeAttributes[attr].end = end;
3070
3071
3072             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3073         },
3074
3075
3076         init: function(el, attributes, duration, method) {
3077
3078             var isAnimated = false;
3079
3080
3081             var startTime = null;
3082
3083
3084             var actualFrames = 0;
3085
3086
3087             el = Roo.getDom(el);
3088
3089
3090             this.attributes = attributes || {};
3091
3092
3093             this.duration = duration || 1;
3094
3095
3096             this.method = method || Roo.lib.Easing.easeNone;
3097
3098
3099             this.useSeconds = true;
3100
3101
3102             this.currentFrame = 0;
3103
3104
3105             this.totalFrames = Roo.lib.AnimMgr.fps;
3106
3107
3108             this.getEl = function() {
3109                 return el;
3110             };
3111
3112
3113             this.isAnimated = function() {
3114                 return isAnimated;
3115             };
3116
3117
3118             this.getStartTime = function() {
3119                 return startTime;
3120             };
3121
3122             this.runtimeAttributes = {};
3123
3124
3125             this.animate = function() {
3126                 if (this.isAnimated()) {
3127                     return false;
3128                 }
3129
3130                 this.currentFrame = 0;
3131
3132                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3133
3134                 Roo.lib.AnimMgr.registerElement(this);
3135             };
3136
3137
3138             this.stop = function(finish) {
3139                 if (finish) {
3140                     this.currentFrame = this.totalFrames;
3141                     this._onTween.fire();
3142                 }
3143                 Roo.lib.AnimMgr.stop(this);
3144             };
3145
3146             var onStart = function() {
3147                 this.onStart.fire();
3148
3149                 this.runtimeAttributes = {};
3150                 for (var attr in this.attributes) {
3151                     this.setRuntimeAttribute(attr);
3152                 }
3153
3154                 isAnimated = true;
3155                 actualFrames = 0;
3156                 startTime = new Date();
3157             };
3158
3159
3160             var onTween = function() {
3161                 var data = {
3162                     duration: new Date() - this.getStartTime(),
3163                     currentFrame: this.currentFrame
3164                 };
3165
3166                 data.toString = function() {
3167                     return (
3168                             'duration: ' + data.duration +
3169                             ', currentFrame: ' + data.currentFrame
3170                             );
3171                 };
3172
3173                 this.onTween.fire(data);
3174
3175                 var runtimeAttributes = this.runtimeAttributes;
3176
3177                 for (var attr in runtimeAttributes) {
3178                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3179                 }
3180
3181                 actualFrames += 1;
3182             };
3183
3184             var onComplete = function() {
3185                 var actual_duration = (new Date() - startTime) / 1000 ;
3186
3187                 var data = {
3188                     duration: actual_duration,
3189                     frames: actualFrames,
3190                     fps: actualFrames / actual_duration
3191                 };
3192
3193                 data.toString = function() {
3194                     return (
3195                             'duration: ' + data.duration +
3196                             ', frames: ' + data.frames +
3197                             ', fps: ' + data.fps
3198                             );
3199                 };
3200
3201                 isAnimated = false;
3202                 actualFrames = 0;
3203                 this.onComplete.fire(data);
3204             };
3205
3206
3207             this._onStart = new Roo.util.Event(this);
3208             this.onStart = new Roo.util.Event(this);
3209             this.onTween = new Roo.util.Event(this);
3210             this._onTween = new Roo.util.Event(this);
3211             this.onComplete = new Roo.util.Event(this);
3212             this._onComplete = new Roo.util.Event(this);
3213             this._onStart.addListener(onStart);
3214             this._onTween.addListener(onTween);
3215             this._onComplete.addListener(onComplete);
3216         }
3217     };
3218 })();
3219 /*
3220  * Portions of this file are based on pieces of Yahoo User Interface Library
3221  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3222  * YUI licensed under the BSD License:
3223  * http://developer.yahoo.net/yui/license.txt
3224  * <script type="text/javascript">
3225  *
3226  */
3227
3228 Roo.lib.AnimMgr = new function() {
3229
3230         var thread = null;
3231
3232
3233         var queue = [];
3234
3235
3236         var tweenCount = 0;
3237
3238
3239         this.fps = 1000;
3240
3241
3242         this.delay = 1;
3243
3244
3245         this.registerElement = function(tween) {
3246             queue[queue.length] = tween;
3247             tweenCount += 1;
3248             tween._onStart.fire();
3249             this.start();
3250         };
3251
3252
3253         this.unRegister = function(tween, index) {
3254             tween._onComplete.fire();
3255             index = index || getIndex(tween);
3256             if (index != -1) {
3257                 queue.splice(index, 1);
3258             }
3259
3260             tweenCount -= 1;
3261             if (tweenCount <= 0) {
3262                 this.stop();
3263             }
3264         };
3265
3266
3267         this.start = function() {
3268             if (thread === null) {
3269                 thread = setInterval(this.run, this.delay);
3270             }
3271         };
3272
3273
3274         this.stop = function(tween) {
3275             if (!tween) {
3276                 clearInterval(thread);
3277
3278                 for (var i = 0, len = queue.length; i < len; ++i) {
3279                     if (queue[0].isAnimated()) {
3280                         this.unRegister(queue[0], 0);
3281                     }
3282                 }
3283
3284                 queue = [];
3285                 thread = null;
3286                 tweenCount = 0;
3287             }
3288             else {
3289                 this.unRegister(tween);
3290             }
3291         };
3292
3293
3294         this.run = function() {
3295             for (var i = 0, len = queue.length; i < len; ++i) {
3296                 var tween = queue[i];
3297                 if (!tween || !tween.isAnimated()) {
3298                     continue;
3299                 }
3300
3301                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3302                 {
3303                     tween.currentFrame += 1;
3304
3305                     if (tween.useSeconds) {
3306                         correctFrame(tween);
3307                     }
3308                     tween._onTween.fire();
3309                 }
3310                 else {
3311                     Roo.lib.AnimMgr.stop(tween, i);
3312                 }
3313             }
3314         };
3315
3316         var getIndex = function(anim) {
3317             for (var i = 0, len = queue.length; i < len; ++i) {
3318                 if (queue[i] == anim) {
3319                     return i;
3320                 }
3321             }
3322             return -1;
3323         };
3324
3325
3326         var correctFrame = function(tween) {
3327             var frames = tween.totalFrames;
3328             var frame = tween.currentFrame;
3329             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3330             var elapsed = (new Date() - tween.getStartTime());
3331             var tweak = 0;
3332
3333             if (elapsed < tween.duration * 1000) {
3334                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3335             } else {
3336                 tweak = frames - (frame + 1);
3337             }
3338             if (tweak > 0 && isFinite(tweak)) {
3339                 if (tween.currentFrame + tweak >= frames) {
3340                     tweak = frames - (frame + 1);
3341                 }
3342
3343                 tween.currentFrame += tweak;
3344             }
3345         };
3346     };/*
3347  * Portions of this file are based on pieces of Yahoo User Interface Library
3348  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3349  * YUI licensed under the BSD License:
3350  * http://developer.yahoo.net/yui/license.txt
3351  * <script type="text/javascript">
3352  *
3353  */
3354 Roo.lib.Bezier = new function() {
3355
3356         this.getPosition = function(points, t) {
3357             var n = points.length;
3358             var tmp = [];
3359
3360             for (var i = 0; i < n; ++i) {
3361                 tmp[i] = [points[i][0], points[i][1]];
3362             }
3363
3364             for (var j = 1; j < n; ++j) {
3365                 for (i = 0; i < n - j; ++i) {
3366                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3367                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3368                 }
3369             }
3370
3371             return [ tmp[0][0], tmp[0][1] ];
3372
3373         };
3374     };/*
3375  * Portions of this file are based on pieces of Yahoo User Interface Library
3376  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3377  * YUI licensed under the BSD License:
3378  * http://developer.yahoo.net/yui/license.txt
3379  * <script type="text/javascript">
3380  *
3381  */
3382 (function() {
3383
3384     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3385         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3386     };
3387
3388     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3389
3390     var fly = Roo.lib.AnimBase.fly;
3391     var Y = Roo.lib;
3392     var superclass = Y.ColorAnim.superclass;
3393     var proto = Y.ColorAnim.prototype;
3394
3395     proto.toString = function() {
3396         var el = this.getEl();
3397         var id = el.id || el.tagName;
3398         return ("ColorAnim " + id);
3399     };
3400
3401     proto.patterns.color = /color$/i;
3402     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3403     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3404     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3405     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3406
3407
3408     proto.parseColor = function(s) {
3409         if (s.length == 3) {
3410             return s;
3411         }
3412
3413         var c = this.patterns.hex.exec(s);
3414         if (c && c.length == 4) {
3415             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3416         }
3417
3418         c = this.patterns.rgb.exec(s);
3419         if (c && c.length == 4) {
3420             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3421         }
3422
3423         c = this.patterns.hex3.exec(s);
3424         if (c && c.length == 4) {
3425             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3426         }
3427
3428         return null;
3429     };
3430     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3431     proto.getAttribute = function(attr) {
3432         var el = this.getEl();
3433         if (this.patterns.color.test(attr)) {
3434             var val = fly(el).getStyle(attr);
3435
3436             if (this.patterns.transparent.test(val)) {
3437                 var parent = el.parentNode;
3438                 val = fly(parent).getStyle(attr);
3439
3440                 while (parent && this.patterns.transparent.test(val)) {
3441                     parent = parent.parentNode;
3442                     val = fly(parent).getStyle(attr);
3443                     if (parent.tagName.toUpperCase() == 'HTML') {
3444                         val = '#fff';
3445                     }
3446                 }
3447             }
3448         } else {
3449             val = superclass.getAttribute.call(this, attr);
3450         }
3451
3452         return val;
3453     };
3454     proto.getAttribute = function(attr) {
3455         var el = this.getEl();
3456         if (this.patterns.color.test(attr)) {
3457             var val = fly(el).getStyle(attr);
3458
3459             if (this.patterns.transparent.test(val)) {
3460                 var parent = el.parentNode;
3461                 val = fly(parent).getStyle(attr);
3462
3463                 while (parent && this.patterns.transparent.test(val)) {
3464                     parent = parent.parentNode;
3465                     val = fly(parent).getStyle(attr);
3466                     if (parent.tagName.toUpperCase() == 'HTML') {
3467                         val = '#fff';
3468                     }
3469                 }
3470             }
3471         } else {
3472             val = superclass.getAttribute.call(this, attr);
3473         }
3474
3475         return val;
3476     };
3477
3478     proto.doMethod = function(attr, start, end) {
3479         var val;
3480
3481         if (this.patterns.color.test(attr)) {
3482             val = [];
3483             for (var i = 0, len = start.length; i < len; ++i) {
3484                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3485             }
3486
3487             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3488         }
3489         else {
3490             val = superclass.doMethod.call(this, attr, start, end);
3491         }
3492
3493         return val;
3494     };
3495
3496     proto.setRuntimeAttribute = function(attr) {
3497         superclass.setRuntimeAttribute.call(this, attr);
3498
3499         if (this.patterns.color.test(attr)) {
3500             var attributes = this.attributes;
3501             var start = this.parseColor(this.runtimeAttributes[attr].start);
3502             var end = this.parseColor(this.runtimeAttributes[attr].end);
3503
3504             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3505                 end = this.parseColor(attributes[attr].by);
3506
3507                 for (var i = 0, len = start.length; i < len; ++i) {
3508                     end[i] = start[i] + end[i];
3509                 }
3510             }
3511
3512             this.runtimeAttributes[attr].start = start;
3513             this.runtimeAttributes[attr].end = end;
3514         }
3515     };
3516 })();
3517
3518 /*
3519  * Portions of this file are based on pieces of Yahoo User Interface Library
3520  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3521  * YUI licensed under the BSD License:
3522  * http://developer.yahoo.net/yui/license.txt
3523  * <script type="text/javascript">
3524  *
3525  */
3526 Roo.lib.Easing = {
3527
3528
3529     easeNone: function (t, b, c, d) {
3530         return c * t / d + b;
3531     },
3532
3533
3534     easeIn: function (t, b, c, d) {
3535         return c * (t /= d) * t + b;
3536     },
3537
3538
3539     easeOut: function (t, b, c, d) {
3540         return -c * (t /= d) * (t - 2) + b;
3541     },
3542
3543
3544     easeBoth: function (t, b, c, d) {
3545         if ((t /= d / 2) < 1) {
3546             return c / 2 * t * t + b;
3547         }
3548
3549         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3550     },
3551
3552
3553     easeInStrong: function (t, b, c, d) {
3554         return c * (t /= d) * t * t * t + b;
3555     },
3556
3557
3558     easeOutStrong: function (t, b, c, d) {
3559         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3560     },
3561
3562
3563     easeBothStrong: function (t, b, c, d) {
3564         if ((t /= d / 2) < 1) {
3565             return c / 2 * t * t * t * t + b;
3566         }
3567
3568         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3569     },
3570
3571
3572
3573     elasticIn: function (t, b, c, d, a, p) {
3574         if (t == 0) {
3575             return b;
3576         }
3577         if ((t /= d) == 1) {
3578             return b + c;
3579         }
3580         if (!p) {
3581             p = d * .3;
3582         }
3583
3584         if (!a || a < Math.abs(c)) {
3585             a = c;
3586             var s = p / 4;
3587         }
3588         else {
3589             var s = p / (2 * Math.PI) * Math.asin(c / a);
3590         }
3591
3592         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3593     },
3594
3595
3596     elasticOut: function (t, b, c, d, a, p) {
3597         if (t == 0) {
3598             return b;
3599         }
3600         if ((t /= d) == 1) {
3601             return b + c;
3602         }
3603         if (!p) {
3604             p = d * .3;
3605         }
3606
3607         if (!a || a < Math.abs(c)) {
3608             a = c;
3609             var s = p / 4;
3610         }
3611         else {
3612             var s = p / (2 * Math.PI) * Math.asin(c / a);
3613         }
3614
3615         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3616     },
3617
3618
3619     elasticBoth: function (t, b, c, d, a, p) {
3620         if (t == 0) {
3621             return b;
3622         }
3623
3624         if ((t /= d / 2) == 2) {
3625             return b + c;
3626         }
3627
3628         if (!p) {
3629             p = d * (.3 * 1.5);
3630         }
3631
3632         if (!a || a < Math.abs(c)) {
3633             a = c;
3634             var s = p / 4;
3635         }
3636         else {
3637             var s = p / (2 * Math.PI) * Math.asin(c / a);
3638         }
3639
3640         if (t < 1) {
3641             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3642                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3643         }
3644         return a * Math.pow(2, -10 * (t -= 1)) *
3645                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3646     },
3647
3648
3649
3650     backIn: function (t, b, c, d, s) {
3651         if (typeof s == 'undefined') {
3652             s = 1.70158;
3653         }
3654         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3655     },
3656
3657
3658     backOut: function (t, b, c, d, s) {
3659         if (typeof s == 'undefined') {
3660             s = 1.70158;
3661         }
3662         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3663     },
3664
3665
3666     backBoth: function (t, b, c, d, s) {
3667         if (typeof s == 'undefined') {
3668             s = 1.70158;
3669         }
3670
3671         if ((t /= d / 2 ) < 1) {
3672             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3673         }
3674         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3675     },
3676
3677
3678     bounceIn: function (t, b, c, d) {
3679         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3680     },
3681
3682
3683     bounceOut: function (t, b, c, d) {
3684         if ((t /= d) < (1 / 2.75)) {
3685             return c * (7.5625 * t * t) + b;
3686         } else if (t < (2 / 2.75)) {
3687             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3688         } else if (t < (2.5 / 2.75)) {
3689             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3690         }
3691         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3692     },
3693
3694
3695     bounceBoth: function (t, b, c, d) {
3696         if (t < d / 2) {
3697             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3698         }
3699         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3700     }
3701 };/*
3702  * Portions of this file are based on pieces of Yahoo User Interface Library
3703  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3704  * YUI licensed under the BSD License:
3705  * http://developer.yahoo.net/yui/license.txt
3706  * <script type="text/javascript">
3707  *
3708  */
3709     (function() {
3710         Roo.lib.Motion = function(el, attributes, duration, method) {
3711             if (el) {
3712                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3713             }
3714         };
3715
3716         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3717
3718
3719         var Y = Roo.lib;
3720         var superclass = Y.Motion.superclass;
3721         var proto = Y.Motion.prototype;
3722
3723         proto.toString = function() {
3724             var el = this.getEl();
3725             var id = el.id || el.tagName;
3726             return ("Motion " + id);
3727         };
3728
3729         proto.patterns.points = /^points$/i;
3730
3731         proto.setAttribute = function(attr, val, unit) {
3732             if (this.patterns.points.test(attr)) {
3733                 unit = unit || 'px';
3734                 superclass.setAttribute.call(this, 'left', val[0], unit);
3735                 superclass.setAttribute.call(this, 'top', val[1], unit);
3736             } else {
3737                 superclass.setAttribute.call(this, attr, val, unit);
3738             }
3739         };
3740
3741         proto.getAttribute = function(attr) {
3742             if (this.patterns.points.test(attr)) {
3743                 var val = [
3744                         superclass.getAttribute.call(this, 'left'),
3745                         superclass.getAttribute.call(this, 'top')
3746                         ];
3747             } else {
3748                 val = superclass.getAttribute.call(this, attr);
3749             }
3750
3751             return val;
3752         };
3753
3754         proto.doMethod = function(attr, start, end) {
3755             var val = null;
3756
3757             if (this.patterns.points.test(attr)) {
3758                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3759                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3760             } else {
3761                 val = superclass.doMethod.call(this, attr, start, end);
3762             }
3763             return val;
3764         };
3765
3766         proto.setRuntimeAttribute = function(attr) {
3767             if (this.patterns.points.test(attr)) {
3768                 var el = this.getEl();
3769                 var attributes = this.attributes;
3770                 var start;
3771                 var control = attributes['points']['control'] || [];
3772                 var end;
3773                 var i, len;
3774
3775                 if (control.length > 0 && !(control[0] instanceof Array)) {
3776                     control = [control];
3777                 } else {
3778                     var tmp = [];
3779                     for (i = 0,len = control.length; i < len; ++i) {
3780                         tmp[i] = control[i];
3781                     }
3782                     control = tmp;
3783                 }
3784
3785                 Roo.fly(el).position();
3786
3787                 if (isset(attributes['points']['from'])) {
3788                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3789                 }
3790                 else {
3791                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3792                 }
3793
3794                 start = this.getAttribute('points');
3795
3796
3797                 if (isset(attributes['points']['to'])) {
3798                     end = translateValues.call(this, attributes['points']['to'], start);
3799
3800                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3801                     for (i = 0,len = control.length; i < len; ++i) {
3802                         control[i] = translateValues.call(this, control[i], start);
3803                     }
3804
3805
3806                 } else if (isset(attributes['points']['by'])) {
3807                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3808
3809                     for (i = 0,len = control.length; i < len; ++i) {
3810                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3811                     }
3812                 }
3813
3814                 this.runtimeAttributes[attr] = [start];
3815
3816                 if (control.length > 0) {
3817                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3818                 }
3819
3820                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3821             }
3822             else {
3823                 superclass.setRuntimeAttribute.call(this, attr);
3824             }
3825         };
3826
3827         var translateValues = function(val, start) {
3828             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3829             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3830
3831             return val;
3832         };
3833
3834         var isset = function(prop) {
3835             return (typeof prop !== 'undefined');
3836         };
3837     })();
3838 /*
3839  * Portions of this file are based on pieces of Yahoo User Interface Library
3840  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3841  * YUI licensed under the BSD License:
3842  * http://developer.yahoo.net/yui/license.txt
3843  * <script type="text/javascript">
3844  *
3845  */
3846     (function() {
3847         Roo.lib.Scroll = function(el, attributes, duration, method) {
3848             if (el) {
3849                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3850             }
3851         };
3852
3853         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3854
3855
3856         var Y = Roo.lib;
3857         var superclass = Y.Scroll.superclass;
3858         var proto = Y.Scroll.prototype;
3859
3860         proto.toString = function() {
3861             var el = this.getEl();
3862             var id = el.id || el.tagName;
3863             return ("Scroll " + id);
3864         };
3865
3866         proto.doMethod = function(attr, start, end) {
3867             var val = null;
3868
3869             if (attr == 'scroll') {
3870                 val = [
3871                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3872                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3873                         ];
3874
3875             } else {
3876                 val = superclass.doMethod.call(this, attr, start, end);
3877             }
3878             return val;
3879         };
3880
3881         proto.getAttribute = function(attr) {
3882             var val = null;
3883             var el = this.getEl();
3884
3885             if (attr == 'scroll') {
3886                 val = [ el.scrollLeft, el.scrollTop ];
3887             } else {
3888                 val = superclass.getAttribute.call(this, attr);
3889             }
3890
3891             return val;
3892         };
3893
3894         proto.setAttribute = function(attr, val, unit) {
3895             var el = this.getEl();
3896
3897             if (attr == 'scroll') {
3898                 el.scrollLeft = val[0];
3899                 el.scrollTop = val[1];
3900             } else {
3901                 superclass.setAttribute.call(this, attr, val, unit);
3902             }
3903         };
3904     })();
3905 /*
3906  * Based on:
3907  * Ext JS Library 1.1.1
3908  * Copyright(c) 2006-2007, Ext JS, LLC.
3909  *
3910  * Originally Released Under LGPL - original licence link has changed is not relivant.
3911  *
3912  * Fork - LGPL
3913  * <script type="text/javascript">
3914  */
3915  
3916
3917 /**
3918  * @class Roo.DomHelper
3919  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3920  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
3921  * @singleton
3922  */
3923 Roo.DomHelper = function(){
3924     var tempTableEl = null;
3925     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3926     var tableRe = /^table|tbody|tr|td$/i;
3927     var xmlns = {};
3928     // build as innerHTML where available
3929     /** @ignore */
3930     var createHtml = function(o){
3931         if(typeof o == 'string'){
3932             return o;
3933         }
3934         var b = "";
3935         if(!o.tag){
3936             o.tag = "div";
3937         }
3938         b += "<" + o.tag;
3939         for(var attr in o){
3940             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
3941             if(attr == "style"){
3942                 var s = o["style"];
3943                 if(typeof s == "function"){
3944                     s = s.call();
3945                 }
3946                 if(typeof s == "string"){
3947                     b += ' style="' + s + '"';
3948                 }else if(typeof s == "object"){
3949                     b += ' style="';
3950                     for(var key in s){
3951                         if(typeof s[key] != "function"){
3952                             b += key + ":" + s[key] + ";";
3953                         }
3954                     }
3955                     b += '"';
3956                 }
3957             }else{
3958                 if(attr == "cls"){
3959                     b += ' class="' + o["cls"] + '"';
3960                 }else if(attr == "htmlFor"){
3961                     b += ' for="' + o["htmlFor"] + '"';
3962                 }else{
3963                     b += " " + attr + '="' + o[attr] + '"';
3964                 }
3965             }
3966         }
3967         if(emptyTags.test(o.tag)){
3968             b += "/>";
3969         }else{
3970             b += ">";
3971             var cn = o.children || o.cn;
3972             if(cn){
3973                 //http://bugs.kde.org/show_bug.cgi?id=71506
3974                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
3975                     for(var i = 0, len = cn.length; i < len; i++) {
3976                         b += createHtml(cn[i], b);
3977                     }
3978                 }else{
3979                     b += createHtml(cn, b);
3980                 }
3981             }
3982             if(o.html){
3983                 b += o.html;
3984             }
3985             b += "</" + o.tag + ">";
3986         }
3987         return b;
3988     };
3989
3990     // build as dom
3991     /** @ignore */
3992     var createDom = function(o, parentNode){
3993          
3994         // defininition craeted..
3995         var ns = false;
3996         if (o.ns && o.ns != 'html') {
3997                
3998             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
3999                 xmlns[o.ns] = o.xmlns;
4000                 ns = o.xmlns;
4001             }
4002             if (typeof(xmlns[o.ns]) == 'undefined') {
4003                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4004             }
4005             ns = xmlns[o.ns];
4006         }
4007         
4008         
4009         if (typeof(o) == 'string') {
4010             return parentNode.appendChild(document.createTextNode(o));
4011         }
4012         o.tag = o.tag || div;
4013         if (o.ns && Roo.isIE) {
4014             ns = false;
4015             o.tag = o.ns + ':' + o.tag;
4016             
4017         }
4018         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4019         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4020         for(var attr in o){
4021             
4022             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4023                     attr == "style" || typeof o[attr] == "function") continue;
4024                     
4025             if(attr=="cls" && Roo.isIE){
4026                 el.className = o["cls"];
4027             }else{
4028                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4029                 else el[attr] = o[attr];
4030             }
4031         }
4032         Roo.DomHelper.applyStyles(el, o.style);
4033         var cn = o.children || o.cn;
4034         if(cn){
4035             //http://bugs.kde.org/show_bug.cgi?id=71506
4036              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4037                 for(var i = 0, len = cn.length; i < len; i++) {
4038                     createDom(cn[i], el);
4039                 }
4040             }else{
4041                 createDom(cn, el);
4042             }
4043         }
4044         if(o.html){
4045             el.innerHTML = o.html;
4046         }
4047         if(parentNode){
4048            parentNode.appendChild(el);
4049         }
4050         return el;
4051     };
4052
4053     var ieTable = function(depth, s, h, e){
4054         tempTableEl.innerHTML = [s, h, e].join('');
4055         var i = -1, el = tempTableEl;
4056         while(++i < depth){
4057             el = el.firstChild;
4058         }
4059         return el;
4060     };
4061
4062     // kill repeat to save bytes
4063     var ts = '<table>',
4064         te = '</table>',
4065         tbs = ts+'<tbody>',
4066         tbe = '</tbody>'+te,
4067         trs = tbs + '<tr>',
4068         tre = '</tr>'+tbe;
4069
4070     /**
4071      * @ignore
4072      * Nasty code for IE's broken table implementation
4073      */
4074     var insertIntoTable = function(tag, where, el, html){
4075         if(!tempTableEl){
4076             tempTableEl = document.createElement('div');
4077         }
4078         var node;
4079         var before = null;
4080         if(tag == 'td'){
4081             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4082                 return;
4083             }
4084             if(where == 'beforebegin'){
4085                 before = el;
4086                 el = el.parentNode;
4087             } else{
4088                 before = el.nextSibling;
4089                 el = el.parentNode;
4090             }
4091             node = ieTable(4, trs, html, tre);
4092         }
4093         else if(tag == 'tr'){
4094             if(where == 'beforebegin'){
4095                 before = el;
4096                 el = el.parentNode;
4097                 node = ieTable(3, tbs, html, tbe);
4098             } else if(where == 'afterend'){
4099                 before = el.nextSibling;
4100                 el = el.parentNode;
4101                 node = ieTable(3, tbs, html, tbe);
4102             } else{ // INTO a TR
4103                 if(where == 'afterbegin'){
4104                     before = el.firstChild;
4105                 }
4106                 node = ieTable(4, trs, html, tre);
4107             }
4108         } else if(tag == 'tbody'){
4109             if(where == 'beforebegin'){
4110                 before = el;
4111                 el = el.parentNode;
4112                 node = ieTable(2, ts, html, te);
4113             } else if(where == 'afterend'){
4114                 before = el.nextSibling;
4115                 el = el.parentNode;
4116                 node = ieTable(2, ts, html, te);
4117             } else{
4118                 if(where == 'afterbegin'){
4119                     before = el.firstChild;
4120                 }
4121                 node = ieTable(3, tbs, html, tbe);
4122             }
4123         } else{ // TABLE
4124             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4125                 return;
4126             }
4127             if(where == 'afterbegin'){
4128                 before = el.firstChild;
4129             }
4130             node = ieTable(2, ts, html, te);
4131         }
4132         el.insertBefore(node, before);
4133         return node;
4134     };
4135
4136     return {
4137     /** True to force the use of DOM instead of html fragments @type Boolean */
4138     useDom : false,
4139
4140     /**
4141      * Returns the markup for the passed Element(s) config
4142      * @param {Object} o The Dom object spec (and children)
4143      * @return {String}
4144      */
4145     markup : function(o){
4146         return createHtml(o);
4147     },
4148
4149     /**
4150      * Applies a style specification to an element
4151      * @param {String/HTMLElement} el The element to apply styles to
4152      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4153      * a function which returns such a specification.
4154      */
4155     applyStyles : function(el, styles){
4156         if(styles){
4157            el = Roo.fly(el);
4158            if(typeof styles == "string"){
4159                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4160                var matches;
4161                while ((matches = re.exec(styles)) != null){
4162                    el.setStyle(matches[1], matches[2]);
4163                }
4164            }else if (typeof styles == "object"){
4165                for (var style in styles){
4166                   el.setStyle(style, styles[style]);
4167                }
4168            }else if (typeof styles == "function"){
4169                 Roo.DomHelper.applyStyles(el, styles.call());
4170            }
4171         }
4172     },
4173
4174     /**
4175      * Inserts an HTML fragment into the Dom
4176      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4177      * @param {HTMLElement} el The context element
4178      * @param {String} html The HTML fragmenet
4179      * @return {HTMLElement} The new node
4180      */
4181     insertHtml : function(where, el, html){
4182         where = where.toLowerCase();
4183         if(el.insertAdjacentHTML){
4184             if(tableRe.test(el.tagName)){
4185                 var rs;
4186                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4187                     return rs;
4188                 }
4189             }
4190             switch(where){
4191                 case "beforebegin":
4192                     el.insertAdjacentHTML('BeforeBegin', html);
4193                     return el.previousSibling;
4194                 case "afterbegin":
4195                     el.insertAdjacentHTML('AfterBegin', html);
4196                     return el.firstChild;
4197                 case "beforeend":
4198                     el.insertAdjacentHTML('BeforeEnd', html);
4199                     return el.lastChild;
4200                 case "afterend":
4201                     el.insertAdjacentHTML('AfterEnd', html);
4202                     return el.nextSibling;
4203             }
4204             throw 'Illegal insertion point -> "' + where + '"';
4205         }
4206         var range = el.ownerDocument.createRange();
4207         var frag;
4208         switch(where){
4209              case "beforebegin":
4210                 range.setStartBefore(el);
4211                 frag = range.createContextualFragment(html);
4212                 el.parentNode.insertBefore(frag, el);
4213                 return el.previousSibling;
4214              case "afterbegin":
4215                 if(el.firstChild){
4216                     range.setStartBefore(el.firstChild);
4217                     frag = range.createContextualFragment(html);
4218                     el.insertBefore(frag, el.firstChild);
4219                     return el.firstChild;
4220                 }else{
4221                     el.innerHTML = html;
4222                     return el.firstChild;
4223                 }
4224             case "beforeend":
4225                 if(el.lastChild){
4226                     range.setStartAfter(el.lastChild);
4227                     frag = range.createContextualFragment(html);
4228                     el.appendChild(frag);
4229                     return el.lastChild;
4230                 }else{
4231                     el.innerHTML = html;
4232                     return el.lastChild;
4233                 }
4234             case "afterend":
4235                 range.setStartAfter(el);
4236                 frag = range.createContextualFragment(html);
4237                 el.parentNode.insertBefore(frag, el.nextSibling);
4238                 return el.nextSibling;
4239             }
4240             throw 'Illegal insertion point -> "' + where + '"';
4241     },
4242
4243     /**
4244      * Creates new Dom element(s) and inserts them before el
4245      * @param {String/HTMLElement/Element} el The context element
4246      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4247      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4248      * @return {HTMLElement/Roo.Element} The new node
4249      */
4250     insertBefore : function(el, o, returnElement){
4251         return this.doInsert(el, o, returnElement, "beforeBegin");
4252     },
4253
4254     /**
4255      * Creates new Dom element(s) and inserts them after el
4256      * @param {String/HTMLElement/Element} el The context element
4257      * @param {Object} o The Dom object spec (and children)
4258      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4259      * @return {HTMLElement/Roo.Element} The new node
4260      */
4261     insertAfter : function(el, o, returnElement){
4262         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4263     },
4264
4265     /**
4266      * Creates new Dom element(s) and inserts them as the first child of el
4267      * @param {String/HTMLElement/Element} el The context element
4268      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4269      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4270      * @return {HTMLElement/Roo.Element} The new node
4271      */
4272     insertFirst : function(el, o, returnElement){
4273         return this.doInsert(el, o, returnElement, "afterBegin");
4274     },
4275
4276     // private
4277     doInsert : function(el, o, returnElement, pos, sibling){
4278         el = Roo.getDom(el);
4279         var newNode;
4280         if(this.useDom || o.ns){
4281             newNode = createDom(o, null);
4282             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4283         }else{
4284             var html = createHtml(o);
4285             newNode = this.insertHtml(pos, el, html);
4286         }
4287         return returnElement ? Roo.get(newNode, true) : newNode;
4288     },
4289
4290     /**
4291      * Creates new Dom element(s) and appends them to el
4292      * @param {String/HTMLElement/Element} el The context element
4293      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4294      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4295      * @return {HTMLElement/Roo.Element} The new node
4296      */
4297     append : function(el, o, returnElement){
4298         el = Roo.getDom(el);
4299         var newNode;
4300         if(this.useDom || o.ns){
4301             newNode = createDom(o, null);
4302             el.appendChild(newNode);
4303         }else{
4304             var html = createHtml(o);
4305             newNode = this.insertHtml("beforeEnd", el, html);
4306         }
4307         return returnElement ? Roo.get(newNode, true) : newNode;
4308     },
4309
4310     /**
4311      * Creates new Dom element(s) and overwrites the contents of el with them
4312      * @param {String/HTMLElement/Element} el The context element
4313      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4314      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4315      * @return {HTMLElement/Roo.Element} The new node
4316      */
4317     overwrite : function(el, o, returnElement){
4318         el = Roo.getDom(el);
4319         if (o.ns) {
4320           
4321             while (el.childNodes.length) {
4322                 el.removeChild(el.firstChild);
4323             }
4324             createDom(o, el);
4325         } else {
4326             el.innerHTML = createHtml(o);   
4327         }
4328         
4329         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4330     },
4331
4332     /**
4333      * Creates a new Roo.DomHelper.Template from the Dom object spec
4334      * @param {Object} o The Dom object spec (and children)
4335      * @return {Roo.DomHelper.Template} The new template
4336      */
4337     createTemplate : function(o){
4338         var html = createHtml(o);
4339         return new Roo.Template(html);
4340     }
4341     };
4342 }();
4343 /*
4344  * Based on:
4345  * Ext JS Library 1.1.1
4346  * Copyright(c) 2006-2007, Ext JS, LLC.
4347  *
4348  * Originally Released Under LGPL - original licence link has changed is not relivant.
4349  *
4350  * Fork - LGPL
4351  * <script type="text/javascript">
4352  */
4353  
4354 /**
4355 * @class Roo.Template
4356 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4357 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4358 * Usage:
4359 <pre><code>
4360 var t = new Roo.Template(
4361     '&lt;div name="{id}"&gt;',
4362         '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
4363     '&lt;/div&gt;'
4364 );
4365 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4366 </code></pre>
4367 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4368 * @constructor
4369 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4370 */
4371 Roo.Template = function(html){
4372     if(html instanceof Array){
4373         html = html.join("");
4374     }else if(arguments.length > 1){
4375         html = Array.prototype.join.call(arguments, "");
4376     }
4377     /**@private*/
4378     this.html = html;
4379     
4380 };
4381 Roo.Template.prototype = {
4382     /**
4383      * Returns an HTML fragment of this template with the specified values applied.
4384      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4385      * @return {String} The HTML fragment
4386      */
4387     applyTemplate : function(values){
4388         if(this.compiled){
4389             return this.compiled(values);
4390         }
4391         var useF = this.disableFormats !== true;
4392         var fm = Roo.util.Format, tpl = this;
4393         var fn = function(m, name, format, args){
4394             if(format && useF){
4395                 if(format.substr(0, 5) == "this."){
4396                     return tpl.call(format.substr(5), values[name], values);
4397                 }else{
4398                     if(args){
4399                         // quoted values are required for strings in compiled templates, 
4400                         // but for non compiled we need to strip them
4401                         // quoted reversed for jsmin
4402                         var re = /^\s*['"](.*)["']\s*$/;
4403                         args = args.split(',');
4404                         for(var i = 0, len = args.length; i < len; i++){
4405                             args[i] = args[i].replace(re, "$1");
4406                         }
4407                         args = [values[name]].concat(args);
4408                     }else{
4409                         args = [values[name]];
4410                     }
4411                     return fm[format].apply(fm, args);
4412                 }
4413             }else{
4414                 return values[name] !== undefined ? values[name] : "";
4415             }
4416         };
4417         return this.html.replace(this.re, fn);
4418     },
4419     
4420     /**
4421      * Sets the HTML used as the template and optionally compiles it.
4422      * @param {String} html
4423      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4424      * @return {Roo.Template} this
4425      */
4426     set : function(html, compile){
4427         this.html = html;
4428         this.compiled = null;
4429         if(compile){
4430             this.compile();
4431         }
4432         return this;
4433     },
4434     
4435     /**
4436      * True to disable format functions (defaults to false)
4437      * @type Boolean
4438      */
4439     disableFormats : false,
4440     
4441     /**
4442     * The regular expression used to match template variables 
4443     * @type RegExp
4444     * @property 
4445     */
4446     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4447     
4448     /**
4449      * Compiles the template into an internal function, eliminating the RegEx overhead.
4450      * @return {Roo.Template} this
4451      */
4452     compile : function(){
4453         var fm = Roo.util.Format;
4454         var useF = this.disableFormats !== true;
4455         var sep = Roo.isGecko ? "+" : ",";
4456         var fn = function(m, name, format, args){
4457             if(format && useF){
4458                 args = args ? ',' + args : "";
4459                 if(format.substr(0, 5) != "this."){
4460                     format = "fm." + format + '(';
4461                 }else{
4462                     format = 'this.call("'+ format.substr(5) + '", ';
4463                     args = ", values";
4464                 }
4465             }else{
4466                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4467             }
4468             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4469         };
4470         var body;
4471         // branched to use + in gecko and [].join() in others
4472         if(Roo.isGecko){
4473             body = "this.compiled = function(values){ return '" +
4474                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4475                     "';};";
4476         }else{
4477             body = ["this.compiled = function(values){ return ['"];
4478             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4479             body.push("'].join('');};");
4480             body = body.join('');
4481         }
4482         /**
4483          * eval:var:values
4484          * eval:var:fm
4485          */
4486         eval(body);
4487         return this;
4488     },
4489     
4490     // private function used to call members
4491     call : function(fnName, value, allValues){
4492         return this[fnName](value, allValues);
4493     },
4494     
4495     /**
4496      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4497      * @param {String/HTMLElement/Roo.Element} el The context element
4498      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4499      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4500      * @return {HTMLElement/Roo.Element} The new node or Element
4501      */
4502     insertFirst: function(el, values, returnElement){
4503         return this.doInsert('afterBegin', el, values, returnElement);
4504     },
4505
4506     /**
4507      * Applies the supplied values to the template and inserts the new node(s) before el.
4508      * @param {String/HTMLElement/Roo.Element} el The context element
4509      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4511      * @return {HTMLElement/Roo.Element} The new node or Element
4512      */
4513     insertBefore: function(el, values, returnElement){
4514         return this.doInsert('beforeBegin', el, values, returnElement);
4515     },
4516
4517     /**
4518      * Applies the supplied values to the template and inserts the new node(s) after el.
4519      * @param {String/HTMLElement/Roo.Element} el The context element
4520      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4521      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4522      * @return {HTMLElement/Roo.Element} The new node or Element
4523      */
4524     insertAfter : function(el, values, returnElement){
4525         return this.doInsert('afterEnd', el, values, returnElement);
4526     },
4527     
4528     /**
4529      * Applies the supplied values to the template and appends the new node(s) to el.
4530      * @param {String/HTMLElement/Roo.Element} el The context element
4531      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4532      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4533      * @return {HTMLElement/Roo.Element} The new node or Element
4534      */
4535     append : function(el, values, returnElement){
4536         return this.doInsert('beforeEnd', el, values, returnElement);
4537     },
4538
4539     doInsert : function(where, el, values, returnEl){
4540         el = Roo.getDom(el);
4541         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4542         return returnEl ? Roo.get(newNode, true) : newNode;
4543     },
4544
4545     /**
4546      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4547      * @param {String/HTMLElement/Roo.Element} el The context element
4548      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4549      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4550      * @return {HTMLElement/Roo.Element} The new node or Element
4551      */
4552     overwrite : function(el, values, returnElement){
4553         el = Roo.getDom(el);
4554         el.innerHTML = this.applyTemplate(values);
4555         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4556     }
4557 };
4558 /**
4559  * Alias for {@link #applyTemplate}
4560  * @method
4561  */
4562 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4563
4564 // backwards compat
4565 Roo.DomHelper.Template = Roo.Template;
4566
4567 /**
4568  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4569  * @param {String/HTMLElement} el A DOM element or its id
4570  * @returns {Roo.Template} The created template
4571  * @static
4572  */
4573 Roo.Template.from = function(el){
4574     el = Roo.getDom(el);
4575     return new Roo.Template(el.value || el.innerHTML);
4576 };/*
4577  * Based on:
4578  * Ext JS Library 1.1.1
4579  * Copyright(c) 2006-2007, Ext JS, LLC.
4580  *
4581  * Originally Released Under LGPL - original licence link has changed is not relivant.
4582  *
4583  * Fork - LGPL
4584  * <script type="text/javascript">
4585  */
4586  
4587
4588 /*
4589  * This is code is also distributed under MIT license for use
4590  * with jQuery and prototype JavaScript libraries.
4591  */
4592 /**
4593  * @class Roo.DomQuery
4594 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4595 <p>
4596 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4597
4598 <p>
4599 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4600 </p>
4601 <h4>Element Selectors:</h4>
4602 <ul class="list">
4603     <li> <b>*</b> any element</li>
4604     <li> <b>E</b> an element with the tag E</li>
4605     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4606     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4607     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4608     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4609 </ul>
4610 <h4>Attribute Selectors:</h4>
4611 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4612 <ul class="list">
4613     <li> <b>E[foo]</b> has an attribute "foo"</li>
4614     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4615     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4616     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4617     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4618     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4619     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4620 </ul>
4621 <h4>Pseudo Classes:</h4>
4622 <ul class="list">
4623     <li> <b>E:first-child</b> E is the first child of its parent</li>
4624     <li> <b>E:last-child</b> E is the last child of its parent</li>
4625     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4626     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4627     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4628     <li> <b>E:only-child</b> E is the only child of its parent</li>
4629     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4630     <li> <b>E:first</b> the first E in the resultset</li>
4631     <li> <b>E:last</b> the last E in the resultset</li>
4632     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4633     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4634     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4635     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4636     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4637     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4638     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4639     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4640     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4641 </ul>
4642 <h4>CSS Value Selectors:</h4>
4643 <ul class="list">
4644     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4645     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4646     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4647     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4648     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4649     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4650 </ul>
4651  * @singleton
4652  */
4653 Roo.DomQuery = function(){
4654     var cache = {}, simpleCache = {}, valueCache = {};
4655     var nonSpace = /\S/;
4656     var trimRe = /^\s+|\s+$/g;
4657     var tplRe = /\{(\d+)\}/g;
4658     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4659     var tagTokenRe = /^(#)?([\w-\*]+)/;
4660     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4661
4662     function child(p, index){
4663         var i = 0;
4664         var n = p.firstChild;
4665         while(n){
4666             if(n.nodeType == 1){
4667                if(++i == index){
4668                    return n;
4669                }
4670             }
4671             n = n.nextSibling;
4672         }
4673         return null;
4674     };
4675
4676     function next(n){
4677         while((n = n.nextSibling) && n.nodeType != 1);
4678         return n;
4679     };
4680
4681     function prev(n){
4682         while((n = n.previousSibling) && n.nodeType != 1);
4683         return n;
4684     };
4685
4686     function children(d){
4687         var n = d.firstChild, ni = -1;
4688             while(n){
4689                 var nx = n.nextSibling;
4690                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4691                     d.removeChild(n);
4692                 }else{
4693                     n.nodeIndex = ++ni;
4694                 }
4695                 n = nx;
4696             }
4697             return this;
4698         };
4699
4700     function byClassName(c, a, v){
4701         if(!v){
4702             return c;
4703         }
4704         var r = [], ri = -1, cn;
4705         for(var i = 0, ci; ci = c[i]; i++){
4706             if((' '+ci.className+' ').indexOf(v) != -1){
4707                 r[++ri] = ci;
4708             }
4709         }
4710         return r;
4711     };
4712
4713     function attrValue(n, attr){
4714         if(!n.tagName && typeof n.length != "undefined"){
4715             n = n[0];
4716         }
4717         if(!n){
4718             return null;
4719         }
4720         if(attr == "for"){
4721             return n.htmlFor;
4722         }
4723         if(attr == "class" || attr == "className"){
4724             return n.className;
4725         }
4726         return n.getAttribute(attr) || n[attr];
4727
4728     };
4729
4730     function getNodes(ns, mode, tagName){
4731         var result = [], ri = -1, cs;
4732         if(!ns){
4733             return result;
4734         }
4735         tagName = tagName || "*";
4736         if(typeof ns.getElementsByTagName != "undefined"){
4737             ns = [ns];
4738         }
4739         if(!mode){
4740             for(var i = 0, ni; ni = ns[i]; i++){
4741                 cs = ni.getElementsByTagName(tagName);
4742                 for(var j = 0, ci; ci = cs[j]; j++){
4743                     result[++ri] = ci;
4744                 }
4745             }
4746         }else if(mode == "/" || mode == ">"){
4747             var utag = tagName.toUpperCase();
4748             for(var i = 0, ni, cn; ni = ns[i]; i++){
4749                 cn = ni.children || ni.childNodes;
4750                 for(var j = 0, cj; cj = cn[j]; j++){
4751                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4752                         result[++ri] = cj;
4753                     }
4754                 }
4755             }
4756         }else if(mode == "+"){
4757             var utag = tagName.toUpperCase();
4758             for(var i = 0, n; n = ns[i]; i++){
4759                 while((n = n.nextSibling) && n.nodeType != 1);
4760                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4761                     result[++ri] = n;
4762                 }
4763             }
4764         }else if(mode == "~"){
4765             for(var i = 0, n; n = ns[i]; i++){
4766                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4767                 if(n){
4768                     result[++ri] = n;
4769                 }
4770             }
4771         }
4772         return result;
4773     };
4774
4775     function concat(a, b){
4776         if(b.slice){
4777             return a.concat(b);
4778         }
4779         for(var i = 0, l = b.length; i < l; i++){
4780             a[a.length] = b[i];
4781         }
4782         return a;
4783     }
4784
4785     function byTag(cs, tagName){
4786         if(cs.tagName || cs == document){
4787             cs = [cs];
4788         }
4789         if(!tagName){
4790             return cs;
4791         }
4792         var r = [], ri = -1;
4793         tagName = tagName.toLowerCase();
4794         for(var i = 0, ci; ci = cs[i]; i++){
4795             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4796                 r[++ri] = ci;
4797             }
4798         }
4799         return r;
4800     };
4801
4802     function byId(cs, attr, id){
4803         if(cs.tagName || cs == document){
4804             cs = [cs];
4805         }
4806         if(!id){
4807             return cs;
4808         }
4809         var r = [], ri = -1;
4810         for(var i = 0,ci; ci = cs[i]; i++){
4811             if(ci && ci.id == id){
4812                 r[++ri] = ci;
4813                 return r;
4814             }
4815         }
4816         return r;
4817     };
4818
4819     function byAttribute(cs, attr, value, op, custom){
4820         var r = [], ri = -1, st = custom=="{";
4821         var f = Roo.DomQuery.operators[op];
4822         for(var i = 0, ci; ci = cs[i]; i++){
4823             var a;
4824             if(st){
4825                 a = Roo.DomQuery.getStyle(ci, attr);
4826             }
4827             else if(attr == "class" || attr == "className"){
4828                 a = ci.className;
4829             }else if(attr == "for"){
4830                 a = ci.htmlFor;
4831             }else if(attr == "href"){
4832                 a = ci.getAttribute("href", 2);
4833             }else{
4834                 a = ci.getAttribute(attr);
4835             }
4836             if((f && f(a, value)) || (!f && a)){
4837                 r[++ri] = ci;
4838             }
4839         }
4840         return r;
4841     };
4842
4843     function byPseudo(cs, name, value){
4844         return Roo.DomQuery.pseudos[name](cs, value);
4845     };
4846
4847     // This is for IE MSXML which does not support expandos.
4848     // IE runs the same speed using setAttribute, however FF slows way down
4849     // and Safari completely fails so they need to continue to use expandos.
4850     var isIE = window.ActiveXObject ? true : false;
4851
4852     // this eval is stop the compressor from
4853     // renaming the variable to something shorter
4854     
4855     /** eval:var:batch */
4856     var batch = 30803; 
4857
4858     var key = 30803;
4859
4860     function nodupIEXml(cs){
4861         var d = ++key;
4862         cs[0].setAttribute("_nodup", d);
4863         var r = [cs[0]];
4864         for(var i = 1, len = cs.length; i < len; i++){
4865             var c = cs[i];
4866             if(!c.getAttribute("_nodup") != d){
4867                 c.setAttribute("_nodup", d);
4868                 r[r.length] = c;
4869             }
4870         }
4871         for(var i = 0, len = cs.length; i < len; i++){
4872             cs[i].removeAttribute("_nodup");
4873         }
4874         return r;
4875     }
4876
4877     function nodup(cs){
4878         if(!cs){
4879             return [];
4880         }
4881         var len = cs.length, c, i, r = cs, cj, ri = -1;
4882         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4883             return cs;
4884         }
4885         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4886             return nodupIEXml(cs);
4887         }
4888         var d = ++key;
4889         cs[0]._nodup = d;
4890         for(i = 1; c = cs[i]; i++){
4891             if(c._nodup != d){
4892                 c._nodup = d;
4893             }else{
4894                 r = [];
4895                 for(var j = 0; j < i; j++){
4896                     r[++ri] = cs[j];
4897                 }
4898                 for(j = i+1; cj = cs[j]; j++){
4899                     if(cj._nodup != d){
4900                         cj._nodup = d;
4901                         r[++ri] = cj;
4902                     }
4903                 }
4904                 return r;
4905             }
4906         }
4907         return r;
4908     }
4909
4910     function quickDiffIEXml(c1, c2){
4911         var d = ++key;
4912         for(var i = 0, len = c1.length; i < len; i++){
4913             c1[i].setAttribute("_qdiff", d);
4914         }
4915         var r = [];
4916         for(var i = 0, len = c2.length; i < len; i++){
4917             if(c2[i].getAttribute("_qdiff") != d){
4918                 r[r.length] = c2[i];
4919             }
4920         }
4921         for(var i = 0, len = c1.length; i < len; i++){
4922            c1[i].removeAttribute("_qdiff");
4923         }
4924         return r;
4925     }
4926
4927     function quickDiff(c1, c2){
4928         var len1 = c1.length;
4929         if(!len1){
4930             return c2;
4931         }
4932         if(isIE && c1[0].selectSingleNode){
4933             return quickDiffIEXml(c1, c2);
4934         }
4935         var d = ++key;
4936         for(var i = 0; i < len1; i++){
4937             c1[i]._qdiff = d;
4938         }
4939         var r = [];
4940         for(var i = 0, len = c2.length; i < len; i++){
4941             if(c2[i]._qdiff != d){
4942                 r[r.length] = c2[i];
4943             }
4944         }
4945         return r;
4946     }
4947
4948     function quickId(ns, mode, root, id){
4949         if(ns == root){
4950            var d = root.ownerDocument || root;
4951            return d.getElementById(id);
4952         }
4953         ns = getNodes(ns, mode, "*");
4954         return byId(ns, null, id);
4955     }
4956
4957     return {
4958         getStyle : function(el, name){
4959             return Roo.fly(el).getStyle(name);
4960         },
4961         /**
4962          * Compiles a selector/xpath query into a reusable function. The returned function
4963          * takes one parameter "root" (optional), which is the context node from where the query should start.
4964          * @param {String} selector The selector/xpath query
4965          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
4966          * @return {Function}
4967          */
4968         compile : function(path, type){
4969             type = type || "select";
4970             
4971             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
4972             var q = path, mode, lq;
4973             var tk = Roo.DomQuery.matchers;
4974             var tklen = tk.length;
4975             var mm;
4976
4977             // accept leading mode switch
4978             var lmode = q.match(modeRe);
4979             if(lmode && lmode[1]){
4980                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
4981                 q = q.replace(lmode[1], "");
4982             }
4983             // strip leading slashes
4984             while(path.substr(0, 1)=="/"){
4985                 path = path.substr(1);
4986             }
4987
4988             while(q && lq != q){
4989                 lq = q;
4990                 var tm = q.match(tagTokenRe);
4991                 if(type == "select"){
4992                     if(tm){
4993                         if(tm[1] == "#"){
4994                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
4995                         }else{
4996                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
4997                         }
4998                         q = q.replace(tm[0], "");
4999                     }else if(q.substr(0, 1) != '@'){
5000                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5001                     }
5002                 }else{
5003                     if(tm){
5004                         if(tm[1] == "#"){
5005                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5006                         }else{
5007                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5008                         }
5009                         q = q.replace(tm[0], "");
5010                     }
5011                 }
5012                 while(!(mm = q.match(modeRe))){
5013                     var matched = false;
5014                     for(var j = 0; j < tklen; j++){
5015                         var t = tk[j];
5016                         var m = q.match(t.re);
5017                         if(m){
5018                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5019                                                     return m[i];
5020                                                 });
5021                             q = q.replace(m[0], "");
5022                             matched = true;
5023                             break;
5024                         }
5025                     }
5026                     // prevent infinite loop on bad selector
5027                     if(!matched){
5028                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5029                     }
5030                 }
5031                 if(mm[1]){
5032                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5033                     q = q.replace(mm[1], "");
5034                 }
5035             }
5036             fn[fn.length] = "return nodup(n);\n}";
5037             
5038              /** 
5039               * list of variables that need from compression as they are used by eval.
5040              *  eval:var:batch 
5041              *  eval:var:nodup
5042              *  eval:var:byTag
5043              *  eval:var:ById
5044              *  eval:var:getNodes
5045              *  eval:var:quickId
5046              *  eval:var:mode
5047              *  eval:var:root
5048              *  eval:var:n
5049              *  eval:var:byClassName
5050              *  eval:var:byPseudo
5051              *  eval:var:byAttribute
5052              *  eval:var:attrValue
5053              * 
5054              **/ 
5055             eval(fn.join(""));
5056             return f;
5057         },
5058
5059         /**
5060          * Selects a group of elements.
5061          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5062          * @param {Node} root (optional) The start of the query (defaults to document).
5063          * @return {Array}
5064          */
5065         select : function(path, root, type){
5066             if(!root || root == document){
5067                 root = document;
5068             }
5069             if(typeof root == "string"){
5070                 root = document.getElementById(root);
5071             }
5072             var paths = path.split(",");
5073             var results = [];
5074             for(var i = 0, len = paths.length; i < len; i++){
5075                 var p = paths[i].replace(trimRe, "");
5076                 if(!cache[p]){
5077                     cache[p] = Roo.DomQuery.compile(p);
5078                     if(!cache[p]){
5079                         throw p + " is not a valid selector";
5080                     }
5081                 }
5082                 var result = cache[p](root);
5083                 if(result && result != document){
5084                     results = results.concat(result);
5085                 }
5086             }
5087             if(paths.length > 1){
5088                 return nodup(results);
5089             }
5090             return results;
5091         },
5092
5093         /**
5094          * Selects a single element.
5095          * @param {String} selector The selector/xpath query
5096          * @param {Node} root (optional) The start of the query (defaults to document).
5097          * @return {Element}
5098          */
5099         selectNode : function(path, root){
5100             return Roo.DomQuery.select(path, root)[0];
5101         },
5102
5103         /**
5104          * Selects the value of a node, optionally replacing null with the defaultValue.
5105          * @param {String} selector The selector/xpath query
5106          * @param {Node} root (optional) The start of the query (defaults to document).
5107          * @param {String} defaultValue
5108          */
5109         selectValue : function(path, root, defaultValue){
5110             path = path.replace(trimRe, "");
5111             if(!valueCache[path]){
5112                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5113             }
5114             var n = valueCache[path](root);
5115             n = n[0] ? n[0] : n;
5116             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5117             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5118         },
5119
5120         /**
5121          * Selects the value of a node, parsing integers and floats.
5122          * @param {String} selector The selector/xpath query
5123          * @param {Node} root (optional) The start of the query (defaults to document).
5124          * @param {Number} defaultValue
5125          * @return {Number}
5126          */
5127         selectNumber : function(path, root, defaultValue){
5128             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5129             return parseFloat(v);
5130         },
5131
5132         /**
5133          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5134          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5135          * @param {String} selector The simple selector to test
5136          * @return {Boolean}
5137          */
5138         is : function(el, ss){
5139             if(typeof el == "string"){
5140                 el = document.getElementById(el);
5141             }
5142             var isArray = (el instanceof Array);
5143             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5144             return isArray ? (result.length == el.length) : (result.length > 0);
5145         },
5146
5147         /**
5148          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5149          * @param {Array} el An array of elements to filter
5150          * @param {String} selector The simple selector to test
5151          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5152          * the selector instead of the ones that match
5153          * @return {Array}
5154          */
5155         filter : function(els, ss, nonMatches){
5156             ss = ss.replace(trimRe, "");
5157             if(!simpleCache[ss]){
5158                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5159             }
5160             var result = simpleCache[ss](els);
5161             return nonMatches ? quickDiff(result, els) : result;
5162         },
5163
5164         /**
5165          * Collection of matching regular expressions and code snippets.
5166          */
5167         matchers : [{
5168                 re: /^\.([\w-]+)/,
5169                 select: 'n = byClassName(n, null, " {1} ");'
5170             }, {
5171                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5172                 select: 'n = byPseudo(n, "{1}", "{2}");'
5173             },{
5174                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5175                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5176             }, {
5177                 re: /^#([\w-]+)/,
5178                 select: 'n = byId(n, null, "{1}");'
5179             },{
5180                 re: /^@([\w-]+)/,
5181                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5182             }
5183         ],
5184
5185         /**
5186          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5187          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5188          */
5189         operators : {
5190             "=" : function(a, v){
5191                 return a == v;
5192             },
5193             "!=" : function(a, v){
5194                 return a != v;
5195             },
5196             "^=" : function(a, v){
5197                 return a && a.substr(0, v.length) == v;
5198             },
5199             "$=" : function(a, v){
5200                 return a && a.substr(a.length-v.length) == v;
5201             },
5202             "*=" : function(a, v){
5203                 return a && a.indexOf(v) !== -1;
5204             },
5205             "%=" : function(a, v){
5206                 return (a % v) == 0;
5207             },
5208             "|=" : function(a, v){
5209                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5210             },
5211             "~=" : function(a, v){
5212                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5213             }
5214         },
5215
5216         /**
5217          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5218          * and the argument (if any) supplied in the selector.
5219          */
5220         pseudos : {
5221             "first-child" : function(c){
5222                 var r = [], ri = -1, n;
5223                 for(var i = 0, ci; ci = n = c[i]; i++){
5224                     while((n = n.previousSibling) && n.nodeType != 1);
5225                     if(!n){
5226                         r[++ri] = ci;
5227                     }
5228                 }
5229                 return r;
5230             },
5231
5232             "last-child" : function(c){
5233                 var r = [], ri = -1, n;
5234                 for(var i = 0, ci; ci = n = c[i]; i++){
5235                     while((n = n.nextSibling) && n.nodeType != 1);
5236                     if(!n){
5237                         r[++ri] = ci;
5238                     }
5239                 }
5240                 return r;
5241             },
5242
5243             "nth-child" : function(c, a) {
5244                 var r = [], ri = -1;
5245                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5246                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5247                 for(var i = 0, n; n = c[i]; i++){
5248                     var pn = n.parentNode;
5249                     if (batch != pn._batch) {
5250                         var j = 0;
5251                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5252                             if(cn.nodeType == 1){
5253                                cn.nodeIndex = ++j;
5254                             }
5255                         }
5256                         pn._batch = batch;
5257                     }
5258                     if (f == 1) {
5259                         if (l == 0 || n.nodeIndex == l){
5260                             r[++ri] = n;
5261                         }
5262                     } else if ((n.nodeIndex + l) % f == 0){
5263                         r[++ri] = n;
5264                     }
5265                 }
5266
5267                 return r;
5268             },
5269
5270             "only-child" : function(c){
5271                 var r = [], ri = -1;;
5272                 for(var i = 0, ci; ci = c[i]; i++){
5273                     if(!prev(ci) && !next(ci)){
5274                         r[++ri] = ci;
5275                     }
5276                 }
5277                 return r;
5278             },
5279
5280             "empty" : function(c){
5281                 var r = [], ri = -1;
5282                 for(var i = 0, ci; ci = c[i]; i++){
5283                     var cns = ci.childNodes, j = 0, cn, empty = true;
5284                     while(cn = cns[j]){
5285                         ++j;
5286                         if(cn.nodeType == 1 || cn.nodeType == 3){
5287                             empty = false;
5288                             break;
5289                         }
5290                     }
5291                     if(empty){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "contains" : function(c, v){
5299                 var r = [], ri = -1;
5300                 for(var i = 0, ci; ci = c[i]; i++){
5301                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5302                         r[++ri] = ci;
5303                     }
5304                 }
5305                 return r;
5306             },
5307
5308             "nodeValue" : function(c, v){
5309                 var r = [], ri = -1;
5310                 for(var i = 0, ci; ci = c[i]; i++){
5311                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5312                         r[++ri] = ci;
5313                     }
5314                 }
5315                 return r;
5316             },
5317
5318             "checked" : function(c){
5319                 var r = [], ri = -1;
5320                 for(var i = 0, ci; ci = c[i]; i++){
5321                     if(ci.checked == true){
5322                         r[++ri] = ci;
5323                     }
5324                 }
5325                 return r;
5326             },
5327
5328             "not" : function(c, ss){
5329                 return Roo.DomQuery.filter(c, ss, true);
5330             },
5331
5332             "odd" : function(c){
5333                 return this["nth-child"](c, "odd");
5334             },
5335
5336             "even" : function(c){
5337                 return this["nth-child"](c, "even");
5338             },
5339
5340             "nth" : function(c, a){
5341                 return c[a-1] || [];
5342             },
5343
5344             "first" : function(c){
5345                 return c[0] || [];
5346             },
5347
5348             "last" : function(c){
5349                 return c[c.length-1] || [];
5350             },
5351
5352             "has" : function(c, ss){
5353                 var s = Roo.DomQuery.select;
5354                 var r = [], ri = -1;
5355                 for(var i = 0, ci; ci = c[i]; i++){
5356                     if(s(ss, ci).length > 0){
5357                         r[++ri] = ci;
5358                     }
5359                 }
5360                 return r;
5361             },
5362
5363             "next" : function(c, ss){
5364                 var is = Roo.DomQuery.is;
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     var n = next(ci);
5368                     if(n && is(n, ss)){
5369                         r[++ri] = ci;
5370                     }
5371                 }
5372                 return r;
5373             },
5374
5375             "prev" : function(c, ss){
5376                 var is = Roo.DomQuery.is;
5377                 var r = [], ri = -1;
5378                 for(var i = 0, ci; ci = c[i]; i++){
5379                     var n = prev(ci);
5380                     if(n && is(n, ss)){
5381                         r[++ri] = ci;
5382                     }
5383                 }
5384                 return r;
5385             }
5386         }
5387     };
5388 }();
5389
5390 /**
5391  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5392  * @param {String} path The selector/xpath query
5393  * @param {Node} root (optional) The start of the query (defaults to document).
5394  * @return {Array}
5395  * @member Roo
5396  * @method query
5397  */
5398 Roo.query = Roo.DomQuery.select;
5399 /*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411  * @class Roo.util.Observable
5412  * Base class that provides a common interface for publishing events. Subclasses are expected to
5413  * to have a property "events" with all the events defined.<br>
5414  * For example:
5415  * <pre><code>
5416  Employee = function(name){
5417     this.name = name;
5418     this.addEvents({
5419         "fired" : true,
5420         "quit" : true
5421     });
5422  }
5423  Roo.extend(Employee, Roo.util.Observable);
5424 </code></pre>
5425  * @param {Object} config properties to use (incuding events / listeners)
5426  */
5427
5428 Roo.util.Observable = function(cfg){
5429     
5430     cfg = cfg|| {};
5431     this.addEvents(cfg.events || {});
5432     if (cfg.events) {
5433         delete cfg.events; // make sure
5434     }
5435      
5436     Roo.apply(this, cfg);
5437     
5438     if(this.listeners){
5439         this.on(this.listeners);
5440         delete this.listeners;
5441     }
5442 };
5443 Roo.util.Observable.prototype = {
5444     /** 
5445  * @cfg {Object} listeners  list of events and functions to call for this object, 
5446  * For example :
5447  * <pre><code>
5448     listeners :  { 
5449        'click' : function(e) {
5450            ..... 
5451         } ,
5452         .... 
5453     } 
5454   </code></pre>
5455  */
5456     
5457     
5458     /**
5459      * Fires the specified event with the passed parameters (minus the event name).
5460      * @param {String} eventName
5461      * @param {Object...} args Variable number of parameters are passed to handlers
5462      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5463      */
5464     fireEvent : function(){
5465         var ce = this.events[arguments[0].toLowerCase()];
5466         if(typeof ce == "object"){
5467             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5468         }else{
5469             return true;
5470         }
5471     },
5472
5473     // private
5474     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5475
5476     /**
5477      * Appends an event handler to this component
5478      * @param {String}   eventName The type of event to listen for
5479      * @param {Function} handler The method the event invokes
5480      * @param {Object}   scope (optional) The scope in which to execute the handler
5481      * function. The handler function's "this" context.
5482      * @param {Object}   options (optional) An object containing handler configuration
5483      * properties. This may contain any of the following properties:<ul>
5484      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5485      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5486      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5487      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5488      * by the specified number of milliseconds. If the event fires again within that time, the original
5489      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5490      * </ul><br>
5491      * <p>
5492      * <b>Combining Options</b><br>
5493      * Using the options argument, it is possible to combine different types of listeners:<br>
5494      * <br>
5495      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5496                 <pre><code>
5497                 el.on('click', this.onClick, this, {
5498                         single: true,
5499                 delay: 100,
5500                 forumId: 4
5501                 });
5502                 </code></pre>
5503      * <p>
5504      * <b>Attaching multiple handlers in 1 call</b><br>
5505      * The method also allows for a single argument to be passed which is a config object containing properties
5506      * which specify multiple handlers.
5507      * <pre><code>
5508                 el.on({
5509                         'click': {
5510                         fn: this.onClick,
5511                         scope: this,
5512                         delay: 100
5513                 }, 
5514                 'mouseover': {
5515                         fn: this.onMouseOver,
5516                         scope: this
5517                 },
5518                 'mouseout': {
5519                         fn: this.onMouseOut,
5520                         scope: this
5521                 }
5522                 });
5523                 </code></pre>
5524      * <p>
5525      * Or a shorthand syntax which passes the same scope object to all handlers:
5526         <pre><code>
5527                 el.on({
5528                         'click': this.onClick,
5529                 'mouseover': this.onMouseOver,
5530                 'mouseout': this.onMouseOut,
5531                 scope: this
5532                 });
5533                 </code></pre>
5534      */
5535     addListener : function(eventName, fn, scope, o){
5536         if(typeof eventName == "object"){
5537             o = eventName;
5538             for(var e in o){
5539                 if(this.filterOptRe.test(e)){
5540                     continue;
5541                 }
5542                 if(typeof o[e] == "function"){
5543                     // shared options
5544                     this.addListener(e, o[e], o.scope,  o);
5545                 }else{
5546                     // individual options
5547                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5548                 }
5549             }
5550             return;
5551         }
5552         o = (!o || typeof o == "boolean") ? {} : o;
5553         eventName = eventName.toLowerCase();
5554         var ce = this.events[eventName] || true;
5555         if(typeof ce == "boolean"){
5556             ce = new Roo.util.Event(this, eventName);
5557             this.events[eventName] = ce;
5558         }
5559         ce.addListener(fn, scope, o);
5560     },
5561
5562     /**
5563      * Removes a listener
5564      * @param {String}   eventName     The type of event to listen for
5565      * @param {Function} handler        The handler to remove
5566      * @param {Object}   scope  (optional) The scope (this object) for the handler
5567      */
5568     removeListener : function(eventName, fn, scope){
5569         var ce = this.events[eventName.toLowerCase()];
5570         if(typeof ce == "object"){
5571             ce.removeListener(fn, scope);
5572         }
5573     },
5574
5575     /**
5576      * Removes all listeners for this object
5577      */
5578     purgeListeners : function(){
5579         for(var evt in this.events){
5580             if(typeof this.events[evt] == "object"){
5581                  this.events[evt].clearListeners();
5582             }
5583         }
5584     },
5585
5586     relayEvents : function(o, events){
5587         var createHandler = function(ename){
5588             return function(){
5589                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5590             };
5591         };
5592         for(var i = 0, len = events.length; i < len; i++){
5593             var ename = events[i];
5594             if(!this.events[ename]){ this.events[ename] = true; };
5595             o.on(ename, createHandler(ename), this);
5596         }
5597     },
5598
5599     /**
5600      * Used to define events on this Observable
5601      * @param {Object} object The object with the events defined
5602      */
5603     addEvents : function(o){
5604         if(!this.events){
5605             this.events = {};
5606         }
5607         Roo.applyIf(this.events, o);
5608     },
5609
5610     /**
5611      * Checks to see if this object has any listeners for a specified event
5612      * @param {String} eventName The name of the event to check for
5613      * @return {Boolean} True if the event is being listened for, else false
5614      */
5615     hasListener : function(eventName){
5616         var e = this.events[eventName];
5617         return typeof e == "object" && e.listeners.length > 0;
5618     }
5619 };
5620 /**
5621  * Appends an event handler to this element (shorthand for addListener)
5622  * @param {String}   eventName     The type of event to listen for
5623  * @param {Function} handler        The method the event invokes
5624  * @param {Object}   scope (optional) The scope in which to execute the handler
5625  * function. The handler function's "this" context.
5626  * @param {Object}   options  (optional)
5627  * @method
5628  */
5629 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5630 /**
5631  * Removes a listener (shorthand for removeListener)
5632  * @param {String}   eventName     The type of event to listen for
5633  * @param {Function} handler        The handler to remove
5634  * @param {Object}   scope  (optional) The scope (this object) for the handler
5635  * @method
5636  */
5637 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5638
5639 /**
5640  * Starts capture on the specified Observable. All events will be passed
5641  * to the supplied function with the event name + standard signature of the event
5642  * <b>before</b> the event is fired. If the supplied function returns false,
5643  * the event will not fire.
5644  * @param {Observable} o The Observable to capture
5645  * @param {Function} fn The function to call
5646  * @param {Object} scope (optional) The scope (this object) for the fn
5647  * @static
5648  */
5649 Roo.util.Observable.capture = function(o, fn, scope){
5650     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5651 };
5652
5653 /**
5654  * Removes <b>all</b> added captures from the Observable.
5655  * @param {Observable} o The Observable to release
5656  * @static
5657  */
5658 Roo.util.Observable.releaseCapture = function(o){
5659     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5660 };
5661
5662 (function(){
5663
5664     var createBuffered = function(h, o, scope){
5665         var task = new Roo.util.DelayedTask();
5666         return function(){
5667             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5668         };
5669     };
5670
5671     var createSingle = function(h, e, fn, scope){
5672         return function(){
5673             e.removeListener(fn, scope);
5674             return h.apply(scope, arguments);
5675         };
5676     };
5677
5678     var createDelayed = function(h, o, scope){
5679         return function(){
5680             var args = Array.prototype.slice.call(arguments, 0);
5681             setTimeout(function(){
5682                 h.apply(scope, args);
5683             }, o.delay || 10);
5684         };
5685     };
5686
5687     Roo.util.Event = function(obj, name){
5688         this.name = name;
5689         this.obj = obj;
5690         this.listeners = [];
5691     };
5692
5693     Roo.util.Event.prototype = {
5694         addListener : function(fn, scope, options){
5695             var o = options || {};
5696             scope = scope || this.obj;
5697             if(!this.isListening(fn, scope)){
5698                 var l = {fn: fn, scope: scope, options: o};
5699                 var h = fn;
5700                 if(o.delay){
5701                     h = createDelayed(h, o, scope);
5702                 }
5703                 if(o.single){
5704                     h = createSingle(h, this, fn, scope);
5705                 }
5706                 if(o.buffer){
5707                     h = createBuffered(h, o, scope);
5708                 }
5709                 l.fireFn = h;
5710                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5711                     this.listeners.push(l);
5712                 }else{
5713                     this.listeners = this.listeners.slice(0);
5714                     this.listeners.push(l);
5715                 }
5716             }
5717         },
5718
5719         findListener : function(fn, scope){
5720             scope = scope || this.obj;
5721             var ls = this.listeners;
5722             for(var i = 0, len = ls.length; i < len; i++){
5723                 var l = ls[i];
5724                 if(l.fn == fn && l.scope == scope){
5725                     return i;
5726                 }
5727             }
5728             return -1;
5729         },
5730
5731         isListening : function(fn, scope){
5732             return this.findListener(fn, scope) != -1;
5733         },
5734
5735         removeListener : function(fn, scope){
5736             var index;
5737             if((index = this.findListener(fn, scope)) != -1){
5738                 if(!this.firing){
5739                     this.listeners.splice(index, 1);
5740                 }else{
5741                     this.listeners = this.listeners.slice(0);
5742                     this.listeners.splice(index, 1);
5743                 }
5744                 return true;
5745             }
5746             return false;
5747         },
5748
5749         clearListeners : function(){
5750             this.listeners = [];
5751         },
5752
5753         fire : function(){
5754             var ls = this.listeners, scope, len = ls.length;
5755             if(len > 0){
5756                 this.firing = true;
5757                 var args = Array.prototype.slice.call(arguments, 0);
5758                 for(var i = 0; i < len; i++){
5759                     var l = ls[i];
5760                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5761                         this.firing = false;
5762                         return false;
5763                     }
5764                 }
5765                 this.firing = false;
5766             }
5767             return true;
5768         }
5769     };
5770 })();/*
5771  * Based on:
5772  * Ext JS Library 1.1.1
5773  * Copyright(c) 2006-2007, Ext JS, LLC.
5774  *
5775  * Originally Released Under LGPL - original licence link has changed is not relivant.
5776  *
5777  * Fork - LGPL
5778  * <script type="text/javascript">
5779  */
5780
5781 /**
5782  * @class Roo.EventManager
5783  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5784  * several useful events directly.
5785  * See {@link Roo.EventObject} for more details on normalized event objects.
5786  * @singleton
5787  */
5788 Roo.EventManager = function(){
5789     var docReadyEvent, docReadyProcId, docReadyState = false;
5790     var resizeEvent, resizeTask, textEvent, textSize;
5791     var E = Roo.lib.Event;
5792     var D = Roo.lib.Dom;
5793
5794
5795     var fireDocReady = function(){
5796         if(!docReadyState){
5797             docReadyState = true;
5798             Roo.isReady = true;
5799             if(docReadyProcId){
5800                 clearInterval(docReadyProcId);
5801             }
5802             if(Roo.isGecko || Roo.isOpera) {
5803                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5804             }
5805             if(Roo.isIE){
5806                 var defer = document.getElementById("ie-deferred-loader");
5807                 if(defer){
5808                     defer.onreadystatechange = null;
5809                     defer.parentNode.removeChild(defer);
5810                 }
5811             }
5812             if(docReadyEvent){
5813                 docReadyEvent.fire();
5814                 docReadyEvent.clearListeners();
5815             }
5816         }
5817     };
5818     
5819     var initDocReady = function(){
5820         docReadyEvent = new Roo.util.Event();
5821         if(Roo.isGecko || Roo.isOpera) {
5822             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5823         }else if(Roo.isIE){
5824             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5825             var defer = document.getElementById("ie-deferred-loader");
5826             defer.onreadystatechange = function(){
5827                 if(this.readyState == "complete"){
5828                     fireDocReady();
5829                 }
5830             };
5831         }else if(Roo.isSafari){ 
5832             docReadyProcId = setInterval(function(){
5833                 var rs = document.readyState;
5834                 if(rs == "complete") {
5835                     fireDocReady();     
5836                  }
5837             }, 10);
5838         }
5839         // no matter what, make sure it fires on load
5840         E.on(window, "load", fireDocReady);
5841     };
5842
5843     var createBuffered = function(h, o){
5844         var task = new Roo.util.DelayedTask(h);
5845         return function(e){
5846             // create new event object impl so new events don't wipe out properties
5847             e = new Roo.EventObjectImpl(e);
5848             task.delay(o.buffer, h, null, [e]);
5849         };
5850     };
5851
5852     var createSingle = function(h, el, ename, fn){
5853         return function(e){
5854             Roo.EventManager.removeListener(el, ename, fn);
5855             h(e);
5856         };
5857     };
5858
5859     var createDelayed = function(h, o){
5860         return function(e){
5861             // create new event object impl so new events don't wipe out properties
5862             e = new Roo.EventObjectImpl(e);
5863             setTimeout(function(){
5864                 h(e);
5865             }, o.delay || 10);
5866         };
5867     };
5868
5869     var listen = function(element, ename, opt, fn, scope){
5870         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5871         fn = fn || o.fn; scope = scope || o.scope;
5872         var el = Roo.getDom(element);
5873         if(!el){
5874             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5875         }
5876         var h = function(e){
5877             e = Roo.EventObject.setEvent(e);
5878             var t;
5879             if(o.delegate){
5880                 t = e.getTarget(o.delegate, el);
5881                 if(!t){
5882                     return;
5883                 }
5884             }else{
5885                 t = e.target;
5886             }
5887             if(o.stopEvent === true){
5888                 e.stopEvent();
5889             }
5890             if(o.preventDefault === true){
5891                e.preventDefault();
5892             }
5893             if(o.stopPropagation === true){
5894                 e.stopPropagation();
5895             }
5896
5897             if(o.normalized === false){
5898                 e = e.browserEvent;
5899             }
5900
5901             fn.call(scope || el, e, t, o);
5902         };
5903         if(o.delay){
5904             h = createDelayed(h, o);
5905         }
5906         if(o.single){
5907             h = createSingle(h, el, ename, fn);
5908         }
5909         if(o.buffer){
5910             h = createBuffered(h, o);
5911         }
5912         fn._handlers = fn._handlers || [];
5913         fn._handlers.push([Roo.id(el), ename, h]);
5914
5915         E.on(el, ename, h);
5916         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5917             el.addEventListener("DOMMouseScroll", h, false);
5918             E.on(window, 'unload', function(){
5919                 el.removeEventListener("DOMMouseScroll", h, false);
5920             });
5921         }
5922         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5923             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5924         }
5925         return h;
5926     };
5927
5928     var stopListening = function(el, ename, fn){
5929         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5930         if(hds){
5931             for(var i = 0, len = hds.length; i < len; i++){
5932                 var h = hds[i];
5933                 if(h[0] == id && h[1] == ename){
5934                     hd = h[2];
5935                     hds.splice(i, 1);
5936                     break;
5937                 }
5938             }
5939         }
5940         E.un(el, ename, hd);
5941         el = Roo.getDom(el);
5942         if(ename == "mousewheel" && el.addEventListener){
5943             el.removeEventListener("DOMMouseScroll", hd, false);
5944         }
5945         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5946             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
5947         }
5948     };
5949
5950     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
5951     
5952     var pub = {
5953         
5954         
5955         /** 
5956          * Fix for doc tools
5957          * @scope Roo.EventManager
5958          */
5959         
5960         
5961         /** 
5962          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
5963          * object with a Roo.EventObject
5964          * @param {Function} fn        The method the event invokes
5965          * @param {Object}   scope    An object that becomes the scope of the handler
5966          * @param {boolean}  override If true, the obj passed in becomes
5967          *                             the execution scope of the listener
5968          * @return {Function} The wrapped function
5969          * @deprecated
5970          */
5971         wrap : function(fn, scope, override){
5972             return function(e){
5973                 Roo.EventObject.setEvent(e);
5974                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
5975             };
5976         },
5977         
5978         /**
5979      * Appends an event handler to an element (shorthand for addListener)
5980      * @param {String/HTMLElement}   element        The html element or id to assign the
5981      * @param {String}   eventName The type of event to listen for
5982      * @param {Function} handler The method the event invokes
5983      * @param {Object}   scope (optional) The scope in which to execute the handler
5984      * function. The handler function's "this" context.
5985      * @param {Object}   options (optional) An object containing handler configuration
5986      * properties. This may contain any of the following properties:<ul>
5987      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5988      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
5989      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
5990      * <li>preventDefault {Boolean} True to prevent the default action</li>
5991      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
5992      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
5993      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5994      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5995      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5996      * by the specified number of milliseconds. If the event fires again within that time, the original
5997      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5998      * </ul><br>
5999      * <p>
6000      * <b>Combining Options</b><br>
6001      * Using the options argument, it is possible to combine different types of listeners:<br>
6002      * <br>
6003      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6004      * Code:<pre><code>
6005 el.on('click', this.onClick, this, {
6006     single: true,
6007     delay: 100,
6008     stopEvent : true,
6009     forumId: 4
6010 });</code></pre>
6011      * <p>
6012      * <b>Attaching multiple handlers in 1 call</b><br>
6013       * The method also allows for a single argument to be passed which is a config object containing properties
6014      * which specify multiple handlers.
6015      * <p>
6016      * Code:<pre><code>
6017 el.on({
6018     'click' : {
6019         fn: this.onClick
6020         scope: this,
6021         delay: 100
6022     },
6023     'mouseover' : {
6024         fn: this.onMouseOver
6025         scope: this
6026     },
6027     'mouseout' : {
6028         fn: this.onMouseOut
6029         scope: this
6030     }
6031 });</code></pre>
6032      * <p>
6033      * Or a shorthand syntax:<br>
6034      * Code:<pre><code>
6035 el.on({
6036     'click' : this.onClick,
6037     'mouseover' : this.onMouseOver,
6038     'mouseout' : this.onMouseOut
6039     scope: this
6040 });</code></pre>
6041      */
6042         addListener : function(element, eventName, fn, scope, options){
6043             if(typeof eventName == "object"){
6044                 var o = eventName;
6045                 for(var e in o){
6046                     if(propRe.test(e)){
6047                         continue;
6048                     }
6049                     if(typeof o[e] == "function"){
6050                         // shared options
6051                         listen(element, e, o, o[e], o.scope);
6052                     }else{
6053                         // individual options
6054                         listen(element, e, o[e]);
6055                     }
6056                 }
6057                 return;
6058             }
6059             return listen(element, eventName, options, fn, scope);
6060         },
6061         
6062         /**
6063          * Removes an event handler
6064          *
6065          * @param {String/HTMLElement}   element        The id or html element to remove the 
6066          *                             event from
6067          * @param {String}   eventName     The type of event
6068          * @param {Function} fn
6069          * @return {Boolean} True if a listener was actually removed
6070          */
6071         removeListener : function(element, eventName, fn){
6072             return stopListening(element, eventName, fn);
6073         },
6074         
6075         /**
6076          * Fires when the document is ready (before onload and before images are loaded). Can be 
6077          * accessed shorthanded Roo.onReady().
6078          * @param {Function} fn        The method the event invokes
6079          * @param {Object}   scope    An  object that becomes the scope of the handler
6080          * @param {boolean}  options
6081          */
6082         onDocumentReady : function(fn, scope, options){
6083             if(docReadyState){ // if it already fired
6084                 docReadyEvent.addListener(fn, scope, options);
6085                 docReadyEvent.fire();
6086                 docReadyEvent.clearListeners();
6087                 return;
6088             }
6089             if(!docReadyEvent){
6090                 initDocReady();
6091             }
6092             docReadyEvent.addListener(fn, scope, options);
6093         },
6094         
6095         /**
6096          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6097          * @param {Function} fn        The method the event invokes
6098          * @param {Object}   scope    An object that becomes the scope of the handler
6099          * @param {boolean}  options
6100          */
6101         onWindowResize : function(fn, scope, options){
6102             if(!resizeEvent){
6103                 resizeEvent = new Roo.util.Event();
6104                 resizeTask = new Roo.util.DelayedTask(function(){
6105                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6106                 });
6107                 E.on(window, "resize", function(){
6108                     if(Roo.isIE){
6109                         resizeTask.delay(50);
6110                     }else{
6111                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6112                     }
6113                 });
6114             }
6115             resizeEvent.addListener(fn, scope, options);
6116         },
6117
6118         /**
6119          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6120          * @param {Function} fn        The method the event invokes
6121          * @param {Object}   scope    An object that becomes the scope of the handler
6122          * @param {boolean}  options
6123          */
6124         onTextResize : function(fn, scope, options){
6125             if(!textEvent){
6126                 textEvent = new Roo.util.Event();
6127                 var textEl = new Roo.Element(document.createElement('div'));
6128                 textEl.dom.className = 'x-text-resize';
6129                 textEl.dom.innerHTML = 'X';
6130                 textEl.appendTo(document.body);
6131                 textSize = textEl.dom.offsetHeight;
6132                 setInterval(function(){
6133                     if(textEl.dom.offsetHeight != textSize){
6134                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6135                     }
6136                 }, this.textResizeInterval);
6137             }
6138             textEvent.addListener(fn, scope, options);
6139         },
6140
6141         /**
6142          * Removes the passed window resize listener.
6143          * @param {Function} fn        The method the event invokes
6144          * @param {Object}   scope    The scope of handler
6145          */
6146         removeResizeListener : function(fn, scope){
6147             if(resizeEvent){
6148                 resizeEvent.removeListener(fn, scope);
6149             }
6150         },
6151
6152         // private
6153         fireResize : function(){
6154             if(resizeEvent){
6155                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6156             }   
6157         },
6158         /**
6159          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6160          */
6161         ieDeferSrc : false,
6162         /**
6163          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6164          */
6165         textResizeInterval : 50
6166     };
6167     
6168     /**
6169      * Fix for doc tools
6170      * @scopeAlias pub=Roo.EventManager
6171      */
6172     
6173      /**
6174      * Appends an event handler to an element (shorthand for addListener)
6175      * @param {String/HTMLElement}   element        The html element or id to assign the
6176      * @param {String}   eventName The type of event to listen for
6177      * @param {Function} handler The method the event invokes
6178      * @param {Object}   scope (optional) The scope in which to execute the handler
6179      * function. The handler function's "this" context.
6180      * @param {Object}   options (optional) An object containing handler configuration
6181      * properties. This may contain any of the following properties:<ul>
6182      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6183      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6184      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6185      * <li>preventDefault {Boolean} True to prevent the default action</li>
6186      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6187      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6188      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6189      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6190      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6191      * by the specified number of milliseconds. If the event fires again within that time, the original
6192      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6193      * </ul><br>
6194      * <p>
6195      * <b>Combining Options</b><br>
6196      * Using the options argument, it is possible to combine different types of listeners:<br>
6197      * <br>
6198      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6199      * Code:<pre><code>
6200 el.on('click', this.onClick, this, {
6201     single: true,
6202     delay: 100,
6203     stopEvent : true,
6204     forumId: 4
6205 });</code></pre>
6206      * <p>
6207      * <b>Attaching multiple handlers in 1 call</b><br>
6208       * The method also allows for a single argument to be passed which is a config object containing properties
6209      * which specify multiple handlers.
6210      * <p>
6211      * Code:<pre><code>
6212 el.on({
6213     'click' : {
6214         fn: this.onClick
6215         scope: this,
6216         delay: 100
6217     },
6218     'mouseover' : {
6219         fn: this.onMouseOver
6220         scope: this
6221     },
6222     'mouseout' : {
6223         fn: this.onMouseOut
6224         scope: this
6225     }
6226 });</code></pre>
6227      * <p>
6228      * Or a shorthand syntax:<br>
6229      * Code:<pre><code>
6230 el.on({
6231     'click' : this.onClick,
6232     'mouseover' : this.onMouseOver,
6233     'mouseout' : this.onMouseOut
6234     scope: this
6235 });</code></pre>
6236      */
6237     pub.on = pub.addListener;
6238     pub.un = pub.removeListener;
6239
6240     pub.stoppedMouseDownEvent = new Roo.util.Event();
6241     return pub;
6242 }();
6243 /**
6244   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6245   * @param {Function} fn        The method the event invokes
6246   * @param {Object}   scope    An  object that becomes the scope of the handler
6247   * @param {boolean}  override If true, the obj passed in becomes
6248   *                             the execution scope of the listener
6249   * @member Roo
6250   * @method onReady
6251  */
6252 Roo.onReady = Roo.EventManager.onDocumentReady;
6253
6254 Roo.onReady(function(){
6255     var bd = Roo.get(document.body);
6256     if(!bd){ return; }
6257
6258     var cls = [
6259             Roo.isIE ? "roo-ie"
6260             : Roo.isGecko ? "roo-gecko"
6261             : Roo.isOpera ? "roo-opera"
6262             : Roo.isSafari ? "roo-safari" : ""];
6263
6264     if(Roo.isMac){
6265         cls.push("roo-mac");
6266     }
6267     if(Roo.isLinux){
6268         cls.push("roo-linux");
6269     }
6270     if(Roo.isBorderBox){
6271         cls.push('roo-border-box');
6272     }
6273     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6274         var p = bd.dom.parentNode;
6275         if(p){
6276             p.className += ' roo-strict';
6277         }
6278     }
6279     bd.addClass(cls.join(' '));
6280 });
6281
6282 /**
6283  * @class Roo.EventObject
6284  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6285  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6286  * Example:
6287  * <pre><code>
6288  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6289     e.preventDefault();
6290     var target = e.getTarget();
6291     ...
6292  }
6293  var myDiv = Roo.get("myDiv");
6294  myDiv.on("click", handleClick);
6295  //or
6296  Roo.EventManager.on("myDiv", 'click', handleClick);
6297  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6298  </code></pre>
6299  * @singleton
6300  */
6301 Roo.EventObject = function(){
6302     
6303     var E = Roo.lib.Event;
6304     
6305     // safari keypress events for special keys return bad keycodes
6306     var safariKeys = {
6307         63234 : 37, // left
6308         63235 : 39, // right
6309         63232 : 38, // up
6310         63233 : 40, // down
6311         63276 : 33, // page up
6312         63277 : 34, // page down
6313         63272 : 46, // delete
6314         63273 : 36, // home
6315         63275 : 35  // end
6316     };
6317
6318     // normalize button clicks
6319     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6320                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6321
6322     Roo.EventObjectImpl = function(e){
6323         if(e){
6324             this.setEvent(e.browserEvent || e);
6325         }
6326     };
6327     Roo.EventObjectImpl.prototype = {
6328         /**
6329          * Used to fix doc tools.
6330          * @scope Roo.EventObject.prototype
6331          */
6332             
6333
6334         
6335         
6336         /** The normal browser event */
6337         browserEvent : null,
6338         /** The button pressed in a mouse event */
6339         button : -1,
6340         /** True if the shift key was down during the event */
6341         shiftKey : false,
6342         /** True if the control key was down during the event */
6343         ctrlKey : false,
6344         /** True if the alt key was down during the event */
6345         altKey : false,
6346
6347         /** Key constant 
6348         * @type Number */
6349         BACKSPACE : 8,
6350         /** Key constant 
6351         * @type Number */
6352         TAB : 9,
6353         /** Key constant 
6354         * @type Number */
6355         RETURN : 13,
6356         /** Key constant 
6357         * @type Number */
6358         ENTER : 13,
6359         /** Key constant 
6360         * @type Number */
6361         SHIFT : 16,
6362         /** Key constant 
6363         * @type Number */
6364         CONTROL : 17,
6365         /** Key constant 
6366         * @type Number */
6367         ESC : 27,
6368         /** Key constant 
6369         * @type Number */
6370         SPACE : 32,
6371         /** Key constant 
6372         * @type Number */
6373         PAGEUP : 33,
6374         /** Key constant 
6375         * @type Number */
6376         PAGEDOWN : 34,
6377         /** Key constant 
6378         * @type Number */
6379         END : 35,
6380         /** Key constant 
6381         * @type Number */
6382         HOME : 36,
6383         /** Key constant 
6384         * @type Number */
6385         LEFT : 37,
6386         /** Key constant 
6387         * @type Number */
6388         UP : 38,
6389         /** Key constant 
6390         * @type Number */
6391         RIGHT : 39,
6392         /** Key constant 
6393         * @type Number */
6394         DOWN : 40,
6395         /** Key constant 
6396         * @type Number */
6397         DELETE : 46,
6398         /** Key constant 
6399         * @type Number */
6400         F5 : 116,
6401
6402            /** @private */
6403         setEvent : function(e){
6404             if(e == this || (e && e.browserEvent)){ // already wrapped
6405                 return e;
6406             }
6407             this.browserEvent = e;
6408             if(e){
6409                 // normalize buttons
6410                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6411                 if(e.type == 'click' && this.button == -1){
6412                     this.button = 0;
6413                 }
6414                 this.type = e.type;
6415                 this.shiftKey = e.shiftKey;
6416                 // mac metaKey behaves like ctrlKey
6417                 this.ctrlKey = e.ctrlKey || e.metaKey;
6418                 this.altKey = e.altKey;
6419                 // in getKey these will be normalized for the mac
6420                 this.keyCode = e.keyCode;
6421                 // keyup warnings on firefox.
6422                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6423                 // cache the target for the delayed and or buffered events
6424                 this.target = E.getTarget(e);
6425                 // same for XY
6426                 this.xy = E.getXY(e);
6427             }else{
6428                 this.button = -1;
6429                 this.shiftKey = false;
6430                 this.ctrlKey = false;
6431                 this.altKey = false;
6432                 this.keyCode = 0;
6433                 this.charCode =0;
6434                 this.target = null;
6435                 this.xy = [0, 0];
6436             }
6437             return this;
6438         },
6439
6440         /**
6441          * Stop the event (preventDefault and stopPropagation)
6442          */
6443         stopEvent : function(){
6444             if(this.browserEvent){
6445                 if(this.browserEvent.type == 'mousedown'){
6446                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6447                 }
6448                 E.stopEvent(this.browserEvent);
6449             }
6450         },
6451
6452         /**
6453          * Prevents the browsers default handling of the event.
6454          */
6455         preventDefault : function(){
6456             if(this.browserEvent){
6457                 E.preventDefault(this.browserEvent);
6458             }
6459         },
6460
6461         /** @private */
6462         isNavKeyPress : function(){
6463             var k = this.keyCode;
6464             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6465             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6466         },
6467
6468         isSpecialKey : function(){
6469             var k = this.keyCode;
6470             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6471             (k == 16) || (k == 17) ||
6472             (k >= 18 && k <= 20) ||
6473             (k >= 33 && k <= 35) ||
6474             (k >= 36 && k <= 39) ||
6475             (k >= 44 && k <= 45);
6476         },
6477         /**
6478          * Cancels bubbling of the event.
6479          */
6480         stopPropagation : function(){
6481             if(this.browserEvent){
6482                 if(this.type == 'mousedown'){
6483                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6484                 }
6485                 E.stopPropagation(this.browserEvent);
6486             }
6487         },
6488
6489         /**
6490          * Gets the key code for the event.
6491          * @return {Number}
6492          */
6493         getCharCode : function(){
6494             return this.charCode || this.keyCode;
6495         },
6496
6497         /**
6498          * Returns a normalized keyCode for the event.
6499          * @return {Number} The key code
6500          */
6501         getKey : function(){
6502             var k = this.keyCode || this.charCode;
6503             return Roo.isSafari ? (safariKeys[k] || k) : k;
6504         },
6505
6506         /**
6507          * Gets the x coordinate of the event.
6508          * @return {Number}
6509          */
6510         getPageX : function(){
6511             return this.xy[0];
6512         },
6513
6514         /**
6515          * Gets the y coordinate of the event.
6516          * @return {Number}
6517          */
6518         getPageY : function(){
6519             return this.xy[1];
6520         },
6521
6522         /**
6523          * Gets the time of the event.
6524          * @return {Number}
6525          */
6526         getTime : function(){
6527             if(this.browserEvent){
6528                 return E.getTime(this.browserEvent);
6529             }
6530             return null;
6531         },
6532
6533         /**
6534          * Gets the page coordinates of the event.
6535          * @return {Array} The xy values like [x, y]
6536          */
6537         getXY : function(){
6538             return this.xy;
6539         },
6540
6541         /**
6542          * Gets the target for the event.
6543          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6544          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6545                 search as a number or element (defaults to 10 || document.body)
6546          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6547          * @return {HTMLelement}
6548          */
6549         getTarget : function(selector, maxDepth, returnEl){
6550             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6551         },
6552         /**
6553          * Gets the related target.
6554          * @return {HTMLElement}
6555          */
6556         getRelatedTarget : function(){
6557             if(this.browserEvent){
6558                 return E.getRelatedTarget(this.browserEvent);
6559             }
6560             return null;
6561         },
6562
6563         /**
6564          * Normalizes mouse wheel delta across browsers
6565          * @return {Number} The delta
6566          */
6567         getWheelDelta : function(){
6568             var e = this.browserEvent;
6569             var delta = 0;
6570             if(e.wheelDelta){ /* IE/Opera. */
6571                 delta = e.wheelDelta/120;
6572             }else if(e.detail){ /* Mozilla case. */
6573                 delta = -e.detail/3;
6574             }
6575             return delta;
6576         },
6577
6578         /**
6579          * Returns true if the control, meta, shift or alt key was pressed during this event.
6580          * @return {Boolean}
6581          */
6582         hasModifier : function(){
6583             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6584         },
6585
6586         /**
6587          * Returns true if the target of this event equals el or is a child of el
6588          * @param {String/HTMLElement/Element} el
6589          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6590          * @return {Boolean}
6591          */
6592         within : function(el, related){
6593             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6594             return t && Roo.fly(el).contains(t);
6595         },
6596
6597         getPoint : function(){
6598             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6599         }
6600     };
6601
6602     return new Roo.EventObjectImpl();
6603 }();
6604             
6605     /*
6606  * Based on:
6607  * Ext JS Library 1.1.1
6608  * Copyright(c) 2006-2007, Ext JS, LLC.
6609  *
6610  * Originally Released Under LGPL - original licence link has changed is not relivant.
6611  *
6612  * Fork - LGPL
6613  * <script type="text/javascript">
6614  */
6615
6616  
6617 // was in Composite Element!??!?!
6618  
6619 (function(){
6620     var D = Roo.lib.Dom;
6621     var E = Roo.lib.Event;
6622     var A = Roo.lib.Anim;
6623
6624     // local style camelizing for speed
6625     var propCache = {};
6626     var camelRe = /(-[a-z])/gi;
6627     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6628     var view = document.defaultView;
6629
6630 /**
6631  * @class Roo.Element
6632  * Represents an Element in the DOM.<br><br>
6633  * Usage:<br>
6634 <pre><code>
6635 var el = Roo.get("my-div");
6636
6637 // or with getEl
6638 var el = getEl("my-div");
6639
6640 // or with a DOM element
6641 var el = Roo.get(myDivElement);
6642 </code></pre>
6643  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6644  * each call instead of constructing a new one.<br><br>
6645  * <b>Animations</b><br />
6646  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6647  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6648 <pre>
6649 Option    Default   Description
6650 --------- --------  ---------------------------------------------
6651 duration  .35       The duration of the animation in seconds
6652 easing    easeOut   The YUI easing method
6653 callback  none      A function to execute when the anim completes
6654 scope     this      The scope (this) of the callback function
6655 </pre>
6656 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6657 * manipulate the animation. Here's an example:
6658 <pre><code>
6659 var el = Roo.get("my-div");
6660
6661 // no animation
6662 el.setWidth(100);
6663
6664 // default animation
6665 el.setWidth(100, true);
6666
6667 // animation with some options set
6668 el.setWidth(100, {
6669     duration: 1,
6670     callback: this.foo,
6671     scope: this
6672 });
6673
6674 // using the "anim" property to get the Anim object
6675 var opt = {
6676     duration: 1,
6677     callback: this.foo,
6678     scope: this
6679 };
6680 el.setWidth(100, opt);
6681 ...
6682 if(opt.anim.isAnimated()){
6683     opt.anim.stop();
6684 }
6685 </code></pre>
6686 * <b> Composite (Collections of) Elements</b><br />
6687  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6688  * @constructor Create a new Element directly.
6689  * @param {String/HTMLElement} element
6690  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6691  */
6692     Roo.Element = function(element, forceNew){
6693         var dom = typeof element == "string" ?
6694                 document.getElementById(element) : element;
6695         if(!dom){ // invalid id/element
6696             return null;
6697         }
6698         var id = dom.id;
6699         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6700             return Roo.Element.cache[id];
6701         }
6702
6703         /**
6704          * The DOM element
6705          * @type HTMLElement
6706          */
6707         this.dom = dom;
6708
6709         /**
6710          * The DOM element ID
6711          * @type String
6712          */
6713         this.id = id || Roo.id(dom);
6714     };
6715
6716     var El = Roo.Element;
6717
6718     El.prototype = {
6719         /**
6720          * The element's default display mode  (defaults to "")
6721          * @type String
6722          */
6723         originalDisplay : "",
6724
6725         visibilityMode : 1,
6726         /**
6727          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6728          * @type String
6729          */
6730         defaultUnit : "px",
6731         /**
6732          * Sets the element's visibility mode. When setVisible() is called it
6733          * will use this to determine whether to set the visibility or the display property.
6734          * @param visMode Element.VISIBILITY or Element.DISPLAY
6735          * @return {Roo.Element} this
6736          */
6737         setVisibilityMode : function(visMode){
6738             this.visibilityMode = visMode;
6739             return this;
6740         },
6741         /**
6742          * Convenience method for setVisibilityMode(Element.DISPLAY)
6743          * @param {String} display (optional) What to set display to when visible
6744          * @return {Roo.Element} this
6745          */
6746         enableDisplayMode : function(display){
6747             this.setVisibilityMode(El.DISPLAY);
6748             if(typeof display != "undefined") this.originalDisplay = display;
6749             return this;
6750         },
6751
6752         /**
6753          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6754          * @param {String} selector The simple selector to test
6755          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6756                 search as a number or element (defaults to 10 || document.body)
6757          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6758          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6759          */
6760         findParent : function(simpleSelector, maxDepth, returnEl){
6761             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6762             maxDepth = maxDepth || 50;
6763             if(typeof maxDepth != "number"){
6764                 stopEl = Roo.getDom(maxDepth);
6765                 maxDepth = 10;
6766             }
6767             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6768                 if(dq.is(p, simpleSelector)){
6769                     return returnEl ? Roo.get(p) : p;
6770                 }
6771                 depth++;
6772                 p = p.parentNode;
6773             }
6774             return null;
6775         },
6776
6777
6778         /**
6779          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6780          * @param {String} selector The simple selector to test
6781          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6782                 search as a number or element (defaults to 10 || document.body)
6783          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6784          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6785          */
6786         findParentNode : function(simpleSelector, maxDepth, returnEl){
6787             var p = Roo.fly(this.dom.parentNode, '_internal');
6788             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6789         },
6790
6791         /**
6792          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6793          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6794          * @param {String} selector The simple selector to test
6795          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6796                 search as a number or element (defaults to 10 || document.body)
6797          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6798          */
6799         up : function(simpleSelector, maxDepth){
6800             return this.findParentNode(simpleSelector, maxDepth, true);
6801         },
6802
6803
6804
6805         /**
6806          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6807          * @param {String} selector The simple selector to test
6808          * @return {Boolean} True if this element matches the selector, else false
6809          */
6810         is : function(simpleSelector){
6811             return Roo.DomQuery.is(this.dom, simpleSelector);
6812         },
6813
6814         /**
6815          * Perform animation on this element.
6816          * @param {Object} args The YUI animation control args
6817          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6818          * @param {Function} onComplete (optional) Function to call when animation completes
6819          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6820          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6821          * @return {Roo.Element} this
6822          */
6823         animate : function(args, duration, onComplete, easing, animType){
6824             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6825             return this;
6826         },
6827
6828         /*
6829          * @private Internal animation call
6830          */
6831         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6832             animType = animType || 'run';
6833             opt = opt || {};
6834             var anim = Roo.lib.Anim[animType](
6835                 this.dom, args,
6836                 (opt.duration || defaultDur) || .35,
6837                 (opt.easing || defaultEase) || 'easeOut',
6838                 function(){
6839                     Roo.callback(cb, this);
6840                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6841                 },
6842                 this
6843             );
6844             opt.anim = anim;
6845             return anim;
6846         },
6847
6848         // private legacy anim prep
6849         preanim : function(a, i){
6850             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6851         },
6852
6853         /**
6854          * Removes worthless text nodes
6855          * @param {Boolean} forceReclean (optional) By default the element
6856          * keeps track if it has been cleaned already so
6857          * you can call this over and over. However, if you update the element and
6858          * need to force a reclean, you can pass true.
6859          */
6860         clean : function(forceReclean){
6861             if(this.isCleaned && forceReclean !== true){
6862                 return this;
6863             }
6864             var ns = /\S/;
6865             var d = this.dom, n = d.firstChild, ni = -1;
6866             while(n){
6867                 var nx = n.nextSibling;
6868                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6869                     d.removeChild(n);
6870                 }else{
6871                     n.nodeIndex = ++ni;
6872                 }
6873                 n = nx;
6874             }
6875             this.isCleaned = true;
6876             return this;
6877         },
6878
6879         // private
6880         calcOffsetsTo : function(el){
6881             el = Roo.get(el);
6882             var d = el.dom;
6883             var restorePos = false;
6884             if(el.getStyle('position') == 'static'){
6885                 el.position('relative');
6886                 restorePos = true;
6887             }
6888             var x = 0, y =0;
6889             var op = this.dom;
6890             while(op && op != d && op.tagName != 'HTML'){
6891                 x+= op.offsetLeft;
6892                 y+= op.offsetTop;
6893                 op = op.offsetParent;
6894             }
6895             if(restorePos){
6896                 el.position('static');
6897             }
6898             return [x, y];
6899         },
6900
6901         /**
6902          * Scrolls this element into view within the passed container.
6903          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6904          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6905          * @return {Roo.Element} this
6906          */
6907         scrollIntoView : function(container, hscroll){
6908             var c = Roo.getDom(container) || document.body;
6909             var el = this.dom;
6910
6911             var o = this.calcOffsetsTo(c),
6912                 l = o[0],
6913                 t = o[1],
6914                 b = t+el.offsetHeight,
6915                 r = l+el.offsetWidth;
6916
6917             var ch = c.clientHeight;
6918             var ct = parseInt(c.scrollTop, 10);
6919             var cl = parseInt(c.scrollLeft, 10);
6920             var cb = ct + ch;
6921             var cr = cl + c.clientWidth;
6922
6923             if(t < ct){
6924                 c.scrollTop = t;
6925             }else if(b > cb){
6926                 c.scrollTop = b-ch;
6927             }
6928
6929             if(hscroll !== false){
6930                 if(l < cl){
6931                     c.scrollLeft = l;
6932                 }else if(r > cr){
6933                     c.scrollLeft = r-c.clientWidth;
6934                 }
6935             }
6936             return this;
6937         },
6938
6939         // private
6940         scrollChildIntoView : function(child, hscroll){
6941             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
6942         },
6943
6944         /**
6945          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
6946          * the new height may not be available immediately.
6947          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
6948          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
6949          * @param {Function} onComplete (optional) Function to call when animation completes
6950          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
6951          * @return {Roo.Element} this
6952          */
6953         autoHeight : function(animate, duration, onComplete, easing){
6954             var oldHeight = this.getHeight();
6955             this.clip();
6956             this.setHeight(1); // force clipping
6957             setTimeout(function(){
6958                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
6959                 if(!animate){
6960                     this.setHeight(height);
6961                     this.unclip();
6962                     if(typeof onComplete == "function"){
6963                         onComplete();
6964                     }
6965                 }else{
6966                     this.setHeight(oldHeight); // restore original height
6967                     this.setHeight(height, animate, duration, function(){
6968                         this.unclip();
6969                         if(typeof onComplete == "function") onComplete();
6970                     }.createDelegate(this), easing);
6971                 }
6972             }.createDelegate(this), 0);
6973             return this;
6974         },
6975
6976         /**
6977          * Returns true if this element is an ancestor of the passed element
6978          * @param {HTMLElement/String} el The element to check
6979          * @return {Boolean} True if this element is an ancestor of el, else false
6980          */
6981         contains : function(el){
6982             if(!el){return false;}
6983             return D.isAncestor(this.dom, el.dom ? el.dom : el);
6984         },
6985
6986         /**
6987          * Checks whether the element is currently visible using both visibility and display properties.
6988          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
6989          * @return {Boolean} True if the element is currently visible, else false
6990          */
6991         isVisible : function(deep) {
6992             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
6993             if(deep !== true || !vis){
6994                 return vis;
6995             }
6996             var p = this.dom.parentNode;
6997             while(p && p.tagName.toLowerCase() != "body"){
6998                 if(!Roo.fly(p, '_isVisible').isVisible()){
6999                     return false;
7000                 }
7001                 p = p.parentNode;
7002             }
7003             return true;
7004         },
7005
7006         /**
7007          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7008          * @param {String} selector The CSS selector
7009          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7010          * @return {CompositeElement/CompositeElementLite} The composite element
7011          */
7012         select : function(selector, unique){
7013             return El.select(selector, unique, this.dom);
7014         },
7015
7016         /**
7017          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7018          * @param {String} selector The CSS selector
7019          * @return {Array} An array of the matched nodes
7020          */
7021         query : function(selector, unique){
7022             return Roo.DomQuery.select(selector, this.dom);
7023         },
7024
7025         /**
7026          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7027          * @param {String} selector The CSS selector
7028          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7029          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7030          */
7031         child : function(selector, returnDom){
7032             var n = Roo.DomQuery.selectNode(selector, this.dom);
7033             return returnDom ? n : Roo.get(n);
7034         },
7035
7036         /**
7037          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7038          * @param {String} selector The CSS selector
7039          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7040          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7041          */
7042         down : function(selector, returnDom){
7043             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7044             return returnDom ? n : Roo.get(n);
7045         },
7046
7047         /**
7048          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7049          * @param {String} group The group the DD object is member of
7050          * @param {Object} config The DD config object
7051          * @param {Object} overrides An object containing methods to override/implement on the DD object
7052          * @return {Roo.dd.DD} The DD object
7053          */
7054         initDD : function(group, config, overrides){
7055             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7056             return Roo.apply(dd, overrides);
7057         },
7058
7059         /**
7060          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7061          * @param {String} group The group the DDProxy object is member of
7062          * @param {Object} config The DDProxy config object
7063          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7064          * @return {Roo.dd.DDProxy} The DDProxy object
7065          */
7066         initDDProxy : function(group, config, overrides){
7067             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7068             return Roo.apply(dd, overrides);
7069         },
7070
7071         /**
7072          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7073          * @param {String} group The group the DDTarget object is member of
7074          * @param {Object} config The DDTarget config object
7075          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7076          * @return {Roo.dd.DDTarget} The DDTarget object
7077          */
7078         initDDTarget : function(group, config, overrides){
7079             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7080             return Roo.apply(dd, overrides);
7081         },
7082
7083         /**
7084          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7085          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7086          * @param {Boolean} visible Whether the element is visible
7087          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7088          * @return {Roo.Element} this
7089          */
7090          setVisible : function(visible, animate){
7091             if(!animate || !A){
7092                 if(this.visibilityMode == El.DISPLAY){
7093                     this.setDisplayed(visible);
7094                 }else{
7095                     this.fixDisplay();
7096                     this.dom.style.visibility = visible ? "visible" : "hidden";
7097                 }
7098             }else{
7099                 // closure for composites
7100                 var dom = this.dom;
7101                 var visMode = this.visibilityMode;
7102                 if(visible){
7103                     this.setOpacity(.01);
7104                     this.setVisible(true);
7105                 }
7106                 this.anim({opacity: { to: (visible?1:0) }},
7107                       this.preanim(arguments, 1),
7108                       null, .35, 'easeIn', function(){
7109                          if(!visible){
7110                              if(visMode == El.DISPLAY){
7111                                  dom.style.display = "none";
7112                              }else{
7113                                  dom.style.visibility = "hidden";
7114                              }
7115                              Roo.get(dom).setOpacity(1);
7116                          }
7117                      });
7118             }
7119             return this;
7120         },
7121
7122         /**
7123          * Returns true if display is not "none"
7124          * @return {Boolean}
7125          */
7126         isDisplayed : function() {
7127             return this.getStyle("display") != "none";
7128         },
7129
7130         /**
7131          * Toggles the element's visibility or display, depending on visibility mode.
7132          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7133          * @return {Roo.Element} this
7134          */
7135         toggle : function(animate){
7136             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7137             return this;
7138         },
7139
7140         /**
7141          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7142          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7143          * @return {Roo.Element} this
7144          */
7145         setDisplayed : function(value) {
7146             if(typeof value == "boolean"){
7147                value = value ? this.originalDisplay : "none";
7148             }
7149             this.setStyle("display", value);
7150             return this;
7151         },
7152
7153         /**
7154          * Tries to focus the element. Any exceptions are caught and ignored.
7155          * @return {Roo.Element} this
7156          */
7157         focus : function() {
7158             try{
7159                 this.dom.focus();
7160             }catch(e){}
7161             return this;
7162         },
7163
7164         /**
7165          * Tries to blur the element. Any exceptions are caught and ignored.
7166          * @return {Roo.Element} this
7167          */
7168         blur : function() {
7169             try{
7170                 this.dom.blur();
7171             }catch(e){}
7172             return this;
7173         },
7174
7175         /**
7176          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7177          * @param {String/Array} className The CSS class to add, or an array of classes
7178          * @return {Roo.Element} this
7179          */
7180         addClass : function(className){
7181             if(className instanceof Array){
7182                 for(var i = 0, len = className.length; i < len; i++) {
7183                     this.addClass(className[i]);
7184                 }
7185             }else{
7186                 if(className && !this.hasClass(className)){
7187                     this.dom.className = this.dom.className + " " + className;
7188                 }
7189             }
7190             return this;
7191         },
7192
7193         /**
7194          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7195          * @param {String/Array} className The CSS class to add, or an array of classes
7196          * @return {Roo.Element} this
7197          */
7198         radioClass : function(className){
7199             var siblings = this.dom.parentNode.childNodes;
7200             for(var i = 0; i < siblings.length; i++) {
7201                 var s = siblings[i];
7202                 if(s.nodeType == 1){
7203                     Roo.get(s).removeClass(className);
7204                 }
7205             }
7206             this.addClass(className);
7207             return this;
7208         },
7209
7210         /**
7211          * Removes one or more CSS classes from the element.
7212          * @param {String/Array} className The CSS class to remove, or an array of classes
7213          * @return {Roo.Element} this
7214          */
7215         removeClass : function(className){
7216             if(!className || !this.dom.className){
7217                 return this;
7218             }
7219             if(className instanceof Array){
7220                 for(var i = 0, len = className.length; i < len; i++) {
7221                     this.removeClass(className[i]);
7222                 }
7223             }else{
7224                 if(this.hasClass(className)){
7225                     var re = this.classReCache[className];
7226                     if (!re) {
7227                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7228                        this.classReCache[className] = re;
7229                     }
7230                     this.dom.className =
7231                         this.dom.className.replace(re, " ");
7232                 }
7233             }
7234             return this;
7235         },
7236
7237         // private
7238         classReCache: {},
7239
7240         /**
7241          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7242          * @param {String} className The CSS class to toggle
7243          * @return {Roo.Element} this
7244          */
7245         toggleClass : function(className){
7246             if(this.hasClass(className)){
7247                 this.removeClass(className);
7248             }else{
7249                 this.addClass(className);
7250             }
7251             return this;
7252         },
7253
7254         /**
7255          * Checks if the specified CSS class exists on this element's DOM node.
7256          * @param {String} className The CSS class to check for
7257          * @return {Boolean} True if the class exists, else false
7258          */
7259         hasClass : function(className){
7260             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7261         },
7262
7263         /**
7264          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7265          * @param {String} oldClassName The CSS class to replace
7266          * @param {String} newClassName The replacement CSS class
7267          * @return {Roo.Element} this
7268          */
7269         replaceClass : function(oldClassName, newClassName){
7270             this.removeClass(oldClassName);
7271             this.addClass(newClassName);
7272             return this;
7273         },
7274
7275         /**
7276          * Returns an object with properties matching the styles requested.
7277          * For example, el.getStyles('color', 'font-size', 'width') might return
7278          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7279          * @param {String} style1 A style name
7280          * @param {String} style2 A style name
7281          * @param {String} etc.
7282          * @return {Object} The style object
7283          */
7284         getStyles : function(){
7285             var a = arguments, len = a.length, r = {};
7286             for(var i = 0; i < len; i++){
7287                 r[a[i]] = this.getStyle(a[i]);
7288             }
7289             return r;
7290         },
7291
7292         /**
7293          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7294          * @param {String} property The style property whose value is returned.
7295          * @return {String} The current value of the style property for this element.
7296          */
7297         getStyle : function(){
7298             return view && view.getComputedStyle ?
7299                 function(prop){
7300                     var el = this.dom, v, cs, camel;
7301                     if(prop == 'float'){
7302                         prop = "cssFloat";
7303                     }
7304                     if(el.style && (v = el.style[prop])){
7305                         return v;
7306                     }
7307                     if(cs = view.getComputedStyle(el, "")){
7308                         if(!(camel = propCache[prop])){
7309                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7310                         }
7311                         return cs[camel];
7312                     }
7313                     return null;
7314                 } :
7315                 function(prop){
7316                     var el = this.dom, v, cs, camel;
7317                     if(prop == 'opacity'){
7318                         if(typeof el.style.filter == 'string'){
7319                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7320                             if(m){
7321                                 var fv = parseFloat(m[1]);
7322                                 if(!isNaN(fv)){
7323                                     return fv ? fv / 100 : 0;
7324                                 }
7325                             }
7326                         }
7327                         return 1;
7328                     }else if(prop == 'float'){
7329                         prop = "styleFloat";
7330                     }
7331                     if(!(camel = propCache[prop])){
7332                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7333                     }
7334                     if(v = el.style[camel]){
7335                         return v;
7336                     }
7337                     if(cs = el.currentStyle){
7338                         return cs[camel];
7339                     }
7340                     return null;
7341                 };
7342         }(),
7343
7344         /**
7345          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7346          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7347          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7348          * @return {Roo.Element} this
7349          */
7350         setStyle : function(prop, value){
7351             if(typeof prop == "string"){
7352                 var camel;
7353                 if(!(camel = propCache[prop])){
7354                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7355                 }
7356                 if(camel == 'opacity') {
7357                     this.setOpacity(value);
7358                 }else{
7359                     this.dom.style[camel] = value;
7360                 }
7361             }else{
7362                 for(var style in prop){
7363                     if(typeof prop[style] != "function"){
7364                        this.setStyle(style, prop[style]);
7365                     }
7366                 }
7367             }
7368             return this;
7369         },
7370
7371         /**
7372          * More flexible version of {@link #setStyle} for setting style properties.
7373          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7374          * a function which returns such a specification.
7375          * @return {Roo.Element} this
7376          */
7377         applyStyles : function(style){
7378             Roo.DomHelper.applyStyles(this.dom, style);
7379             return this;
7380         },
7381
7382         /**
7383           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7384           * @return {Number} The X position of the element
7385           */
7386         getX : function(){
7387             return D.getX(this.dom);
7388         },
7389
7390         /**
7391           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7392           * @return {Number} The Y position of the element
7393           */
7394         getY : function(){
7395             return D.getY(this.dom);
7396         },
7397
7398         /**
7399           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7400           * @return {Array} The XY position of the element
7401           */
7402         getXY : function(){
7403             return D.getXY(this.dom);
7404         },
7405
7406         /**
7407          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7408          * @param {Number} The X position of the element
7409          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7410          * @return {Roo.Element} this
7411          */
7412         setX : function(x, animate){
7413             if(!animate || !A){
7414                 D.setX(this.dom, x);
7415             }else{
7416                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7417             }
7418             return this;
7419         },
7420
7421         /**
7422          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7423          * @param {Number} The Y position of the element
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         setY : function(y, animate){
7428             if(!animate || !A){
7429                 D.setY(this.dom, y);
7430             }else{
7431                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7432             }
7433             return this;
7434         },
7435
7436         /**
7437          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7438          * @param {String} left The left CSS property value
7439          * @return {Roo.Element} this
7440          */
7441         setLeft : function(left){
7442             this.setStyle("left", this.addUnits(left));
7443             return this;
7444         },
7445
7446         /**
7447          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7448          * @param {String} top The top CSS property value
7449          * @return {Roo.Element} this
7450          */
7451         setTop : function(top){
7452             this.setStyle("top", this.addUnits(top));
7453             return this;
7454         },
7455
7456         /**
7457          * Sets the element's CSS right style.
7458          * @param {String} right The right CSS property value
7459          * @return {Roo.Element} this
7460          */
7461         setRight : function(right){
7462             this.setStyle("right", this.addUnits(right));
7463             return this;
7464         },
7465
7466         /**
7467          * Sets the element's CSS bottom style.
7468          * @param {String} bottom The bottom CSS property value
7469          * @return {Roo.Element} this
7470          */
7471         setBottom : function(bottom){
7472             this.setStyle("bottom", this.addUnits(bottom));
7473             return this;
7474         },
7475
7476         /**
7477          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7478          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7479          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7480          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7481          * @return {Roo.Element} this
7482          */
7483         setXY : function(pos, animate){
7484             if(!animate || !A){
7485                 D.setXY(this.dom, pos);
7486             }else{
7487                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7488             }
7489             return this;
7490         },
7491
7492         /**
7493          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7494          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7495          * @param {Number} x X value for new position (coordinates are page-based)
7496          * @param {Number} y Y value for new position (coordinates are page-based)
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setLocation : function(x, y, animate){
7501             this.setXY([x, y], this.preanim(arguments, 2));
7502             return this;
7503         },
7504
7505         /**
7506          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7507          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7508          * @param {Number} x X value for new position (coordinates are page-based)
7509          * @param {Number} y Y value for new position (coordinates are page-based)
7510          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7511          * @return {Roo.Element} this
7512          */
7513         moveTo : function(x, y, animate){
7514             this.setXY([x, y], this.preanim(arguments, 2));
7515             return this;
7516         },
7517
7518         /**
7519          * Returns the region of the given element.
7520          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7521          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7522          */
7523         getRegion : function(){
7524             return D.getRegion(this.dom);
7525         },
7526
7527         /**
7528          * Returns the offset height of the element
7529          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7530          * @return {Number} The element's height
7531          */
7532         getHeight : function(contentHeight){
7533             var h = this.dom.offsetHeight || 0;
7534             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7535         },
7536
7537         /**
7538          * Returns the offset width of the element
7539          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7540          * @return {Number} The element's width
7541          */
7542         getWidth : function(contentWidth){
7543             var w = this.dom.offsetWidth || 0;
7544             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7545         },
7546
7547         /**
7548          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7549          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7550          * if a height has not been set using CSS.
7551          * @return {Number}
7552          */
7553         getComputedHeight : function(){
7554             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7555             if(!h){
7556                 h = parseInt(this.getStyle('height'), 10) || 0;
7557                 if(!this.isBorderBox()){
7558                     h += this.getFrameWidth('tb');
7559                 }
7560             }
7561             return h;
7562         },
7563
7564         /**
7565          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7566          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7567          * if a width has not been set using CSS.
7568          * @return {Number}
7569          */
7570         getComputedWidth : function(){
7571             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7572             if(!w){
7573                 w = parseInt(this.getStyle('width'), 10) || 0;
7574                 if(!this.isBorderBox()){
7575                     w += this.getFrameWidth('lr');
7576                 }
7577             }
7578             return w;
7579         },
7580
7581         /**
7582          * Returns the size of the element.
7583          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7584          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7585          */
7586         getSize : function(contentSize){
7587             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7588         },
7589
7590         /**
7591          * Returns the width and height of the viewport.
7592          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7593          */
7594         getViewSize : function(){
7595             var d = this.dom, doc = document, aw = 0, ah = 0;
7596             if(d == doc || d == doc.body){
7597                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7598             }else{
7599                 return {
7600                     width : d.clientWidth,
7601                     height: d.clientHeight
7602                 };
7603             }
7604         },
7605
7606         /**
7607          * Returns the value of the "value" attribute
7608          * @param {Boolean} asNumber true to parse the value as a number
7609          * @return {String/Number}
7610          */
7611         getValue : function(asNumber){
7612             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7613         },
7614
7615         // private
7616         adjustWidth : function(width){
7617             if(typeof width == "number"){
7618                 if(this.autoBoxAdjust && !this.isBorderBox()){
7619                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7620                 }
7621                 if(width < 0){
7622                     width = 0;
7623                 }
7624             }
7625             return width;
7626         },
7627
7628         // private
7629         adjustHeight : function(height){
7630             if(typeof height == "number"){
7631                if(this.autoBoxAdjust && !this.isBorderBox()){
7632                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7633                }
7634                if(height < 0){
7635                    height = 0;
7636                }
7637             }
7638             return height;
7639         },
7640
7641         /**
7642          * Set the width of the element
7643          * @param {Number} width The new width
7644          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7645          * @return {Roo.Element} this
7646          */
7647         setWidth : function(width, animate){
7648             width = this.adjustWidth(width);
7649             if(!animate || !A){
7650                 this.dom.style.width = this.addUnits(width);
7651             }else{
7652                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7653             }
7654             return this;
7655         },
7656
7657         /**
7658          * Set the height of the element
7659          * @param {Number} height The new height
7660          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7661          * @return {Roo.Element} this
7662          */
7663          setHeight : function(height, animate){
7664             height = this.adjustHeight(height);
7665             if(!animate || !A){
7666                 this.dom.style.height = this.addUnits(height);
7667             }else{
7668                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7669             }
7670             return this;
7671         },
7672
7673         /**
7674          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7675          * @param {Number} width The new width
7676          * @param {Number} height The new height
7677          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7678          * @return {Roo.Element} this
7679          */
7680          setSize : function(width, height, animate){
7681             if(typeof width == "object"){ // in case of object from getSize()
7682                 height = width.height; width = width.width;
7683             }
7684             width = this.adjustWidth(width); height = this.adjustHeight(height);
7685             if(!animate || !A){
7686                 this.dom.style.width = this.addUnits(width);
7687                 this.dom.style.height = this.addUnits(height);
7688             }else{
7689                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7690             }
7691             return this;
7692         },
7693
7694         /**
7695          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7696          * @param {Number} x X value for new position (coordinates are page-based)
7697          * @param {Number} y Y value for new position (coordinates are page-based)
7698          * @param {Number} width The new width
7699          * @param {Number} height The new height
7700          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7701          * @return {Roo.Element} this
7702          */
7703         setBounds : function(x, y, width, height, animate){
7704             if(!animate || !A){
7705                 this.setSize(width, height);
7706                 this.setLocation(x, y);
7707             }else{
7708                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7709                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7710                               this.preanim(arguments, 4), 'motion');
7711             }
7712             return this;
7713         },
7714
7715         /**
7716          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7717          * @param {Roo.lib.Region} region The region to fill
7718          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7719          * @return {Roo.Element} this
7720          */
7721         setRegion : function(region, animate){
7722             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7723             return this;
7724         },
7725
7726         /**
7727          * Appends an event handler
7728          *
7729          * @param {String}   eventName     The type of event to append
7730          * @param {Function} fn        The method the event invokes
7731          * @param {Object} scope       (optional) The scope (this object) of the fn
7732          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7733          */
7734         addListener : function(eventName, fn, scope, options){
7735             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7736         },
7737
7738         /**
7739          * Removes an event handler from this element
7740          * @param {String} eventName the type of event to remove
7741          * @param {Function} fn the method the event invokes
7742          * @return {Roo.Element} this
7743          */
7744         removeListener : function(eventName, fn){
7745             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7746             return this;
7747         },
7748
7749         /**
7750          * Removes all previous added listeners from this element
7751          * @return {Roo.Element} this
7752          */
7753         removeAllListeners : function(){
7754             E.purgeElement(this.dom);
7755             return this;
7756         },
7757
7758         relayEvent : function(eventName, observable){
7759             this.on(eventName, function(e){
7760                 observable.fireEvent(eventName, e);
7761             });
7762         },
7763
7764         /**
7765          * Set the opacity of the element
7766          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7767          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7768          * @return {Roo.Element} this
7769          */
7770          setOpacity : function(opacity, animate){
7771             if(!animate || !A){
7772                 var s = this.dom.style;
7773                 if(Roo.isIE){
7774                     s.zoom = 1;
7775                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7776                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7777                 }else{
7778                     s.opacity = opacity;
7779                 }
7780             }else{
7781                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7782             }
7783             return this;
7784         },
7785
7786         /**
7787          * Gets the left X coordinate
7788          * @param {Boolean} local True to get the local css position instead of page coordinate
7789          * @return {Number}
7790          */
7791         getLeft : function(local){
7792             if(!local){
7793                 return this.getX();
7794             }else{
7795                 return parseInt(this.getStyle("left"), 10) || 0;
7796             }
7797         },
7798
7799         /**
7800          * Gets the right X coordinate of the element (element X position + element width)
7801          * @param {Boolean} local True to get the local css position instead of page coordinate
7802          * @return {Number}
7803          */
7804         getRight : function(local){
7805             if(!local){
7806                 return this.getX() + this.getWidth();
7807             }else{
7808                 return (this.getLeft(true) + this.getWidth()) || 0;
7809             }
7810         },
7811
7812         /**
7813          * Gets the top Y coordinate
7814          * @param {Boolean} local True to get the local css position instead of page coordinate
7815          * @return {Number}
7816          */
7817         getTop : function(local) {
7818             if(!local){
7819                 return this.getY();
7820             }else{
7821                 return parseInt(this.getStyle("top"), 10) || 0;
7822             }
7823         },
7824
7825         /**
7826          * Gets the bottom Y coordinate of the element (element Y position + element height)
7827          * @param {Boolean} local True to get the local css position instead of page coordinate
7828          * @return {Number}
7829          */
7830         getBottom : function(local){
7831             if(!local){
7832                 return this.getY() + this.getHeight();
7833             }else{
7834                 return (this.getTop(true) + this.getHeight()) || 0;
7835             }
7836         },
7837
7838         /**
7839         * Initializes positioning on this element. If a desired position is not passed, it will make the
7840         * the element positioned relative IF it is not already positioned.
7841         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7842         * @param {Number} zIndex (optional) The zIndex to apply
7843         * @param {Number} x (optional) Set the page X position
7844         * @param {Number} y (optional) Set the page Y position
7845         */
7846         position : function(pos, zIndex, x, y){
7847             if(!pos){
7848                if(this.getStyle('position') == 'static'){
7849                    this.setStyle('position', 'relative');
7850                }
7851             }else{
7852                 this.setStyle("position", pos);
7853             }
7854             if(zIndex){
7855                 this.setStyle("z-index", zIndex);
7856             }
7857             if(x !== undefined && y !== undefined){
7858                 this.setXY([x, y]);
7859             }else if(x !== undefined){
7860                 this.setX(x);
7861             }else if(y !== undefined){
7862                 this.setY(y);
7863             }
7864         },
7865
7866         /**
7867         * Clear positioning back to the default when the document was loaded
7868         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7869         * @return {Roo.Element} this
7870          */
7871         clearPositioning : function(value){
7872             value = value ||'';
7873             this.setStyle({
7874                 "left": value,
7875                 "right": value,
7876                 "top": value,
7877                 "bottom": value,
7878                 "z-index": "",
7879                 "position" : "static"
7880             });
7881             return this;
7882         },
7883
7884         /**
7885         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7886         * snapshot before performing an update and then restoring the element.
7887         * @return {Object}
7888         */
7889         getPositioning : function(){
7890             var l = this.getStyle("left");
7891             var t = this.getStyle("top");
7892             return {
7893                 "position" : this.getStyle("position"),
7894                 "left" : l,
7895                 "right" : l ? "" : this.getStyle("right"),
7896                 "top" : t,
7897                 "bottom" : t ? "" : this.getStyle("bottom"),
7898                 "z-index" : this.getStyle("z-index")
7899             };
7900         },
7901
7902         /**
7903          * Gets the width of the border(s) for the specified side(s)
7904          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7905          * passing lr would get the border (l)eft width + the border (r)ight width.
7906          * @return {Number} The width of the sides passed added together
7907          */
7908         getBorderWidth : function(side){
7909             return this.addStyles(side, El.borders);
7910         },
7911
7912         /**
7913          * Gets the width of the padding(s) for the specified side(s)
7914          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7915          * passing lr would get the padding (l)eft + the padding (r)ight.
7916          * @return {Number} The padding of the sides passed added together
7917          */
7918         getPadding : function(side){
7919             return this.addStyles(side, El.paddings);
7920         },
7921
7922         /**
7923         * Set positioning with an object returned by getPositioning().
7924         * @param {Object} posCfg
7925         * @return {Roo.Element} this
7926          */
7927         setPositioning : function(pc){
7928             this.applyStyles(pc);
7929             if(pc.right == "auto"){
7930                 this.dom.style.right = "";
7931             }
7932             if(pc.bottom == "auto"){
7933                 this.dom.style.bottom = "";
7934             }
7935             return this;
7936         },
7937
7938         // private
7939         fixDisplay : function(){
7940             if(this.getStyle("display") == "none"){
7941                 this.setStyle("visibility", "hidden");
7942                 this.setStyle("display", this.originalDisplay); // first try reverting to default
7943                 if(this.getStyle("display") == "none"){ // if that fails, default to block
7944                     this.setStyle("display", "block");
7945                 }
7946             }
7947         },
7948
7949         /**
7950          * Quick set left and top adding default units
7951          * @param {String} left The left CSS property value
7952          * @param {String} top The top CSS property value
7953          * @return {Roo.Element} this
7954          */
7955          setLeftTop : function(left, top){
7956             this.dom.style.left = this.addUnits(left);
7957             this.dom.style.top = this.addUnits(top);
7958             return this;
7959         },
7960
7961         /**
7962          * Move this element relative to its current position.
7963          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
7964          * @param {Number} distance How far to move the element in pixels
7965          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7966          * @return {Roo.Element} this
7967          */
7968          move : function(direction, distance, animate){
7969             var xy = this.getXY();
7970             direction = direction.toLowerCase();
7971             switch(direction){
7972                 case "l":
7973                 case "left":
7974                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
7975                     break;
7976                case "r":
7977                case "right":
7978                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
7979                     break;
7980                case "t":
7981                case "top":
7982                case "up":
7983                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
7984                     break;
7985                case "b":
7986                case "bottom":
7987                case "down":
7988                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
7989                     break;
7990             }
7991             return this;
7992         },
7993
7994         /**
7995          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
7996          * @return {Roo.Element} this
7997          */
7998         clip : function(){
7999             if(!this.isClipped){
8000                this.isClipped = true;
8001                this.originalClip = {
8002                    "o": this.getStyle("overflow"),
8003                    "x": this.getStyle("overflow-x"),
8004                    "y": this.getStyle("overflow-y")
8005                };
8006                this.setStyle("overflow", "hidden");
8007                this.setStyle("overflow-x", "hidden");
8008                this.setStyle("overflow-y", "hidden");
8009             }
8010             return this;
8011         },
8012
8013         /**
8014          *  Return clipping (overflow) to original clipping before clip() was called
8015          * @return {Roo.Element} this
8016          */
8017         unclip : function(){
8018             if(this.isClipped){
8019                 this.isClipped = false;
8020                 var o = this.originalClip;
8021                 if(o.o){this.setStyle("overflow", o.o);}
8022                 if(o.x){this.setStyle("overflow-x", o.x);}
8023                 if(o.y){this.setStyle("overflow-y", o.y);}
8024             }
8025             return this;
8026         },
8027
8028
8029         /**
8030          * Gets the x,y coordinates specified by the anchor position on the element.
8031          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8032          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8033          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8034          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8035          * @return {Array} [x, y] An array containing the element's x and y coordinates
8036          */
8037         getAnchorXY : function(anchor, local, s){
8038             //Passing a different size is useful for pre-calculating anchors,
8039             //especially for anchored animations that change the el size.
8040
8041             var w, h, vp = false;
8042             if(!s){
8043                 var d = this.dom;
8044                 if(d == document.body || d == document){
8045                     vp = true;
8046                     w = D.getViewWidth(); h = D.getViewHeight();
8047                 }else{
8048                     w = this.getWidth(); h = this.getHeight();
8049                 }
8050             }else{
8051                 w = s.width;  h = s.height;
8052             }
8053             var x = 0, y = 0, r = Math.round;
8054             switch((anchor || "tl").toLowerCase()){
8055                 case "c":
8056                     x = r(w*.5);
8057                     y = r(h*.5);
8058                 break;
8059                 case "t":
8060                     x = r(w*.5);
8061                     y = 0;
8062                 break;
8063                 case "l":
8064                     x = 0;
8065                     y = r(h*.5);
8066                 break;
8067                 case "r":
8068                     x = w;
8069                     y = r(h*.5);
8070                 break;
8071                 case "b":
8072                     x = r(w*.5);
8073                     y = h;
8074                 break;
8075                 case "tl":
8076                     x = 0;
8077                     y = 0;
8078                 break;
8079                 case "bl":
8080                     x = 0;
8081                     y = h;
8082                 break;
8083                 case "br":
8084                     x = w;
8085                     y = h;
8086                 break;
8087                 case "tr":
8088                     x = w;
8089                     y = 0;
8090                 break;
8091             }
8092             if(local === true){
8093                 return [x, y];
8094             }
8095             if(vp){
8096                 var sc = this.getScroll();
8097                 return [x + sc.left, y + sc.top];
8098             }
8099             //Add the element's offset xy
8100             var o = this.getXY();
8101             return [x+o[0], y+o[1]];
8102         },
8103
8104         /**
8105          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8106          * supported position values.
8107          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8108          * @param {String} position The position to align to.
8109          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8110          * @return {Array} [x, y]
8111          */
8112         getAlignToXY : function(el, p, o){
8113             el = Roo.get(el);
8114             var d = this.dom;
8115             if(!el.dom){
8116                 throw "Element.alignTo with an element that doesn't exist";
8117             }
8118             var c = false; //constrain to viewport
8119             var p1 = "", p2 = "";
8120             o = o || [0,0];
8121
8122             if(!p){
8123                 p = "tl-bl";
8124             }else if(p == "?"){
8125                 p = "tl-bl?";
8126             }else if(p.indexOf("-") == -1){
8127                 p = "tl-" + p;
8128             }
8129             p = p.toLowerCase();
8130             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8131             if(!m){
8132                throw "Element.alignTo with an invalid alignment " + p;
8133             }
8134             p1 = m[1]; p2 = m[2]; c = !!m[3];
8135
8136             //Subtract the aligned el's internal xy from the target's offset xy
8137             //plus custom offset to get the aligned el's new offset xy
8138             var a1 = this.getAnchorXY(p1, true);
8139             var a2 = el.getAnchorXY(p2, false);
8140             var x = a2[0] - a1[0] + o[0];
8141             var y = a2[1] - a1[1] + o[1];
8142             if(c){
8143                 //constrain the aligned el to viewport if necessary
8144                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8145                 // 5px of margin for ie
8146                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8147
8148                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8149                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8150                 //otherwise swap the aligned el to the opposite border of the target.
8151                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8152                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8153                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8154                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8155
8156                var doc = document;
8157                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8158                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8159
8160                if((x+w) > dw + scrollX){
8161                     x = swapX ? r.left-w : dw+scrollX-w;
8162                 }
8163                if(x < scrollX){
8164                    x = swapX ? r.right : scrollX;
8165                }
8166                if((y+h) > dh + scrollY){
8167                     y = swapY ? r.top-h : dh+scrollY-h;
8168                 }
8169                if (y < scrollY){
8170                    y = swapY ? r.bottom : scrollY;
8171                }
8172             }
8173             return [x,y];
8174         },
8175
8176         // private
8177         getConstrainToXY : function(){
8178             var os = {top:0, left:0, bottom:0, right: 0};
8179
8180             return function(el, local, offsets, proposedXY){
8181                 el = Roo.get(el);
8182                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8183
8184                 var vw, vh, vx = 0, vy = 0;
8185                 if(el.dom == document.body || el.dom == document){
8186                     vw = Roo.lib.Dom.getViewWidth();
8187                     vh = Roo.lib.Dom.getViewHeight();
8188                 }else{
8189                     vw = el.dom.clientWidth;
8190                     vh = el.dom.clientHeight;
8191                     if(!local){
8192                         var vxy = el.getXY();
8193                         vx = vxy[0];
8194                         vy = vxy[1];
8195                     }
8196                 }
8197
8198                 var s = el.getScroll();
8199
8200                 vx += offsets.left + s.left;
8201                 vy += offsets.top + s.top;
8202
8203                 vw -= offsets.right;
8204                 vh -= offsets.bottom;
8205
8206                 var vr = vx+vw;
8207                 var vb = vy+vh;
8208
8209                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8210                 var x = xy[0], y = xy[1];
8211                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8212
8213                 // only move it if it needs it
8214                 var moved = false;
8215
8216                 // first validate right/bottom
8217                 if((x + w) > vr){
8218                     x = vr - w;
8219                     moved = true;
8220                 }
8221                 if((y + h) > vb){
8222                     y = vb - h;
8223                     moved = true;
8224                 }
8225                 // then make sure top/left isn't negative
8226                 if(x < vx){
8227                     x = vx;
8228                     moved = true;
8229                 }
8230                 if(y < vy){
8231                     y = vy;
8232                     moved = true;
8233                 }
8234                 return moved ? [x, y] : false;
8235             };
8236         }(),
8237
8238         // private
8239         adjustForConstraints : function(xy, parent, offsets){
8240             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8241         },
8242
8243         /**
8244          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8245          * document it aligns it to the viewport.
8246          * The position parameter is optional, and can be specified in any one of the following formats:
8247          * <ul>
8248          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8249          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8250          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8251          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8252          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8253          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8254          * </ul>
8255          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8256          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8257          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8258          * that specified in order to enforce the viewport constraints.
8259          * Following are all of the supported anchor positions:
8260     <pre>
8261     Value  Description
8262     -----  -----------------------------
8263     tl     The top left corner (default)
8264     t      The center of the top edge
8265     tr     The top right corner
8266     l      The center of the left edge
8267     c      In the center of the element
8268     r      The center of the right edge
8269     bl     The bottom left corner
8270     b      The center of the bottom edge
8271     br     The bottom right corner
8272     </pre>
8273     Example Usage:
8274     <pre><code>
8275     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8276     el.alignTo("other-el");
8277
8278     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8279     el.alignTo("other-el", "tr?");
8280
8281     // align the bottom right corner of el with the center left edge of other-el
8282     el.alignTo("other-el", "br-l?");
8283
8284     // align the center of el with the bottom left corner of other-el and
8285     // adjust the x position by -6 pixels (and the y position by 0)
8286     el.alignTo("other-el", "c-bl", [-6, 0]);
8287     </code></pre>
8288          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8289          * @param {String} position The position to align to.
8290          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8291          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8292          * @return {Roo.Element} this
8293          */
8294         alignTo : function(element, position, offsets, animate){
8295             var xy = this.getAlignToXY(element, position, offsets);
8296             this.setXY(xy, this.preanim(arguments, 3));
8297             return this;
8298         },
8299
8300         /**
8301          * Anchors an element to another element and realigns it when the window is resized.
8302          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8303          * @param {String} position The position to align to.
8304          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8305          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8306          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8307          * is a number, it is used as the buffer delay (defaults to 50ms).
8308          * @param {Function} callback The function to call after the animation finishes
8309          * @return {Roo.Element} this
8310          */
8311         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8312             var action = function(){
8313                 this.alignTo(el, alignment, offsets, animate);
8314                 Roo.callback(callback, this);
8315             };
8316             Roo.EventManager.onWindowResize(action, this);
8317             var tm = typeof monitorScroll;
8318             if(tm != 'undefined'){
8319                 Roo.EventManager.on(window, 'scroll', action, this,
8320                     {buffer: tm == 'number' ? monitorScroll : 50});
8321             }
8322             action.call(this); // align immediately
8323             return this;
8324         },
8325         /**
8326          * Clears any opacity settings from this element. Required in some cases for IE.
8327          * @return {Roo.Element} this
8328          */
8329         clearOpacity : function(){
8330             if (window.ActiveXObject) {
8331                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8332                     this.dom.style.filter = "";
8333                 }
8334             } else {
8335                 this.dom.style.opacity = "";
8336                 this.dom.style["-moz-opacity"] = "";
8337                 this.dom.style["-khtml-opacity"] = "";
8338             }
8339             return this;
8340         },
8341
8342         /**
8343          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8344          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8345          * @return {Roo.Element} this
8346          */
8347         hide : function(animate){
8348             this.setVisible(false, this.preanim(arguments, 0));
8349             return this;
8350         },
8351
8352         /**
8353         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8354         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8355          * @return {Roo.Element} this
8356          */
8357         show : function(animate){
8358             this.setVisible(true, this.preanim(arguments, 0));
8359             return this;
8360         },
8361
8362         /**
8363          * @private Test if size has a unit, otherwise appends the default
8364          */
8365         addUnits : function(size){
8366             return Roo.Element.addUnits(size, this.defaultUnit);
8367         },
8368
8369         /**
8370          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8371          * @return {Roo.Element} this
8372          */
8373         beginMeasure : function(){
8374             var el = this.dom;
8375             if(el.offsetWidth || el.offsetHeight){
8376                 return this; // offsets work already
8377             }
8378             var changed = [];
8379             var p = this.dom, b = document.body; // start with this element
8380             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8381                 var pe = Roo.get(p);
8382                 if(pe.getStyle('display') == 'none'){
8383                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8384                     p.style.visibility = "hidden";
8385                     p.style.display = "block";
8386                 }
8387                 p = p.parentNode;
8388             }
8389             this._measureChanged = changed;
8390             return this;
8391
8392         },
8393
8394         /**
8395          * Restores displays to before beginMeasure was called
8396          * @return {Roo.Element} this
8397          */
8398         endMeasure : function(){
8399             var changed = this._measureChanged;
8400             if(changed){
8401                 for(var i = 0, len = changed.length; i < len; i++) {
8402                     var r = changed[i];
8403                     r.el.style.visibility = r.visibility;
8404                     r.el.style.display = "none";
8405                 }
8406                 this._measureChanged = null;
8407             }
8408             return this;
8409         },
8410
8411         /**
8412         * Update the innerHTML of this element, optionally searching for and processing scripts
8413         * @param {String} html The new HTML
8414         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8415         * @param {Function} callback For async script loading you can be noticed when the update completes
8416         * @return {Roo.Element} this
8417          */
8418         update : function(html, loadScripts, callback){
8419             if(typeof html == "undefined"){
8420                 html = "";
8421             }
8422             if(loadScripts !== true){
8423                 this.dom.innerHTML = html;
8424                 if(typeof callback == "function"){
8425                     callback();
8426                 }
8427                 return this;
8428             }
8429             var id = Roo.id();
8430             var dom = this.dom;
8431
8432             html += '<span id="' + id + '"></span>';
8433
8434             E.onAvailable(id, function(){
8435                 var hd = document.getElementsByTagName("head")[0];
8436                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8437                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8438                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8439
8440                 var match;
8441                 while(match = re.exec(html)){
8442                     var attrs = match[1];
8443                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8444                     if(srcMatch && srcMatch[2]){
8445                        var s = document.createElement("script");
8446                        s.src = srcMatch[2];
8447                        var typeMatch = attrs.match(typeRe);
8448                        if(typeMatch && typeMatch[2]){
8449                            s.type = typeMatch[2];
8450                        }
8451                        hd.appendChild(s);
8452                     }else if(match[2] && match[2].length > 0){
8453                         if(window.execScript) {
8454                            window.execScript(match[2]);
8455                         } else {
8456                             /**
8457                              * eval:var:id
8458                              * eval:var:dom
8459                              * eval:var:html
8460                              * 
8461                              */
8462                            window.eval(match[2]);
8463                         }
8464                     }
8465                 }
8466                 var el = document.getElementById(id);
8467                 if(el){el.parentNode.removeChild(el);}
8468                 if(typeof callback == "function"){
8469                     callback();
8470                 }
8471             });
8472             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8473             return this;
8474         },
8475
8476         /**
8477          * Direct access to the UpdateManager update() method (takes the same parameters).
8478          * @param {String/Function} url The url for this request or a function to call to get the url
8479          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8480          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8481          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8482          * @return {Roo.Element} this
8483          */
8484         load : function(){
8485             var um = this.getUpdateManager();
8486             um.update.apply(um, arguments);
8487             return this;
8488         },
8489
8490         /**
8491         * Gets this element's UpdateManager
8492         * @return {Roo.UpdateManager} The UpdateManager
8493         */
8494         getUpdateManager : function(){
8495             if(!this.updateManager){
8496                 this.updateManager = new Roo.UpdateManager(this);
8497             }
8498             return this.updateManager;
8499         },
8500
8501         /**
8502          * Disables text selection for this element (normalized across browsers)
8503          * @return {Roo.Element} this
8504          */
8505         unselectable : function(){
8506             this.dom.unselectable = "on";
8507             this.swallowEvent("selectstart", true);
8508             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8509             this.addClass("x-unselectable");
8510             return this;
8511         },
8512
8513         /**
8514         * Calculates the x, y to center this element on the screen
8515         * @return {Array} The x, y values [x, y]
8516         */
8517         getCenterXY : function(){
8518             return this.getAlignToXY(document, 'c-c');
8519         },
8520
8521         /**
8522         * Centers the Element in either the viewport, or another Element.
8523         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8524         */
8525         center : function(centerIn){
8526             this.alignTo(centerIn || document, 'c-c');
8527             return this;
8528         },
8529
8530         /**
8531          * Tests various css rules/browsers to determine if this element uses a border box
8532          * @return {Boolean}
8533          */
8534         isBorderBox : function(){
8535             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8536         },
8537
8538         /**
8539          * Return a box {x, y, width, height} that can be used to set another elements
8540          * size/location to match this element.
8541          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8542          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8543          * @return {Object} box An object in the format {x, y, width, height}
8544          */
8545         getBox : function(contentBox, local){
8546             var xy;
8547             if(!local){
8548                 xy = this.getXY();
8549             }else{
8550                 var left = parseInt(this.getStyle("left"), 10) || 0;
8551                 var top = parseInt(this.getStyle("top"), 10) || 0;
8552                 xy = [left, top];
8553             }
8554             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8555             if(!contentBox){
8556                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8557             }else{
8558                 var l = this.getBorderWidth("l")+this.getPadding("l");
8559                 var r = this.getBorderWidth("r")+this.getPadding("r");
8560                 var t = this.getBorderWidth("t")+this.getPadding("t");
8561                 var b = this.getBorderWidth("b")+this.getPadding("b");
8562                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8563             }
8564             bx.right = bx.x + bx.width;
8565             bx.bottom = bx.y + bx.height;
8566             return bx;
8567         },
8568
8569         /**
8570          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8571          for more information about the sides.
8572          * @param {String} sides
8573          * @return {Number}
8574          */
8575         getFrameWidth : function(sides, onlyContentBox){
8576             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8577         },
8578
8579         /**
8580          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8581          * @param {Object} box The box to fill {x, y, width, height}
8582          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8583          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8584          * @return {Roo.Element} this
8585          */
8586         setBox : function(box, adjust, animate){
8587             var w = box.width, h = box.height;
8588             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8589                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8590                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8591             }
8592             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8593             return this;
8594         },
8595
8596         /**
8597          * Forces the browser to repaint this element
8598          * @return {Roo.Element} this
8599          */
8600          repaint : function(){
8601             var dom = this.dom;
8602             this.addClass("x-repaint");
8603             setTimeout(function(){
8604                 Roo.get(dom).removeClass("x-repaint");
8605             }, 1);
8606             return this;
8607         },
8608
8609         /**
8610          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8611          * then it returns the calculated width of the sides (see getPadding)
8612          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8613          * @return {Object/Number}
8614          */
8615         getMargins : function(side){
8616             if(!side){
8617                 return {
8618                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8619                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8620                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8621                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8622                 };
8623             }else{
8624                 return this.addStyles(side, El.margins);
8625              }
8626         },
8627
8628         // private
8629         addStyles : function(sides, styles){
8630             var val = 0, v, w;
8631             for(var i = 0, len = sides.length; i < len; i++){
8632                 v = this.getStyle(styles[sides.charAt(i)]);
8633                 if(v){
8634                      w = parseInt(v, 10);
8635                      if(w){ val += w; }
8636                 }
8637             }
8638             return val;
8639         },
8640
8641         /**
8642          * Creates a proxy element of this element
8643          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8644          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8645          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8646          * @return {Roo.Element} The new proxy element
8647          */
8648         createProxy : function(config, renderTo, matchBox){
8649             if(renderTo){
8650                 renderTo = Roo.getDom(renderTo);
8651             }else{
8652                 renderTo = document.body;
8653             }
8654             config = typeof config == "object" ?
8655                 config : {tag : "div", cls: config};
8656             var proxy = Roo.DomHelper.append(renderTo, config, true);
8657             if(matchBox){
8658                proxy.setBox(this.getBox());
8659             }
8660             return proxy;
8661         },
8662
8663         /**
8664          * Puts a mask over this element to disable user interaction. Requires core.css.
8665          * This method can only be applied to elements which accept child nodes.
8666          * @param {String} msg (optional) A message to display in the mask
8667          * @param {String} msgCls (optional) A css class to apply to the msg element
8668          * @return {Element} The mask  element
8669          */
8670         mask : function(msg, msgCls){
8671             if(this.getStyle("position") == "static"){
8672                 this.setStyle("position", "relative");
8673             }
8674             if(!this._mask){
8675                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8676             }
8677             this.addClass("x-masked");
8678             this._mask.setDisplayed(true);
8679             if(typeof msg == 'string'){
8680                 if(!this._maskMsg){
8681                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8682                 }
8683                 var mm = this._maskMsg;
8684                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8685                 mm.dom.firstChild.innerHTML = msg;
8686                 mm.setDisplayed(true);
8687                 mm.center(this);
8688             }
8689             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8690                 this._mask.setHeight(this.getHeight());
8691             }
8692             return this._mask;
8693         },
8694
8695         /**
8696          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8697          * it is cached for reuse.
8698          */
8699         unmask : function(removeEl){
8700             if(this._mask){
8701                 if(removeEl === true){
8702                     this._mask.remove();
8703                     delete this._mask;
8704                     if(this._maskMsg){
8705                         this._maskMsg.remove();
8706                         delete this._maskMsg;
8707                     }
8708                 }else{
8709                     this._mask.setDisplayed(false);
8710                     if(this._maskMsg){
8711                         this._maskMsg.setDisplayed(false);
8712                     }
8713                 }
8714             }
8715             this.removeClass("x-masked");
8716         },
8717
8718         /**
8719          * Returns true if this element is masked
8720          * @return {Boolean}
8721          */
8722         isMasked : function(){
8723             return this._mask && this._mask.isVisible();
8724         },
8725
8726         /**
8727          * Creates an iframe shim for this element to keep selects and other windowed objects from
8728          * showing through.
8729          * @return {Roo.Element} The new shim element
8730          */
8731         createShim : function(){
8732             var el = document.createElement('iframe');
8733             el.frameBorder = 'no';
8734             el.className = 'roo-shim';
8735             if(Roo.isIE && Roo.isSecure){
8736                 el.src = Roo.SSL_SECURE_URL;
8737             }
8738             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8739             shim.autoBoxAdjust = false;
8740             return shim;
8741         },
8742
8743         /**
8744          * Removes this element from the DOM and deletes it from the cache
8745          */
8746         remove : function(){
8747             if(this.dom.parentNode){
8748                 this.dom.parentNode.removeChild(this.dom);
8749             }
8750             delete El.cache[this.dom.id];
8751         },
8752
8753         /**
8754          * Sets up event handlers to add and remove a css class when the mouse is over this element
8755          * @param {String} className
8756          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8757          * mouseout events for children elements
8758          * @return {Roo.Element} this
8759          */
8760         addClassOnOver : function(className, preventFlicker){
8761             this.on("mouseover", function(){
8762                 Roo.fly(this, '_internal').addClass(className);
8763             }, this.dom);
8764             var removeFn = function(e){
8765                 if(preventFlicker !== true || !e.within(this, true)){
8766                     Roo.fly(this, '_internal').removeClass(className);
8767                 }
8768             };
8769             this.on("mouseout", removeFn, this.dom);
8770             return this;
8771         },
8772
8773         /**
8774          * Sets up event handlers to add and remove a css class when this element has the focus
8775          * @param {String} className
8776          * @return {Roo.Element} this
8777          */
8778         addClassOnFocus : function(className){
8779             this.on("focus", function(){
8780                 Roo.fly(this, '_internal').addClass(className);
8781             }, this.dom);
8782             this.on("blur", function(){
8783                 Roo.fly(this, '_internal').removeClass(className);
8784             }, this.dom);
8785             return this;
8786         },
8787         /**
8788          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8789          * @param {String} className
8790          * @return {Roo.Element} this
8791          */
8792         addClassOnClick : function(className){
8793             var dom = this.dom;
8794             this.on("mousedown", function(){
8795                 Roo.fly(dom, '_internal').addClass(className);
8796                 var d = Roo.get(document);
8797                 var fn = function(){
8798                     Roo.fly(dom, '_internal').removeClass(className);
8799                     d.removeListener("mouseup", fn);
8800                 };
8801                 d.on("mouseup", fn);
8802             });
8803             return this;
8804         },
8805
8806         /**
8807          * Stops the specified event from bubbling and optionally prevents the default action
8808          * @param {String} eventName
8809          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8810          * @return {Roo.Element} this
8811          */
8812         swallowEvent : function(eventName, preventDefault){
8813             var fn = function(e){
8814                 e.stopPropagation();
8815                 if(preventDefault){
8816                     e.preventDefault();
8817                 }
8818             };
8819             if(eventName instanceof Array){
8820                 for(var i = 0, len = eventName.length; i < len; i++){
8821                      this.on(eventName[i], fn);
8822                 }
8823                 return this;
8824             }
8825             this.on(eventName, fn);
8826             return this;
8827         },
8828
8829         /**
8830          * @private
8831          */
8832       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8833
8834         /**
8835          * Sizes this element to its parent element's dimensions performing
8836          * neccessary box adjustments.
8837          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8838          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8839          * @return {Roo.Element} this
8840          */
8841         fitToParent : function(monitorResize, targetParent) {
8842           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8843           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8844           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8845             return;
8846           }
8847           var p = Roo.get(targetParent || this.dom.parentNode);
8848           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8849           if (monitorResize === true) {
8850             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8851             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8852           }
8853           return this;
8854         },
8855
8856         /**
8857          * Gets the next sibling, skipping text nodes
8858          * @return {HTMLElement} The next sibling or null
8859          */
8860         getNextSibling : function(){
8861             var n = this.dom.nextSibling;
8862             while(n && n.nodeType != 1){
8863                 n = n.nextSibling;
8864             }
8865             return n;
8866         },
8867
8868         /**
8869          * Gets the previous sibling, skipping text nodes
8870          * @return {HTMLElement} The previous sibling or null
8871          */
8872         getPrevSibling : function(){
8873             var n = this.dom.previousSibling;
8874             while(n && n.nodeType != 1){
8875                 n = n.previousSibling;
8876             }
8877             return n;
8878         },
8879
8880
8881         /**
8882          * Appends the passed element(s) to this element
8883          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8884          * @return {Roo.Element} this
8885          */
8886         appendChild: function(el){
8887             el = Roo.get(el);
8888             el.appendTo(this);
8889             return this;
8890         },
8891
8892         /**
8893          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8894          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8895          * automatically generated with the specified attributes.
8896          * @param {HTMLElement} insertBefore (optional) a child element of this element
8897          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8898          * @return {Roo.Element} The new child element
8899          */
8900         createChild: function(config, insertBefore, returnDom){
8901             config = config || {tag:'div'};
8902             if(insertBefore){
8903                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8904             }
8905             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8906         },
8907
8908         /**
8909          * Appends this element to the passed element
8910          * @param {String/HTMLElement/Element} el The new parent element
8911          * @return {Roo.Element} this
8912          */
8913         appendTo: function(el){
8914             el = Roo.getDom(el);
8915             el.appendChild(this.dom);
8916             return this;
8917         },
8918
8919         /**
8920          * Inserts this element before the passed element in the DOM
8921          * @param {String/HTMLElement/Element} el The element to insert before
8922          * @return {Roo.Element} this
8923          */
8924         insertBefore: function(el){
8925             el = Roo.getDom(el);
8926             el.parentNode.insertBefore(this.dom, el);
8927             return this;
8928         },
8929
8930         /**
8931          * Inserts this element after the passed element in the DOM
8932          * @param {String/HTMLElement/Element} el The element to insert after
8933          * @return {Roo.Element} this
8934          */
8935         insertAfter: function(el){
8936             el = Roo.getDom(el);
8937             el.parentNode.insertBefore(this.dom, el.nextSibling);
8938             return this;
8939         },
8940
8941         /**
8942          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
8943          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8944          * @return {Roo.Element} The new child
8945          */
8946         insertFirst: function(el, returnDom){
8947             el = el || {};
8948             if(typeof el == 'object' && !el.nodeType){ // dh config
8949                 return this.createChild(el, this.dom.firstChild, returnDom);
8950             }else{
8951                 el = Roo.getDom(el);
8952                 this.dom.insertBefore(el, this.dom.firstChild);
8953                 return !returnDom ? Roo.get(el) : el;
8954             }
8955         },
8956
8957         /**
8958          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
8959          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
8960          * @param {String} where (optional) 'before' or 'after' defaults to before
8961          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8962          * @return {Roo.Element} the inserted Element
8963          */
8964         insertSibling: function(el, where, returnDom){
8965             where = where ? where.toLowerCase() : 'before';
8966             el = el || {};
8967             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
8968
8969             if(typeof el == 'object' && !el.nodeType){ // dh config
8970                 if(where == 'after' && !this.dom.nextSibling){
8971                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
8972                 }else{
8973                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
8974                 }
8975
8976             }else{
8977                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
8978                             where == 'before' ? this.dom : this.dom.nextSibling);
8979                 if(!returnDom){
8980                     rt = Roo.get(rt);
8981                 }
8982             }
8983             return rt;
8984         },
8985
8986         /**
8987          * Creates and wraps this element with another element
8988          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
8989          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
8990          * @return {HTMLElement/Element} The newly created wrapper element
8991          */
8992         wrap: function(config, returnDom){
8993             if(!config){
8994                 config = {tag: "div"};
8995             }
8996             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
8997             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
8998             return newEl;
8999         },
9000
9001         /**
9002          * Replaces the passed element with this element
9003          * @param {String/HTMLElement/Element} el The element to replace
9004          * @return {Roo.Element} this
9005          */
9006         replace: function(el){
9007             el = Roo.get(el);
9008             this.insertBefore(el);
9009             el.remove();
9010             return this;
9011         },
9012
9013         /**
9014          * Inserts an html fragment into this element
9015          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9016          * @param {String} html The HTML fragment
9017          * @param {Boolean} returnEl True to return an Roo.Element
9018          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9019          */
9020         insertHtml : function(where, html, returnEl){
9021             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9022             return returnEl ? Roo.get(el) : el;
9023         },
9024
9025         /**
9026          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9027          * @param {Object} o The object with the attributes
9028          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9029          * @return {Roo.Element} this
9030          */
9031         set : function(o, useSet){
9032             var el = this.dom;
9033             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9034             for(var attr in o){
9035                 if(attr == "style" || typeof o[attr] == "function") continue;
9036                 if(attr=="cls"){
9037                     el.className = o["cls"];
9038                 }else{
9039                     if(useSet) el.setAttribute(attr, o[attr]);
9040                     else el[attr] = o[attr];
9041                 }
9042             }
9043             if(o.style){
9044                 Roo.DomHelper.applyStyles(el, o.style);
9045             }
9046             return this;
9047         },
9048
9049         /**
9050          * Convenience method for constructing a KeyMap
9051          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9052          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9053          * @param {Function} fn The function to call
9054          * @param {Object} scope (optional) The scope of the function
9055          * @return {Roo.KeyMap} The KeyMap created
9056          */
9057         addKeyListener : function(key, fn, scope){
9058             var config;
9059             if(typeof key != "object" || key instanceof Array){
9060                 config = {
9061                     key: key,
9062                     fn: fn,
9063                     scope: scope
9064                 };
9065             }else{
9066                 config = {
9067                     key : key.key,
9068                     shift : key.shift,
9069                     ctrl : key.ctrl,
9070                     alt : key.alt,
9071                     fn: fn,
9072                     scope: scope
9073                 };
9074             }
9075             return new Roo.KeyMap(this, config);
9076         },
9077
9078         /**
9079          * Creates a KeyMap for this element
9080          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9081          * @return {Roo.KeyMap} The KeyMap created
9082          */
9083         addKeyMap : function(config){
9084             return new Roo.KeyMap(this, config);
9085         },
9086
9087         /**
9088          * Returns true if this element is scrollable.
9089          * @return {Boolean}
9090          */
9091          isScrollable : function(){
9092             var dom = this.dom;
9093             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9094         },
9095
9096         /**
9097          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9098          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9099          * @param {Number} value The new scroll value
9100          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9101          * @return {Element} this
9102          */
9103
9104         scrollTo : function(side, value, animate){
9105             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9106             if(!animate || !A){
9107                 this.dom[prop] = value;
9108             }else{
9109                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9110                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9111             }
9112             return this;
9113         },
9114
9115         /**
9116          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9117          * within this element's scrollable range.
9118          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9119          * @param {Number} distance How far to scroll the element in pixels
9120          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9121          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9122          * was scrolled as far as it could go.
9123          */
9124          scroll : function(direction, distance, animate){
9125              if(!this.isScrollable()){
9126                  return;
9127              }
9128              var el = this.dom;
9129              var l = el.scrollLeft, t = el.scrollTop;
9130              var w = el.scrollWidth, h = el.scrollHeight;
9131              var cw = el.clientWidth, ch = el.clientHeight;
9132              direction = direction.toLowerCase();
9133              var scrolled = false;
9134              var a = this.preanim(arguments, 2);
9135              switch(direction){
9136                  case "l":
9137                  case "left":
9138                      if(w - l > cw){
9139                          var v = Math.min(l + distance, w-cw);
9140                          this.scrollTo("left", v, a);
9141                          scrolled = true;
9142                      }
9143                      break;
9144                 case "r":
9145                 case "right":
9146                      if(l > 0){
9147                          var v = Math.max(l - distance, 0);
9148                          this.scrollTo("left", v, a);
9149                          scrolled = true;
9150                      }
9151                      break;
9152                 case "t":
9153                 case "top":
9154                 case "up":
9155                      if(t > 0){
9156                          var v = Math.max(t - distance, 0);
9157                          this.scrollTo("top", v, a);
9158                          scrolled = true;
9159                      }
9160                      break;
9161                 case "b":
9162                 case "bottom":
9163                 case "down":
9164                      if(h - t > ch){
9165                          var v = Math.min(t + distance, h-ch);
9166                          this.scrollTo("top", v, a);
9167                          scrolled = true;
9168                      }
9169                      break;
9170              }
9171              return scrolled;
9172         },
9173
9174         /**
9175          * Translates the passed page coordinates into left/top css values for this element
9176          * @param {Number/Array} x The page x or an array containing [x, y]
9177          * @param {Number} y The page y
9178          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9179          */
9180         translatePoints : function(x, y){
9181             if(typeof x == 'object' || x instanceof Array){
9182                 y = x[1]; x = x[0];
9183             }
9184             var p = this.getStyle('position');
9185             var o = this.getXY();
9186
9187             var l = parseInt(this.getStyle('left'), 10);
9188             var t = parseInt(this.getStyle('top'), 10);
9189
9190             if(isNaN(l)){
9191                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9192             }
9193             if(isNaN(t)){
9194                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9195             }
9196
9197             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9198         },
9199
9200         /**
9201          * Returns the current scroll position of the element.
9202          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9203          */
9204         getScroll : function(){
9205             var d = this.dom, doc = document;
9206             if(d == doc || d == doc.body){
9207                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9208                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9209                 return {left: l, top: t};
9210             }else{
9211                 return {left: d.scrollLeft, top: d.scrollTop};
9212             }
9213         },
9214
9215         /**
9216          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9217          * are convert to standard 6 digit hex color.
9218          * @param {String} attr The css attribute
9219          * @param {String} defaultValue The default value to use when a valid color isn't found
9220          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9221          * YUI color anims.
9222          */
9223         getColor : function(attr, defaultValue, prefix){
9224             var v = this.getStyle(attr);
9225             if(!v || v == "transparent" || v == "inherit") {
9226                 return defaultValue;
9227             }
9228             var color = typeof prefix == "undefined" ? "#" : prefix;
9229             if(v.substr(0, 4) == "rgb("){
9230                 var rvs = v.slice(4, v.length -1).split(",");
9231                 for(var i = 0; i < 3; i++){
9232                     var h = parseInt(rvs[i]).toString(16);
9233                     if(h < 16){
9234                         h = "0" + h;
9235                     }
9236                     color += h;
9237                 }
9238             } else {
9239                 if(v.substr(0, 1) == "#"){
9240                     if(v.length == 4) {
9241                         for(var i = 1; i < 4; i++){
9242                             var c = v.charAt(i);
9243                             color +=  c + c;
9244                         }
9245                     }else if(v.length == 7){
9246                         color += v.substr(1);
9247                     }
9248                 }
9249             }
9250             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9251         },
9252
9253         /**
9254          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9255          * gradient background, rounded corners and a 4-way shadow.
9256          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9257          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9258          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9259          * @return {Roo.Element} this
9260          */
9261         boxWrap : function(cls){
9262             cls = cls || 'x-box';
9263             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9264             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9265             return el;
9266         },
9267
9268         /**
9269          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9270          * @param {String} namespace The namespace in which to look for the attribute
9271          * @param {String} name The attribute name
9272          * @return {String} The attribute value
9273          */
9274         getAttributeNS : Roo.isIE ? function(ns, name){
9275             var d = this.dom;
9276             var type = typeof d[ns+":"+name];
9277             if(type != 'undefined' && type != 'unknown'){
9278                 return d[ns+":"+name];
9279             }
9280             return d[name];
9281         } : function(ns, name){
9282             var d = this.dom;
9283             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9284         }
9285     };
9286
9287     var ep = El.prototype;
9288
9289     /**
9290      * Appends an event handler (Shorthand for addListener)
9291      * @param {String}   eventName     The type of event to append
9292      * @param {Function} fn        The method the event invokes
9293      * @param {Object} scope       (optional) The scope (this object) of the fn
9294      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9295      * @method
9296      */
9297     ep.on = ep.addListener;
9298         // backwards compat
9299     ep.mon = ep.addListener;
9300
9301     /**
9302      * Removes an event handler from this element (shorthand for removeListener)
9303      * @param {String} eventName the type of event to remove
9304      * @param {Function} fn the method the event invokes
9305      * @return {Roo.Element} this
9306      * @method
9307      */
9308     ep.un = ep.removeListener;
9309
9310     /**
9311      * true to automatically adjust width and height settings for box-model issues (default to true)
9312      */
9313     ep.autoBoxAdjust = true;
9314
9315     // private
9316     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9317
9318     // private
9319     El.addUnits = function(v, defaultUnit){
9320         if(v === "" || v == "auto"){
9321             return v;
9322         }
9323         if(v === undefined){
9324             return '';
9325         }
9326         if(typeof v == "number" || !El.unitPattern.test(v)){
9327             return v + (defaultUnit || 'px');
9328         }
9329         return v;
9330     };
9331
9332     // special markup used throughout Roo when box wrapping elements
9333     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9334     /**
9335      * Visibility mode constant - Use visibility to hide element
9336      * @static
9337      * @type Number
9338      */
9339     El.VISIBILITY = 1;
9340     /**
9341      * Visibility mode constant - Use display to hide element
9342      * @static
9343      * @type Number
9344      */
9345     El.DISPLAY = 2;
9346
9347     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9348     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9349     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9350
9351
9352
9353     /**
9354      * @private
9355      */
9356     El.cache = {};
9357
9358     var docEl;
9359
9360     /**
9361      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9362      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9363      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9364      * @return {Element} The Element object
9365      * @static
9366      */
9367     El.get = function(el){
9368         var ex, elm, id;
9369         if(!el){ return null; }
9370         if(typeof el == "string"){ // element id
9371             if(!(elm = document.getElementById(el))){
9372                 return null;
9373             }
9374             if(ex = El.cache[el]){
9375                 ex.dom = elm;
9376             }else{
9377                 ex = El.cache[el] = new El(elm);
9378             }
9379             return ex;
9380         }else if(el.tagName){ // dom element
9381             if(!(id = el.id)){
9382                 id = Roo.id(el);
9383             }
9384             if(ex = El.cache[id]){
9385                 ex.dom = el;
9386             }else{
9387                 ex = El.cache[id] = new El(el);
9388             }
9389             return ex;
9390         }else if(el instanceof El){
9391             if(el != docEl){
9392                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9393                                                               // catch case where it hasn't been appended
9394                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9395             }
9396             return el;
9397         }else if(el.isComposite){
9398             return el;
9399         }else if(el instanceof Array){
9400             return El.select(el);
9401         }else if(el == document){
9402             // create a bogus element object representing the document object
9403             if(!docEl){
9404                 var f = function(){};
9405                 f.prototype = El.prototype;
9406                 docEl = new f();
9407                 docEl.dom = document;
9408             }
9409             return docEl;
9410         }
9411         return null;
9412     };
9413
9414     // private
9415     El.uncache = function(el){
9416         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9417             if(a[i]){
9418                 delete El.cache[a[i].id || a[i]];
9419             }
9420         }
9421     };
9422
9423     // private
9424     // Garbage collection - uncache elements/purge listeners on orphaned elements
9425     // so we don't hold a reference and cause the browser to retain them
9426     El.garbageCollect = function(){
9427         if(!Roo.enableGarbageCollector){
9428             clearInterval(El.collectorThread);
9429             return;
9430         }
9431         for(var eid in El.cache){
9432             var el = El.cache[eid], d = el.dom;
9433             // -------------------------------------------------------
9434             // Determining what is garbage:
9435             // -------------------------------------------------------
9436             // !d
9437             // dom node is null, definitely garbage
9438             // -------------------------------------------------------
9439             // !d.parentNode
9440             // no parentNode == direct orphan, definitely garbage
9441             // -------------------------------------------------------
9442             // !d.offsetParent && !document.getElementById(eid)
9443             // display none elements have no offsetParent so we will
9444             // also try to look it up by it's id. However, check
9445             // offsetParent first so we don't do unneeded lookups.
9446             // This enables collection of elements that are not orphans
9447             // directly, but somewhere up the line they have an orphan
9448             // parent.
9449             // -------------------------------------------------------
9450             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9451                 delete El.cache[eid];
9452                 if(d && Roo.enableListenerCollection){
9453                     E.purgeElement(d);
9454                 }
9455             }
9456         }
9457     }
9458     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9459
9460
9461     // dom is optional
9462     El.Flyweight = function(dom){
9463         this.dom = dom;
9464     };
9465     El.Flyweight.prototype = El.prototype;
9466
9467     El._flyweights = {};
9468     /**
9469      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9470      * the dom node can be overwritten by other code.
9471      * @param {String/HTMLElement} el The dom node or id
9472      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9473      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9474      * @static
9475      * @return {Element} The shared Element object
9476      */
9477     El.fly = function(el, named){
9478         named = named || '_global';
9479         el = Roo.getDom(el);
9480         if(!el){
9481             return null;
9482         }
9483         if(!El._flyweights[named]){
9484             El._flyweights[named] = new El.Flyweight();
9485         }
9486         El._flyweights[named].dom = el;
9487         return El._flyweights[named];
9488     };
9489
9490     /**
9491      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9492      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9493      * Shorthand of {@link Roo.Element#get}
9494      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9495      * @return {Element} The Element object
9496      * @member Roo
9497      * @method get
9498      */
9499     Roo.get = El.get;
9500     /**
9501      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9502      * the dom node can be overwritten by other code.
9503      * Shorthand of {@link Roo.Element#fly}
9504      * @param {String/HTMLElement} el The dom node or id
9505      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9506      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9507      * @static
9508      * @return {Element} The shared Element object
9509      * @member Roo
9510      * @method fly
9511      */
9512     Roo.fly = El.fly;
9513
9514     // speedy lookup for elements never to box adjust
9515     var noBoxAdjust = Roo.isStrict ? {
9516         select:1
9517     } : {
9518         input:1, select:1, textarea:1
9519     };
9520     if(Roo.isIE || Roo.isGecko){
9521         noBoxAdjust['button'] = 1;
9522     }
9523
9524
9525     Roo.EventManager.on(window, 'unload', function(){
9526         delete El.cache;
9527         delete El._flyweights;
9528     });
9529 })();
9530
9531
9532
9533
9534 if(Roo.DomQuery){
9535     Roo.Element.selectorFunction = Roo.DomQuery.select;
9536 }
9537
9538 Roo.Element.select = function(selector, unique, root){
9539     var els;
9540     if(typeof selector == "string"){
9541         els = Roo.Element.selectorFunction(selector, root);
9542     }else if(selector.length !== undefined){
9543         els = selector;
9544     }else{
9545         throw "Invalid selector";
9546     }
9547     if(unique === true){
9548         return new Roo.CompositeElement(els);
9549     }else{
9550         return new Roo.CompositeElementLite(els);
9551     }
9552 };
9553 /**
9554  * Selects elements based on the passed CSS selector to enable working on them as 1.
9555  * @param {String/Array} selector The CSS selector or an array of elements
9556  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9557  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9558  * @return {CompositeElementLite/CompositeElement}
9559  * @member Roo
9560  * @method select
9561  */
9562 Roo.select = Roo.Element.select;
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577 /*
9578  * Based on:
9579  * Ext JS Library 1.1.1
9580  * Copyright(c) 2006-2007, Ext JS, LLC.
9581  *
9582  * Originally Released Under LGPL - original licence link has changed is not relivant.
9583  *
9584  * Fork - LGPL
9585  * <script type="text/javascript">
9586  */
9587
9588
9589
9590 //Notifies Element that fx methods are available
9591 Roo.enableFx = true;
9592
9593 /**
9594  * @class Roo.Fx
9595  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9596  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9597  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9598  * Element effects to work.</p><br/>
9599  *
9600  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9601  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9602  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9603  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9604  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9605  * expected results and should be done with care.</p><br/>
9606  *
9607  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9608  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9609 <pre>
9610 Value  Description
9611 -----  -----------------------------
9612 tl     The top left corner
9613 t      The center of the top edge
9614 tr     The top right corner
9615 l      The center of the left edge
9616 r      The center of the right edge
9617 bl     The bottom left corner
9618 b      The center of the bottom edge
9619 br     The bottom right corner
9620 </pre>
9621  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9622  * below are common options that can be passed to any Fx method.</b>
9623  * @cfg {Function} callback A function called when the effect is finished
9624  * @cfg {Object} scope The scope of the effect function
9625  * @cfg {String} easing A valid Easing value for the effect
9626  * @cfg {String} afterCls A css class to apply after the effect
9627  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9628  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9629  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9630  * effects that end with the element being visually hidden, ignored otherwise)
9631  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9632  * a function which returns such a specification that will be applied to the Element after the effect finishes
9633  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9634  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9635  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9636  */
9637 Roo.Fx = {
9638         /**
9639          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9640          * origin for the slide effect.  This function automatically handles wrapping the element with
9641          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9642          * Usage:
9643          *<pre><code>
9644 // default: slide the element in from the top
9645 el.slideIn();
9646
9647 // custom: slide the element in from the right with a 2-second duration
9648 el.slideIn('r', { duration: 2 });
9649
9650 // common config options shown with default values
9651 el.slideIn('t', {
9652     easing: 'easeOut',
9653     duration: .5
9654 });
9655 </code></pre>
9656          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9657          * @param {Object} options (optional) Object literal with any of the Fx config options
9658          * @return {Roo.Element} The Element
9659          */
9660     slideIn : function(anchor, o){
9661         var el = this.getFxEl();
9662         o = o || {};
9663
9664         el.queueFx(o, function(){
9665
9666             anchor = anchor || "t";
9667
9668             // fix display to visibility
9669             this.fixDisplay();
9670
9671             // restore values after effect
9672             var r = this.getFxRestore();
9673             var b = this.getBox();
9674             // fixed size for slide
9675             this.setSize(b);
9676
9677             // wrap if needed
9678             var wrap = this.fxWrap(r.pos, o, "hidden");
9679
9680             var st = this.dom.style;
9681             st.visibility = "visible";
9682             st.position = "absolute";
9683
9684             // clear out temp styles after slide and unwrap
9685             var after = function(){
9686                 el.fxUnwrap(wrap, r.pos, o);
9687                 st.width = r.width;
9688                 st.height = r.height;
9689                 el.afterFx(o);
9690             };
9691             // time to calc the positions
9692             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9693
9694             switch(anchor.toLowerCase()){
9695                 case "t":
9696                     wrap.setSize(b.width, 0);
9697                     st.left = st.bottom = "0";
9698                     a = {height: bh};
9699                 break;
9700                 case "l":
9701                     wrap.setSize(0, b.height);
9702                     st.right = st.top = "0";
9703                     a = {width: bw};
9704                 break;
9705                 case "r":
9706                     wrap.setSize(0, b.height);
9707                     wrap.setX(b.right);
9708                     st.left = st.top = "0";
9709                     a = {width: bw, points: pt};
9710                 break;
9711                 case "b":
9712                     wrap.setSize(b.width, 0);
9713                     wrap.setY(b.bottom);
9714                     st.left = st.top = "0";
9715                     a = {height: bh, points: pt};
9716                 break;
9717                 case "tl":
9718                     wrap.setSize(0, 0);
9719                     st.right = st.bottom = "0";
9720                     a = {width: bw, height: bh};
9721                 break;
9722                 case "bl":
9723                     wrap.setSize(0, 0);
9724                     wrap.setY(b.y+b.height);
9725                     st.right = st.top = "0";
9726                     a = {width: bw, height: bh, points: pt};
9727                 break;
9728                 case "br":
9729                     wrap.setSize(0, 0);
9730                     wrap.setXY([b.right, b.bottom]);
9731                     st.left = st.top = "0";
9732                     a = {width: bw, height: bh, points: pt};
9733                 break;
9734                 case "tr":
9735                     wrap.setSize(0, 0);
9736                     wrap.setX(b.x+b.width);
9737                     st.left = st.bottom = "0";
9738                     a = {width: bw, height: bh, points: pt};
9739                 break;
9740             }
9741             this.dom.style.visibility = "visible";
9742             wrap.show();
9743
9744             arguments.callee.anim = wrap.fxanim(a,
9745                 o,
9746                 'motion',
9747                 .5,
9748                 'easeOut', after);
9749         });
9750         return this;
9751     },
9752     
9753         /**
9754          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9755          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9756          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9757          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9758          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9759          * Usage:
9760          *<pre><code>
9761 // default: slide the element out to the top
9762 el.slideOut();
9763
9764 // custom: slide the element out to the right with a 2-second duration
9765 el.slideOut('r', { duration: 2 });
9766
9767 // common config options shown with default values
9768 el.slideOut('t', {
9769     easing: 'easeOut',
9770     duration: .5,
9771     remove: false,
9772     useDisplay: false
9773 });
9774 </code></pre>
9775          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9776          * @param {Object} options (optional) Object literal with any of the Fx config options
9777          * @return {Roo.Element} The Element
9778          */
9779     slideOut : function(anchor, o){
9780         var el = this.getFxEl();
9781         o = o || {};
9782
9783         el.queueFx(o, function(){
9784
9785             anchor = anchor || "t";
9786
9787             // restore values after effect
9788             var r = this.getFxRestore();
9789             
9790             var b = this.getBox();
9791             // fixed size for slide
9792             this.setSize(b);
9793
9794             // wrap if needed
9795             var wrap = this.fxWrap(r.pos, o, "visible");
9796
9797             var st = this.dom.style;
9798             st.visibility = "visible";
9799             st.position = "absolute";
9800
9801             wrap.setSize(b);
9802
9803             var after = function(){
9804                 if(o.useDisplay){
9805                     el.setDisplayed(false);
9806                 }else{
9807                     el.hide();
9808                 }
9809
9810                 el.fxUnwrap(wrap, r.pos, o);
9811
9812                 st.width = r.width;
9813                 st.height = r.height;
9814
9815                 el.afterFx(o);
9816             };
9817
9818             var a, zero = {to: 0};
9819             switch(anchor.toLowerCase()){
9820                 case "t":
9821                     st.left = st.bottom = "0";
9822                     a = {height: zero};
9823                 break;
9824                 case "l":
9825                     st.right = st.top = "0";
9826                     a = {width: zero};
9827                 break;
9828                 case "r":
9829                     st.left = st.top = "0";
9830                     a = {width: zero, points: {to:[b.right, b.y]}};
9831                 break;
9832                 case "b":
9833                     st.left = st.top = "0";
9834                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9835                 break;
9836                 case "tl":
9837                     st.right = st.bottom = "0";
9838                     a = {width: zero, height: zero};
9839                 break;
9840                 case "bl":
9841                     st.right = st.top = "0";
9842                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9843                 break;
9844                 case "br":
9845                     st.left = st.top = "0";
9846                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9847                 break;
9848                 case "tr":
9849                     st.left = st.bottom = "0";
9850                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9851                 break;
9852             }
9853
9854             arguments.callee.anim = wrap.fxanim(a,
9855                 o,
9856                 'motion',
9857                 .5,
9858                 "easeOut", after);
9859         });
9860         return this;
9861     },
9862
9863         /**
9864          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9865          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9866          * The element must be removed from the DOM using the 'remove' config option if desired.
9867          * Usage:
9868          *<pre><code>
9869 // default
9870 el.puff();
9871
9872 // common config options shown with default values
9873 el.puff({
9874     easing: 'easeOut',
9875     duration: .5,
9876     remove: false,
9877     useDisplay: false
9878 });
9879 </code></pre>
9880          * @param {Object} options (optional) Object literal with any of the Fx config options
9881          * @return {Roo.Element} The Element
9882          */
9883     puff : function(o){
9884         var el = this.getFxEl();
9885         o = o || {};
9886
9887         el.queueFx(o, function(){
9888             this.clearOpacity();
9889             this.show();
9890
9891             // restore values after effect
9892             var r = this.getFxRestore();
9893             var st = this.dom.style;
9894
9895             var after = function(){
9896                 if(o.useDisplay){
9897                     el.setDisplayed(false);
9898                 }else{
9899                     el.hide();
9900                 }
9901
9902                 el.clearOpacity();
9903
9904                 el.setPositioning(r.pos);
9905                 st.width = r.width;
9906                 st.height = r.height;
9907                 st.fontSize = '';
9908                 el.afterFx(o);
9909             };
9910
9911             var width = this.getWidth();
9912             var height = this.getHeight();
9913
9914             arguments.callee.anim = this.fxanim({
9915                     width : {to: this.adjustWidth(width * 2)},
9916                     height : {to: this.adjustHeight(height * 2)},
9917                     points : {by: [-(width * .5), -(height * .5)]},
9918                     opacity : {to: 0},
9919                     fontSize: {to:200, unit: "%"}
9920                 },
9921                 o,
9922                 'motion',
9923                 .5,
9924                 "easeOut", after);
9925         });
9926         return this;
9927     },
9928
9929         /**
9930          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
9931          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
9932          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
9933          * Usage:
9934          *<pre><code>
9935 // default
9936 el.switchOff();
9937
9938 // all config options shown with default values
9939 el.switchOff({
9940     easing: 'easeIn',
9941     duration: .3,
9942     remove: false,
9943     useDisplay: false
9944 });
9945 </code></pre>
9946          * @param {Object} options (optional) Object literal with any of the Fx config options
9947          * @return {Roo.Element} The Element
9948          */
9949     switchOff : function(o){
9950         var el = this.getFxEl();
9951         o = o || {};
9952
9953         el.queueFx(o, function(){
9954             this.clearOpacity();
9955             this.clip();
9956
9957             // restore values after effect
9958             var r = this.getFxRestore();
9959             var st = this.dom.style;
9960
9961             var after = function(){
9962                 if(o.useDisplay){
9963                     el.setDisplayed(false);
9964                 }else{
9965                     el.hide();
9966                 }
9967
9968                 el.clearOpacity();
9969                 el.setPositioning(r.pos);
9970                 st.width = r.width;
9971                 st.height = r.height;
9972
9973                 el.afterFx(o);
9974             };
9975
9976             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
9977                 this.clearOpacity();
9978                 (function(){
9979                     this.fxanim({
9980                         height:{to:1},
9981                         points:{by:[0, this.getHeight() * .5]}
9982                     }, o, 'motion', 0.3, 'easeIn', after);
9983                 }).defer(100, this);
9984             });
9985         });
9986         return this;
9987     },
9988
9989     /**
9990      * Highlights the Element by setting a color (applies to the background-color by default, but can be
9991      * changed using the "attr" config option) and then fading back to the original color. If no original
9992      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
9993      * Usage:
9994 <pre><code>
9995 // default: highlight background to yellow
9996 el.highlight();
9997
9998 // custom: highlight foreground text to blue for 2 seconds
9999 el.highlight("0000ff", { attr: 'color', duration: 2 });
10000
10001 // common config options shown with default values
10002 el.highlight("ffff9c", {
10003     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10004     endColor: (current color) or "ffffff",
10005     easing: 'easeIn',
10006     duration: 1
10007 });
10008 </code></pre>
10009      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10010      * @param {Object} options (optional) Object literal with any of the Fx config options
10011      * @return {Roo.Element} The Element
10012      */ 
10013     highlight : function(color, o){
10014         var el = this.getFxEl();
10015         o = o || {};
10016
10017         el.queueFx(o, function(){
10018             color = color || "ffff9c";
10019             attr = o.attr || "backgroundColor";
10020
10021             this.clearOpacity();
10022             this.show();
10023
10024             var origColor = this.getColor(attr);
10025             var restoreColor = this.dom.style[attr];
10026             endColor = (o.endColor || origColor) || "ffffff";
10027
10028             var after = function(){
10029                 el.dom.style[attr] = restoreColor;
10030                 el.afterFx(o);
10031             };
10032
10033             var a = {};
10034             a[attr] = {from: color, to: endColor};
10035             arguments.callee.anim = this.fxanim(a,
10036                 o,
10037                 'color',
10038                 1,
10039                 'easeIn', after);
10040         });
10041         return this;
10042     },
10043
10044    /**
10045     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10046     * Usage:
10047 <pre><code>
10048 // default: a single light blue ripple
10049 el.frame();
10050
10051 // custom: 3 red ripples lasting 3 seconds total
10052 el.frame("ff0000", 3, { duration: 3 });
10053
10054 // common config options shown with default values
10055 el.frame("C3DAF9", 1, {
10056     duration: 1 //duration of entire animation (not each individual ripple)
10057     // Note: Easing is not configurable and will be ignored if included
10058 });
10059 </code></pre>
10060     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10061     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10062     * @param {Object} options (optional) Object literal with any of the Fx config options
10063     * @return {Roo.Element} The Element
10064     */
10065     frame : function(color, count, o){
10066         var el = this.getFxEl();
10067         o = o || {};
10068
10069         el.queueFx(o, function(){
10070             color = color || "#C3DAF9";
10071             if(color.length == 6){
10072                 color = "#" + color;
10073             }
10074             count = count || 1;
10075             duration = o.duration || 1;
10076             this.show();
10077
10078             var b = this.getBox();
10079             var animFn = function(){
10080                 var proxy = this.createProxy({
10081
10082                      style:{
10083                         visbility:"hidden",
10084                         position:"absolute",
10085                         "z-index":"35000", // yee haw
10086                         border:"0px solid " + color
10087                      }
10088                   });
10089                 var scale = Roo.isBorderBox ? 2 : 1;
10090                 proxy.animate({
10091                     top:{from:b.y, to:b.y - 20},
10092                     left:{from:b.x, to:b.x - 20},
10093                     borderWidth:{from:0, to:10},
10094                     opacity:{from:1, to:0},
10095                     height:{from:b.height, to:(b.height + (20*scale))},
10096                     width:{from:b.width, to:(b.width + (20*scale))}
10097                 }, duration, function(){
10098                     proxy.remove();
10099                 });
10100                 if(--count > 0){
10101                      animFn.defer((duration/2)*1000, this);
10102                 }else{
10103                     el.afterFx(o);
10104                 }
10105             };
10106             animFn.call(this);
10107         });
10108         return this;
10109     },
10110
10111    /**
10112     * Creates a pause before any subsequent queued effects begin.  If there are
10113     * no effects queued after the pause it will have no effect.
10114     * Usage:
10115 <pre><code>
10116 el.pause(1);
10117 </code></pre>
10118     * @param {Number} seconds The length of time to pause (in seconds)
10119     * @return {Roo.Element} The Element
10120     */
10121     pause : function(seconds){
10122         var el = this.getFxEl();
10123         var o = {};
10124
10125         el.queueFx(o, function(){
10126             setTimeout(function(){
10127                 el.afterFx(o);
10128             }, seconds * 1000);
10129         });
10130         return this;
10131     },
10132
10133    /**
10134     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10135     * using the "endOpacity" config option.
10136     * Usage:
10137 <pre><code>
10138 // default: fade in from opacity 0 to 100%
10139 el.fadeIn();
10140
10141 // custom: fade in from opacity 0 to 75% over 2 seconds
10142 el.fadeIn({ endOpacity: .75, duration: 2});
10143
10144 // common config options shown with default values
10145 el.fadeIn({
10146     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10147     easing: 'easeOut',
10148     duration: .5
10149 });
10150 </code></pre>
10151     * @param {Object} options (optional) Object literal with any of the Fx config options
10152     * @return {Roo.Element} The Element
10153     */
10154     fadeIn : function(o){
10155         var el = this.getFxEl();
10156         o = o || {};
10157         el.queueFx(o, function(){
10158             this.setOpacity(0);
10159             this.fixDisplay();
10160             this.dom.style.visibility = 'visible';
10161             var to = o.endOpacity || 1;
10162             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10163                 o, null, .5, "easeOut", function(){
10164                 if(to == 1){
10165                     this.clearOpacity();
10166                 }
10167                 el.afterFx(o);
10168             });
10169         });
10170         return this;
10171     },
10172
10173    /**
10174     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10175     * using the "endOpacity" config option.
10176     * Usage:
10177 <pre><code>
10178 // default: fade out from the element's current opacity to 0
10179 el.fadeOut();
10180
10181 // custom: fade out from the element's current opacity to 25% over 2 seconds
10182 el.fadeOut({ endOpacity: .25, duration: 2});
10183
10184 // common config options shown with default values
10185 el.fadeOut({
10186     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10187     easing: 'easeOut',
10188     duration: .5
10189     remove: false,
10190     useDisplay: false
10191 });
10192 </code></pre>
10193     * @param {Object} options (optional) Object literal with any of the Fx config options
10194     * @return {Roo.Element} The Element
10195     */
10196     fadeOut : function(o){
10197         var el = this.getFxEl();
10198         o = o || {};
10199         el.queueFx(o, function(){
10200             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10201                 o, null, .5, "easeOut", function(){
10202                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10203                      this.dom.style.display = "none";
10204                 }else{
10205                      this.dom.style.visibility = "hidden";
10206                 }
10207                 this.clearOpacity();
10208                 el.afterFx(o);
10209             });
10210         });
10211         return this;
10212     },
10213
10214    /**
10215     * Animates the transition of an element's dimensions from a starting height/width
10216     * to an ending height/width.
10217     * Usage:
10218 <pre><code>
10219 // change height and width to 100x100 pixels
10220 el.scale(100, 100);
10221
10222 // common config options shown with default values.  The height and width will default to
10223 // the element's existing values if passed as null.
10224 el.scale(
10225     [element's width],
10226     [element's height], {
10227     easing: 'easeOut',
10228     duration: .35
10229 });
10230 </code></pre>
10231     * @param {Number} width  The new width (pass undefined to keep the original width)
10232     * @param {Number} height  The new height (pass undefined to keep the original height)
10233     * @param {Object} options (optional) Object literal with any of the Fx config options
10234     * @return {Roo.Element} The Element
10235     */
10236     scale : function(w, h, o){
10237         this.shift(Roo.apply({}, o, {
10238             width: w,
10239             height: h
10240         }));
10241         return this;
10242     },
10243
10244    /**
10245     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10246     * Any of these properties not specified in the config object will not be changed.  This effect 
10247     * requires that at least one new dimension, position or opacity setting must be passed in on
10248     * the config object in order for the function to have any effect.
10249     * Usage:
10250 <pre><code>
10251 // slide the element horizontally to x position 200 while changing the height and opacity
10252 el.shift({ x: 200, height: 50, opacity: .8 });
10253
10254 // common config options shown with default values.
10255 el.shift({
10256     width: [element's width],
10257     height: [element's height],
10258     x: [element's x position],
10259     y: [element's y position],
10260     opacity: [element's opacity],
10261     easing: 'easeOut',
10262     duration: .35
10263 });
10264 </code></pre>
10265     * @param {Object} options  Object literal with any of the Fx config options
10266     * @return {Roo.Element} The Element
10267     */
10268     shift : function(o){
10269         var el = this.getFxEl();
10270         o = o || {};
10271         el.queueFx(o, function(){
10272             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10273             if(w !== undefined){
10274                 a.width = {to: this.adjustWidth(w)};
10275             }
10276             if(h !== undefined){
10277                 a.height = {to: this.adjustHeight(h)};
10278             }
10279             if(x !== undefined || y !== undefined){
10280                 a.points = {to: [
10281                     x !== undefined ? x : this.getX(),
10282                     y !== undefined ? y : this.getY()
10283                 ]};
10284             }
10285             if(op !== undefined){
10286                 a.opacity = {to: op};
10287             }
10288             if(o.xy !== undefined){
10289                 a.points = {to: o.xy};
10290             }
10291             arguments.callee.anim = this.fxanim(a,
10292                 o, 'motion', .35, "easeOut", function(){
10293                 el.afterFx(o);
10294             });
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10301          * ending point of the effect.
10302          * Usage:
10303          *<pre><code>
10304 // default: slide the element downward while fading out
10305 el.ghost();
10306
10307 // custom: slide the element out to the right with a 2-second duration
10308 el.ghost('r', { duration: 2 });
10309
10310 // common config options shown with default values
10311 el.ghost('b', {
10312     easing: 'easeOut',
10313     duration: .5
10314     remove: false,
10315     useDisplay: false
10316 });
10317 </code></pre>
10318          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10319          * @param {Object} options (optional) Object literal with any of the Fx config options
10320          * @return {Roo.Element} The Element
10321          */
10322     ghost : function(anchor, o){
10323         var el = this.getFxEl();
10324         o = o || {};
10325
10326         el.queueFx(o, function(){
10327             anchor = anchor || "b";
10328
10329             // restore values after effect
10330             var r = this.getFxRestore();
10331             var w = this.getWidth(),
10332                 h = this.getHeight();
10333
10334             var st = this.dom.style;
10335
10336             var after = function(){
10337                 if(o.useDisplay){
10338                     el.setDisplayed(false);
10339                 }else{
10340                     el.hide();
10341                 }
10342
10343                 el.clearOpacity();
10344                 el.setPositioning(r.pos);
10345                 st.width = r.width;
10346                 st.height = r.height;
10347
10348                 el.afterFx(o);
10349             };
10350
10351             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10352             switch(anchor.toLowerCase()){
10353                 case "t":
10354                     pt.by = [0, -h];
10355                 break;
10356                 case "l":
10357                     pt.by = [-w, 0];
10358                 break;
10359                 case "r":
10360                     pt.by = [w, 0];
10361                 break;
10362                 case "b":
10363                     pt.by = [0, h];
10364                 break;
10365                 case "tl":
10366                     pt.by = [-w, -h];
10367                 break;
10368                 case "bl":
10369                     pt.by = [-w, h];
10370                 break;
10371                 case "br":
10372                     pt.by = [w, h];
10373                 break;
10374                 case "tr":
10375                     pt.by = [w, -h];
10376                 break;
10377             }
10378
10379             arguments.callee.anim = this.fxanim(a,
10380                 o,
10381                 'motion',
10382                 .5,
10383                 "easeOut", after);
10384         });
10385         return this;
10386     },
10387
10388         /**
10389          * Ensures that all effects queued after syncFx is called on the element are
10390          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10391          * @return {Roo.Element} The Element
10392          */
10393     syncFx : function(){
10394         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10395             block : false,
10396             concurrent : true,
10397             stopFx : false
10398         });
10399         return this;
10400     },
10401
10402         /**
10403          * Ensures that all effects queued after sequenceFx is called on the element are
10404          * run in sequence.  This is the opposite of {@link #syncFx}.
10405          * @return {Roo.Element} The Element
10406          */
10407     sequenceFx : function(){
10408         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10409             block : false,
10410             concurrent : false,
10411             stopFx : false
10412         });
10413         return this;
10414     },
10415
10416         /* @private */
10417     nextFx : function(){
10418         var ef = this.fxQueue[0];
10419         if(ef){
10420             ef.call(this);
10421         }
10422     },
10423
10424         /**
10425          * Returns true if the element has any effects actively running or queued, else returns false.
10426          * @return {Boolean} True if element has active effects, else false
10427          */
10428     hasActiveFx : function(){
10429         return this.fxQueue && this.fxQueue[0];
10430     },
10431
10432         /**
10433          * Stops any running effects and clears the element's internal effects queue if it contains
10434          * any additional effects that haven't started yet.
10435          * @return {Roo.Element} The Element
10436          */
10437     stopFx : function(){
10438         if(this.hasActiveFx()){
10439             var cur = this.fxQueue[0];
10440             if(cur && cur.anim && cur.anim.isAnimated()){
10441                 this.fxQueue = [cur]; // clear out others
10442                 cur.anim.stop(true);
10443             }
10444         }
10445         return this;
10446     },
10447
10448         /* @private */
10449     beforeFx : function(o){
10450         if(this.hasActiveFx() && !o.concurrent){
10451            if(o.stopFx){
10452                this.stopFx();
10453                return true;
10454            }
10455            return false;
10456         }
10457         return true;
10458     },
10459
10460         /**
10461          * Returns true if the element is currently blocking so that no other effect can be queued
10462          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10463          * used to ensure that an effect initiated by a user action runs to completion prior to the
10464          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10465          * @return {Boolean} True if blocking, else false
10466          */
10467     hasFxBlock : function(){
10468         var q = this.fxQueue;
10469         return q && q[0] && q[0].block;
10470     },
10471
10472         /* @private */
10473     queueFx : function(o, fn){
10474         if(!this.fxQueue){
10475             this.fxQueue = [];
10476         }
10477         if(!this.hasFxBlock()){
10478             Roo.applyIf(o, this.fxDefaults);
10479             if(!o.concurrent){
10480                 var run = this.beforeFx(o);
10481                 fn.block = o.block;
10482                 this.fxQueue.push(fn);
10483                 if(run){
10484                     this.nextFx();
10485                 }
10486             }else{
10487                 fn.call(this);
10488             }
10489         }
10490         return this;
10491     },
10492
10493         /* @private */
10494     fxWrap : function(pos, o, vis){
10495         var wrap;
10496         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10497             var wrapXY;
10498             if(o.fixPosition){
10499                 wrapXY = this.getXY();
10500             }
10501             var div = document.createElement("div");
10502             div.style.visibility = vis;
10503             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10504             wrap.setPositioning(pos);
10505             if(wrap.getStyle("position") == "static"){
10506                 wrap.position("relative");
10507             }
10508             this.clearPositioning('auto');
10509             wrap.clip();
10510             wrap.dom.appendChild(this.dom);
10511             if(wrapXY){
10512                 wrap.setXY(wrapXY);
10513             }
10514         }
10515         return wrap;
10516     },
10517
10518         /* @private */
10519     fxUnwrap : function(wrap, pos, o){
10520         this.clearPositioning();
10521         this.setPositioning(pos);
10522         if(!o.wrap){
10523             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10524             wrap.remove();
10525         }
10526     },
10527
10528         /* @private */
10529     getFxRestore : function(){
10530         var st = this.dom.style;
10531         return {pos: this.getPositioning(), width: st.width, height : st.height};
10532     },
10533
10534         /* @private */
10535     afterFx : function(o){
10536         if(o.afterStyle){
10537             this.applyStyles(o.afterStyle);
10538         }
10539         if(o.afterCls){
10540             this.addClass(o.afterCls);
10541         }
10542         if(o.remove === true){
10543             this.remove();
10544         }
10545         Roo.callback(o.callback, o.scope, [this]);
10546         if(!o.concurrent){
10547             this.fxQueue.shift();
10548             this.nextFx();
10549         }
10550     },
10551
10552         /* @private */
10553     getFxEl : function(){ // support for composite element fx
10554         return Roo.get(this.dom);
10555     },
10556
10557         /* @private */
10558     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10559         animType = animType || 'run';
10560         opt = opt || {};
10561         var anim = Roo.lib.Anim[animType](
10562             this.dom, args,
10563             (opt.duration || defaultDur) || .35,
10564             (opt.easing || defaultEase) || 'easeOut',
10565             function(){
10566                 Roo.callback(cb, this);
10567             },
10568             this
10569         );
10570         opt.anim = anim;
10571         return anim;
10572     }
10573 };
10574
10575 // backwords compat
10576 Roo.Fx.resize = Roo.Fx.scale;
10577
10578 //When included, Roo.Fx is automatically applied to Element so that all basic
10579 //effects are available directly via the Element API
10580 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10581  * Based on:
10582  * Ext JS Library 1.1.1
10583  * Copyright(c) 2006-2007, Ext JS, LLC.
10584  *
10585  * Originally Released Under LGPL - original licence link has changed is not relivant.
10586  *
10587  * Fork - LGPL
10588  * <script type="text/javascript">
10589  */
10590
10591
10592 /**
10593  * @class Roo.CompositeElement
10594  * Standard composite class. Creates a Roo.Element for every element in the collection.
10595  * <br><br>
10596  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10597  * actions will be performed on all the elements in this collection.</b>
10598  * <br><br>
10599  * All methods return <i>this</i> and can be chained.
10600  <pre><code>
10601  var els = Roo.select("#some-el div.some-class", true);
10602  // or select directly from an existing element
10603  var el = Roo.get('some-el');
10604  el.select('div.some-class', true);
10605
10606  els.setWidth(100); // all elements become 100 width
10607  els.hide(true); // all elements fade out and hide
10608  // or
10609  els.setWidth(100).hide(true);
10610  </code></pre>
10611  */
10612 Roo.CompositeElement = function(els){
10613     this.elements = [];
10614     this.addElements(els);
10615 };
10616 Roo.CompositeElement.prototype = {
10617     isComposite: true,
10618     addElements : function(els){
10619         if(!els) return this;
10620         if(typeof els == "string"){
10621             els = Roo.Element.selectorFunction(els);
10622         }
10623         var yels = this.elements;
10624         var index = yels.length-1;
10625         for(var i = 0, len = els.length; i < len; i++) {
10626                 yels[++index] = Roo.get(els[i]);
10627         }
10628         return this;
10629     },
10630
10631     /**
10632     * Clears this composite and adds the elements returned by the passed selector.
10633     * @param {String/Array} els A string CSS selector, an array of elements or an element
10634     * @return {CompositeElement} this
10635     */
10636     fill : function(els){
10637         this.elements = [];
10638         this.add(els);
10639         return this;
10640     },
10641
10642     /**
10643     * Filters this composite to only elements that match the passed selector.
10644     * @param {String} selector A string CSS selector
10645     * @return {CompositeElement} this
10646     */
10647     filter : function(selector){
10648         var els = [];
10649         this.each(function(el){
10650             if(el.is(selector)){
10651                 els[els.length] = el.dom;
10652             }
10653         });
10654         this.fill(els);
10655         return this;
10656     },
10657
10658     invoke : function(fn, args){
10659         var els = this.elements;
10660         for(var i = 0, len = els.length; i < len; i++) {
10661                 Roo.Element.prototype[fn].apply(els[i], args);
10662         }
10663         return this;
10664     },
10665     /**
10666     * Adds elements to this composite.
10667     * @param {String/Array} els A string CSS selector, an array of elements or an element
10668     * @return {CompositeElement} this
10669     */
10670     add : function(els){
10671         if(typeof els == "string"){
10672             this.addElements(Roo.Element.selectorFunction(els));
10673         }else if(els.length !== undefined){
10674             this.addElements(els);
10675         }else{
10676             this.addElements([els]);
10677         }
10678         return this;
10679     },
10680     /**
10681     * Calls the passed function passing (el, this, index) for each element in this composite.
10682     * @param {Function} fn The function to call
10683     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10684     * @return {CompositeElement} this
10685     */
10686     each : function(fn, scope){
10687         var els = this.elements;
10688         for(var i = 0, len = els.length; i < len; i++){
10689             if(fn.call(scope || els[i], els[i], this, i) === false) {
10690                 break;
10691             }
10692         }
10693         return this;
10694     },
10695
10696     /**
10697      * Returns the Element object at the specified index
10698      * @param {Number} index
10699      * @return {Roo.Element}
10700      */
10701     item : function(index){
10702         return this.elements[index] || null;
10703     },
10704
10705     /**
10706      * Returns the first Element
10707      * @return {Roo.Element}
10708      */
10709     first : function(){
10710         return this.item(0);
10711     },
10712
10713     /**
10714      * Returns the last Element
10715      * @return {Roo.Element}
10716      */
10717     last : function(){
10718         return this.item(this.elements.length-1);
10719     },
10720
10721     /**
10722      * Returns the number of elements in this composite
10723      * @return Number
10724      */
10725     getCount : function(){
10726         return this.elements.length;
10727     },
10728
10729     /**
10730      * Returns true if this composite contains the passed element
10731      * @return Boolean
10732      */
10733     contains : function(el){
10734         return this.indexOf(el) !== -1;
10735     },
10736
10737     /**
10738      * Returns true if this composite contains the passed element
10739      * @return Boolean
10740      */
10741     indexOf : function(el){
10742         return this.elements.indexOf(Roo.get(el));
10743     },
10744
10745
10746     /**
10747     * Removes the specified element(s).
10748     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10749     * or an array of any of those.
10750     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10751     * @return {CompositeElement} this
10752     */
10753     removeElement : function(el, removeDom){
10754         if(el instanceof Array){
10755             for(var i = 0, len = el.length; i < len; i++){
10756                 this.removeElement(el[i]);
10757             }
10758             return this;
10759         }
10760         var index = typeof el == 'number' ? el : this.indexOf(el);
10761         if(index !== -1){
10762             if(removeDom){
10763                 var d = this.elements[index];
10764                 if(d.dom){
10765                     d.remove();
10766                 }else{
10767                     d.parentNode.removeChild(d);
10768                 }
10769             }
10770             this.elements.splice(index, 1);
10771         }
10772         return this;
10773     },
10774
10775     /**
10776     * Replaces the specified element with the passed element.
10777     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10778     * to replace.
10779     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10780     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10781     * @return {CompositeElement} this
10782     */
10783     replaceElement : function(el, replacement, domReplace){
10784         var index = typeof el == 'number' ? el : this.indexOf(el);
10785         if(index !== -1){
10786             if(domReplace){
10787                 this.elements[index].replaceWith(replacement);
10788             }else{
10789                 this.elements.splice(index, 1, Roo.get(replacement))
10790             }
10791         }
10792         return this;
10793     },
10794
10795     /**
10796      * Removes all elements.
10797      */
10798     clear : function(){
10799         this.elements = [];
10800     }
10801 };
10802 (function(){
10803     Roo.CompositeElement.createCall = function(proto, fnName){
10804         if(!proto[fnName]){
10805             proto[fnName] = function(){
10806                 return this.invoke(fnName, arguments);
10807             };
10808         }
10809     };
10810     for(var fnName in Roo.Element.prototype){
10811         if(typeof Roo.Element.prototype[fnName] == "function"){
10812             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10813         }
10814     };
10815 })();
10816 /*
10817  * Based on:
10818  * Ext JS Library 1.1.1
10819  * Copyright(c) 2006-2007, Ext JS, LLC.
10820  *
10821  * Originally Released Under LGPL - original licence link has changed is not relivant.
10822  *
10823  * Fork - LGPL
10824  * <script type="text/javascript">
10825  */
10826
10827 /**
10828  * @class Roo.CompositeElementLite
10829  * @extends Roo.CompositeElement
10830  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10831  <pre><code>
10832  var els = Roo.select("#some-el div.some-class");
10833  // or select directly from an existing element
10834  var el = Roo.get('some-el');
10835  el.select('div.some-class');
10836
10837  els.setWidth(100); // all elements become 100 width
10838  els.hide(true); // all elements fade out and hide
10839  // or
10840  els.setWidth(100).hide(true);
10841  </code></pre><br><br>
10842  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10843  * actions will be performed on all the elements in this collection.</b>
10844  */
10845 Roo.CompositeElementLite = function(els){
10846     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10847     this.el = new Roo.Element.Flyweight();
10848 };
10849 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10850     addElements : function(els){
10851         if(els){
10852             if(els instanceof Array){
10853                 this.elements = this.elements.concat(els);
10854             }else{
10855                 var yels = this.elements;
10856                 var index = yels.length-1;
10857                 for(var i = 0, len = els.length; i < len; i++) {
10858                     yels[++index] = els[i];
10859                 }
10860             }
10861         }
10862         return this;
10863     },
10864     invoke : function(fn, args){
10865         var els = this.elements;
10866         var el = this.el;
10867         for(var i = 0, len = els.length; i < len; i++) {
10868             el.dom = els[i];
10869                 Roo.Element.prototype[fn].apply(el, args);
10870         }
10871         return this;
10872     },
10873     /**
10874      * Returns a flyweight Element of the dom element object at the specified index
10875      * @param {Number} index
10876      * @return {Roo.Element}
10877      */
10878     item : function(index){
10879         if(!this.elements[index]){
10880             return null;
10881         }
10882         this.el.dom = this.elements[index];
10883         return this.el;
10884     },
10885
10886     // fixes scope with flyweight
10887     addListener : function(eventName, handler, scope, opt){
10888         var els = this.elements;
10889         for(var i = 0, len = els.length; i < len; i++) {
10890             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10891         }
10892         return this;
10893     },
10894
10895     /**
10896     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10897     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10898     * a reference to the dom node, use el.dom.</b>
10899     * @param {Function} fn The function to call
10900     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10901     * @return {CompositeElement} this
10902     */
10903     each : function(fn, scope){
10904         var els = this.elements;
10905         var el = this.el;
10906         for(var i = 0, len = els.length; i < len; i++){
10907             el.dom = els[i];
10908                 if(fn.call(scope || el, el, this, i) === false){
10909                 break;
10910             }
10911         }
10912         return this;
10913     },
10914
10915     indexOf : function(el){
10916         return this.elements.indexOf(Roo.getDom(el));
10917     },
10918
10919     replaceElement : function(el, replacement, domReplace){
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             replacement = Roo.getDom(replacement);
10923             if(domReplace){
10924                 var d = this.elements[index];
10925                 d.parentNode.insertBefore(replacement, d);
10926                 d.parentNode.removeChild(d);
10927             }
10928             this.elements.splice(index, 1, replacement);
10929         }
10930         return this;
10931     }
10932 });
10933 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
10934
10935 /*
10936  * Based on:
10937  * Ext JS Library 1.1.1
10938  * Copyright(c) 2006-2007, Ext JS, LLC.
10939  *
10940  * Originally Released Under LGPL - original licence link has changed is not relivant.
10941  *
10942  * Fork - LGPL
10943  * <script type="text/javascript">
10944  */
10945
10946  
10947
10948 /**
10949  * @class Roo.data.Connection
10950  * @extends Roo.util.Observable
10951  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
10952  * either to a configured URL, or to a URL specified at request time.<br><br>
10953  * <p>
10954  * Requests made by this class are asynchronous, and will return immediately. No data from
10955  * the server will be available to the statement immediately following the {@link #request} call.
10956  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
10957  * <p>
10958  * Note: If you are doing a file upload, you will not get a normal response object sent back to
10959  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
10960  * The response object is created using the innerHTML of the IFRAME's document as the responseText
10961  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
10962  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
10963  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
10964  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
10965  * standard DOM methods.
10966  * @constructor
10967  * @param {Object} config a configuration object.
10968  */
10969 Roo.data.Connection = function(config){
10970     Roo.apply(this, config);
10971     this.addEvents({
10972         /**
10973          * @event beforerequest
10974          * Fires before a network request is made to retrieve a data object.
10975          * @param {Connection} conn This Connection object.
10976          * @param {Object} options The options config object passed to the {@link #request} method.
10977          */
10978         "beforerequest" : true,
10979         /**
10980          * @event requestcomplete
10981          * Fires if the request was successfully completed.
10982          * @param {Connection} conn This Connection object.
10983          * @param {Object} response The XHR object containing the response data.
10984          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10985          * @param {Object} options The options config object passed to the {@link #request} method.
10986          */
10987         "requestcomplete" : true,
10988         /**
10989          * @event requestexception
10990          * Fires if an error HTTP status was returned from the server.
10991          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
10992          * @param {Connection} conn This Connection object.
10993          * @param {Object} response The XHR object containing the response data.
10994          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
10995          * @param {Object} options The options config object passed to the {@link #request} method.
10996          */
10997         "requestexception" : true
10998     });
10999     Roo.data.Connection.superclass.constructor.call(this);
11000 };
11001
11002 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11003     /**
11004      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11005      */
11006     /**
11007      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11008      * extra parameters to each request made by this object. (defaults to undefined)
11009      */
11010     /**
11011      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11012      *  to each request made by this object. (defaults to undefined)
11013      */
11014     /**
11015      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11016      */
11017     /**
11018      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11019      */
11020     timeout : 30000,
11021     /**
11022      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11023      * @type Boolean
11024      */
11025     autoAbort:false,
11026
11027     /**
11028      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11029      * @type Boolean
11030      */
11031     disableCaching: true,
11032
11033     /**
11034      * Sends an HTTP request to a remote server.
11035      * @param {Object} options An object which may contain the following properties:<ul>
11036      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11037      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11038      * request, a url encoded string or a function to call to get either.</li>
11039      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11040      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11041      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11042      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11043      * <li>options {Object} The parameter to the request call.</li>
11044      * <li>success {Boolean} True if the request succeeded.</li>
11045      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11046      * </ul></li>
11047      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11048      * The callback is passed the following parameters:<ul>
11049      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11050      * <li>options {Object} The parameter to the request call.</li>
11051      * </ul></li>
11052      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11053      * The callback is passed the following parameters:<ul>
11054      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11055      * <li>options {Object} The parameter to the request call.</li>
11056      * </ul></li>
11057      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11058      * for the callback function. Defaults to the browser window.</li>
11059      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11060      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11061      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11062      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11063      * params for the post data. Any params will be appended to the URL.</li>
11064      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11065      * </ul>
11066      * @return {Number} transactionId
11067      */
11068     request : function(o){
11069         if(this.fireEvent("beforerequest", this, o) !== false){
11070             var p = o.params;
11071
11072             if(typeof p == "function"){
11073                 p = p.call(o.scope||window, o);
11074             }
11075             if(typeof p == "object"){
11076                 p = Roo.urlEncode(o.params);
11077             }
11078             if(this.extraParams){
11079                 var extras = Roo.urlEncode(this.extraParams);
11080                 p = p ? (p + '&' + extras) : extras;
11081             }
11082
11083             var url = o.url || this.url;
11084             if(typeof url == 'function'){
11085                 url = url.call(o.scope||window, o);
11086             }
11087
11088             if(o.form){
11089                 var form = Roo.getDom(o.form);
11090                 url = url || form.action;
11091
11092                 var enctype = form.getAttribute("enctype");
11093                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11094                     return this.doFormUpload(o, p, url);
11095                 }
11096                 var f = Roo.lib.Ajax.serializeForm(form);
11097                 p = p ? (p + '&' + f) : f;
11098             }
11099
11100             var hs = o.headers;
11101             if(this.defaultHeaders){
11102                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11103                 if(!o.headers){
11104                     o.headers = hs;
11105                 }
11106             }
11107
11108             var cb = {
11109                 success: this.handleResponse,
11110                 failure: this.handleFailure,
11111                 scope: this,
11112                 argument: {options: o},
11113                 timeout : this.timeout
11114             };
11115
11116             var method = o.method||this.method||(p ? "POST" : "GET");
11117
11118             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11119                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11120             }
11121
11122             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11123                 if(o.autoAbort){
11124                     this.abort();
11125                 }
11126             }else if(this.autoAbort !== false){
11127                 this.abort();
11128             }
11129
11130             if((method == 'GET' && p) || o.xmlData){
11131                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11132                 p = '';
11133             }
11134             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11135             return this.transId;
11136         }else{
11137             Roo.callback(o.callback, o.scope, [o, null, null]);
11138             return null;
11139         }
11140     },
11141
11142     /**
11143      * Determine whether this object has a request outstanding.
11144      * @param {Number} transactionId (Optional) defaults to the last transaction
11145      * @return {Boolean} True if there is an outstanding request.
11146      */
11147     isLoading : function(transId){
11148         if(transId){
11149             return Roo.lib.Ajax.isCallInProgress(transId);
11150         }else{
11151             return this.transId ? true : false;
11152         }
11153     },
11154
11155     /**
11156      * Aborts any outstanding request.
11157      * @param {Number} transactionId (Optional) defaults to the last transaction
11158      */
11159     abort : function(transId){
11160         if(transId || this.isLoading()){
11161             Roo.lib.Ajax.abort(transId || this.transId);
11162         }
11163     },
11164
11165     // private
11166     handleResponse : function(response){
11167         this.transId = false;
11168         var options = response.argument.options;
11169         response.argument = options ? options.argument : null;
11170         this.fireEvent("requestcomplete", this, response, options);
11171         Roo.callback(options.success, options.scope, [response, options]);
11172         Roo.callback(options.callback, options.scope, [options, true, response]);
11173     },
11174
11175     // private
11176     handleFailure : function(response, e){
11177         this.transId = false;
11178         var options = response.argument.options;
11179         response.argument = options ? options.argument : null;
11180         this.fireEvent("requestexception", this, response, options, e);
11181         Roo.callback(options.failure, options.scope, [response, options]);
11182         Roo.callback(options.callback, options.scope, [options, false, response]);
11183     },
11184
11185     // private
11186     doFormUpload : function(o, ps, url){
11187         var id = Roo.id();
11188         var frame = document.createElement('iframe');
11189         frame.id = id;
11190         frame.name = id;
11191         frame.className = 'x-hidden';
11192         if(Roo.isIE){
11193             frame.src = Roo.SSL_SECURE_URL;
11194         }
11195         document.body.appendChild(frame);
11196
11197         if(Roo.isIE){
11198            document.frames[id].name = id;
11199         }
11200
11201         var form = Roo.getDom(o.form);
11202         form.target = id;
11203         form.method = 'POST';
11204         form.enctype = form.encoding = 'multipart/form-data';
11205         if(url){
11206             form.action = url;
11207         }
11208
11209         var hiddens, hd;
11210         if(ps){ // add dynamic params
11211             hiddens = [];
11212             ps = Roo.urlDecode(ps, false);
11213             for(var k in ps){
11214                 if(ps.hasOwnProperty(k)){
11215                     hd = document.createElement('input');
11216                     hd.type = 'hidden';
11217                     hd.name = k;
11218                     hd.value = ps[k];
11219                     form.appendChild(hd);
11220                     hiddens.push(hd);
11221                 }
11222             }
11223         }
11224
11225         function cb(){
11226             var r = {  // bogus response object
11227                 responseText : '',
11228                 responseXML : null
11229             };
11230
11231             r.argument = o ? o.argument : null;
11232
11233             try { //
11234                 var doc;
11235                 if(Roo.isIE){
11236                     doc = frame.contentWindow.document;
11237                 }else {
11238                     doc = (frame.contentDocument || window.frames[id].document);
11239                 }
11240                 if(doc && doc.body){
11241                     r.responseText = doc.body.innerHTML;
11242                 }
11243                 if(doc && doc.XMLDocument){
11244                     r.responseXML = doc.XMLDocument;
11245                 }else {
11246                     r.responseXML = doc;
11247                 }
11248             }
11249             catch(e) {
11250                 // ignore
11251             }
11252
11253             Roo.EventManager.removeListener(frame, 'load', cb, this);
11254
11255             this.fireEvent("requestcomplete", this, r, o);
11256             Roo.callback(o.success, o.scope, [r, o]);
11257             Roo.callback(o.callback, o.scope, [o, true, r]);
11258
11259             setTimeout(function(){document.body.removeChild(frame);}, 100);
11260         }
11261
11262         Roo.EventManager.on(frame, 'load', cb, this);
11263         form.submit();
11264
11265         if(hiddens){ // remove dynamic params
11266             for(var i = 0, len = hiddens.length; i < len; i++){
11267                 form.removeChild(hiddens[i]);
11268             }
11269         }
11270     }
11271 });
11272
11273 /**
11274  * @class Roo.Ajax
11275  * @extends Roo.data.Connection
11276  * Global Ajax request class.
11277  *
11278  * @singleton
11279  */
11280 Roo.Ajax = new Roo.data.Connection({
11281     // fix up the docs
11282    /**
11283      * @cfg {String} url @hide
11284      */
11285     /**
11286      * @cfg {Object} extraParams @hide
11287      */
11288     /**
11289      * @cfg {Object} defaultHeaders @hide
11290      */
11291     /**
11292      * @cfg {String} method (Optional) @hide
11293      */
11294     /**
11295      * @cfg {Number} timeout (Optional) @hide
11296      */
11297     /**
11298      * @cfg {Boolean} autoAbort (Optional) @hide
11299      */
11300
11301     /**
11302      * @cfg {Boolean} disableCaching (Optional) @hide
11303      */
11304
11305     /**
11306      * @property  disableCaching
11307      * True to add a unique cache-buster param to GET requests. (defaults to true)
11308      * @type Boolean
11309      */
11310     /**
11311      * @property  url
11312      * The default URL to be used for requests to the server. (defaults to undefined)
11313      * @type String
11314      */
11315     /**
11316      * @property  extraParams
11317      * An object containing properties which are used as
11318      * extra parameters to each request made by this object. (defaults to undefined)
11319      * @type Object
11320      */
11321     /**
11322      * @property  defaultHeaders
11323      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11324      * @type Object
11325      */
11326     /**
11327      * @property  method
11328      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11329      * @type String
11330      */
11331     /**
11332      * @property  timeout
11333      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11334      * @type Number
11335      */
11336
11337     /**
11338      * @property  autoAbort
11339      * Whether a new request should abort any pending requests. (defaults to false)
11340      * @type Boolean
11341      */
11342     autoAbort : false,
11343
11344     /**
11345      * Serialize the passed form into a url encoded string
11346      * @param {String/HTMLElement} form
11347      * @return {String}
11348      */
11349     serializeForm : function(form){
11350         return Roo.lib.Ajax.serializeForm(form);
11351     }
11352 });/*
11353  * Based on:
11354  * Ext JS Library 1.1.1
11355  * Copyright(c) 2006-2007, Ext JS, LLC.
11356  *
11357  * Originally Released Under LGPL - original licence link has changed is not relivant.
11358  *
11359  * Fork - LGPL
11360  * <script type="text/javascript">
11361  */
11362  
11363 /**
11364  * @class Roo.Ajax
11365  * @extends Roo.data.Connection
11366  * Global Ajax request class.
11367  *
11368  * @instanceOf  Roo.data.Connection
11369  */
11370 Roo.Ajax = new Roo.data.Connection({
11371     // fix up the docs
11372     
11373     /**
11374      * fix up scoping
11375      * @scope Roo.Ajax
11376      */
11377     
11378    /**
11379      * @cfg {String} url @hide
11380      */
11381     /**
11382      * @cfg {Object} extraParams @hide
11383      */
11384     /**
11385      * @cfg {Object} defaultHeaders @hide
11386      */
11387     /**
11388      * @cfg {String} method (Optional) @hide
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) @hide
11392      */
11393     /**
11394      * @cfg {Boolean} autoAbort (Optional) @hide
11395      */
11396
11397     /**
11398      * @cfg {Boolean} disableCaching (Optional) @hide
11399      */
11400
11401     /**
11402      * @property  disableCaching
11403      * True to add a unique cache-buster param to GET requests. (defaults to true)
11404      * @type Boolean
11405      */
11406     /**
11407      * @property  url
11408      * The default URL to be used for requests to the server. (defaults to undefined)
11409      * @type String
11410      */
11411     /**
11412      * @property  extraParams
11413      * An object containing properties which are used as
11414      * extra parameters to each request made by this object. (defaults to undefined)
11415      * @type Object
11416      */
11417     /**
11418      * @property  defaultHeaders
11419      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11420      * @type Object
11421      */
11422     /**
11423      * @property  method
11424      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11425      * @type String
11426      */
11427     /**
11428      * @property  timeout
11429      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11430      * @type Number
11431      */
11432
11433     /**
11434      * @property  autoAbort
11435      * Whether a new request should abort any pending requests. (defaults to false)
11436      * @type Boolean
11437      */
11438     autoAbort : false,
11439
11440     /**
11441      * Serialize the passed form into a url encoded string
11442      * @param {String/HTMLElement} form
11443      * @return {String}
11444      */
11445     serializeForm : function(form){
11446         return Roo.lib.Ajax.serializeForm(form);
11447     }
11448 });/*
11449  * Based on:
11450  * Ext JS Library 1.1.1
11451  * Copyright(c) 2006-2007, Ext JS, LLC.
11452  *
11453  * Originally Released Under LGPL - original licence link has changed is not relivant.
11454  *
11455  * Fork - LGPL
11456  * <script type="text/javascript">
11457  */
11458
11459  
11460 /**
11461  * @class Roo.UpdateManager
11462  * @extends Roo.util.Observable
11463  * Provides AJAX-style update for Element object.<br><br>
11464  * Usage:<br>
11465  * <pre><code>
11466  * // Get it from a Roo.Element object
11467  * var el = Roo.get("foo");
11468  * var mgr = el.getUpdateManager();
11469  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11470  * ...
11471  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11472  * <br>
11473  * // or directly (returns the same UpdateManager instance)
11474  * var mgr = new Roo.UpdateManager("myElementId");
11475  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11476  * mgr.on("update", myFcnNeedsToKnow);
11477  * <br>
11478    // short handed call directly from the element object
11479    Roo.get("foo").load({
11480         url: "bar.php",
11481         scripts:true,
11482         params: "for=bar",
11483         text: "Loading Foo..."
11484    });
11485  * </code></pre>
11486  * @constructor
11487  * Create new UpdateManager directly.
11488  * @param {String/HTMLElement/Roo.Element} el The element to update
11489  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11490  */
11491 Roo.UpdateManager = function(el, forceNew){
11492     el = Roo.get(el);
11493     if(!forceNew && el.updateManager){
11494         return el.updateManager;
11495     }
11496     /**
11497      * The Element object
11498      * @type Roo.Element
11499      */
11500     this.el = el;
11501     /**
11502      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11503      * @type String
11504      */
11505     this.defaultUrl = null;
11506
11507     this.addEvents({
11508         /**
11509          * @event beforeupdate
11510          * Fired before an update is made, return false from your handler and the update is cancelled.
11511          * @param {Roo.Element} el
11512          * @param {String/Object/Function} url
11513          * @param {String/Object} params
11514          */
11515         "beforeupdate": true,
11516         /**
11517          * @event update
11518          * Fired after successful update is made.
11519          * @param {Roo.Element} el
11520          * @param {Object} oResponseObject The response Object
11521          */
11522         "update": true,
11523         /**
11524          * @event failure
11525          * Fired on update failure.
11526          * @param {Roo.Element} el
11527          * @param {Object} oResponseObject The response Object
11528          */
11529         "failure": true
11530     });
11531     var d = Roo.UpdateManager.defaults;
11532     /**
11533      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11534      * @type String
11535      */
11536     this.sslBlankUrl = d.sslBlankUrl;
11537     /**
11538      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11539      * @type Boolean
11540      */
11541     this.disableCaching = d.disableCaching;
11542     /**
11543      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11544      * @type String
11545      */
11546     this.indicatorText = d.indicatorText;
11547     /**
11548      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11549      * @type String
11550      */
11551     this.showLoadIndicator = d.showLoadIndicator;
11552     /**
11553      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11554      * @type Number
11555      */
11556     this.timeout = d.timeout;
11557
11558     /**
11559      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11560      * @type Boolean
11561      */
11562     this.loadScripts = d.loadScripts;
11563
11564     /**
11565      * Transaction object of current executing transaction
11566      */
11567     this.transaction = null;
11568
11569     /**
11570      * @private
11571      */
11572     this.autoRefreshProcId = null;
11573     /**
11574      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11575      * @type Function
11576      */
11577     this.refreshDelegate = this.refresh.createDelegate(this);
11578     /**
11579      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11580      * @type Function
11581      */
11582     this.updateDelegate = this.update.createDelegate(this);
11583     /**
11584      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11585      * @type Function
11586      */
11587     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11588     /**
11589      * @private
11590      */
11591     this.successDelegate = this.processSuccess.createDelegate(this);
11592     /**
11593      * @private
11594      */
11595     this.failureDelegate = this.processFailure.createDelegate(this);
11596
11597     if(!this.renderer){
11598      /**
11599       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11600       */
11601     this.renderer = new Roo.UpdateManager.BasicRenderer();
11602     }
11603     
11604     Roo.UpdateManager.superclass.constructor.call(this);
11605 };
11606
11607 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11608     /**
11609      * Get the Element this UpdateManager is bound to
11610      * @return {Roo.Element} The element
11611      */
11612     getEl : function(){
11613         return this.el;
11614     },
11615     /**
11616      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11617      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11618 <pre><code>
11619 um.update({<br/>
11620     url: "your-url.php",<br/>
11621     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11622     callback: yourFunction,<br/>
11623     scope: yourObject, //(optional scope)  <br/>
11624     discardUrl: false, <br/>
11625     nocache: false,<br/>
11626     text: "Loading...",<br/>
11627     timeout: 30,<br/>
11628     scripts: false<br/>
11629 });
11630 </code></pre>
11631      * The only required property is url. The optional properties nocache, text and scripts
11632      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11633      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11634      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11635      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11636      */
11637     update : function(url, params, callback, discardUrl){
11638         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11639             var method = this.method, cfg;
11640             if(typeof url == "object"){ // must be config object
11641                 cfg = url;
11642                 url = cfg.url;
11643                 params = params || cfg.params;
11644                 callback = callback || cfg.callback;
11645                 discardUrl = discardUrl || cfg.discardUrl;
11646                 if(callback && cfg.scope){
11647                     callback = callback.createDelegate(cfg.scope);
11648                 }
11649                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11650                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11651                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11652                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11653                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11654             }
11655             this.showLoading();
11656             if(!discardUrl){
11657                 this.defaultUrl = url;
11658             }
11659             if(typeof url == "function"){
11660                 url = url.call(this);
11661             }
11662
11663             method = method || (params ? "POST" : "GET");
11664             if(method == "GET"){
11665                 url = this.prepareUrl(url);
11666             }
11667
11668             var o = Roo.apply(cfg ||{}, {
11669                 url : url,
11670                 params: params,
11671                 success: this.successDelegate,
11672                 failure: this.failureDelegate,
11673                 callback: undefined,
11674                 timeout: (this.timeout*1000),
11675                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11676             });
11677
11678             this.transaction = Roo.Ajax.request(o);
11679         }
11680     },
11681
11682     /**
11683      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11684      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11685      * @param {String/HTMLElement} form The form Id or form element
11686      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11687      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11688      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11689      */
11690     formUpdate : function(form, url, reset, callback){
11691         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11692             if(typeof url == "function"){
11693                 url = url.call(this);
11694             }
11695             form = Roo.getDom(form);
11696             this.transaction = Roo.Ajax.request({
11697                 form: form,
11698                 url:url,
11699                 success: this.successDelegate,
11700                 failure: this.failureDelegate,
11701                 timeout: (this.timeout*1000),
11702                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11703             });
11704             this.showLoading.defer(1, this);
11705         }
11706     },
11707
11708     /**
11709      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11710      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11711      */
11712     refresh : function(callback){
11713         if(this.defaultUrl == null){
11714             return;
11715         }
11716         this.update(this.defaultUrl, null, callback, true);
11717     },
11718
11719     /**
11720      * Set this element to auto refresh.
11721      * @param {Number} interval How often to update (in seconds).
11722      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11723      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11724      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11725      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11726      */
11727     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11728         if(refreshNow){
11729             this.update(url || this.defaultUrl, params, callback, true);
11730         }
11731         if(this.autoRefreshProcId){
11732             clearInterval(this.autoRefreshProcId);
11733         }
11734         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11735     },
11736
11737     /**
11738      * Stop auto refresh on this element.
11739      */
11740      stopAutoRefresh : function(){
11741         if(this.autoRefreshProcId){
11742             clearInterval(this.autoRefreshProcId);
11743             delete this.autoRefreshProcId;
11744         }
11745     },
11746
11747     isAutoRefreshing : function(){
11748        return this.autoRefreshProcId ? true : false;
11749     },
11750     /**
11751      * Called to update the element to "Loading" state. Override to perform custom action.
11752      */
11753     showLoading : function(){
11754         if(this.showLoadIndicator){
11755             this.el.update(this.indicatorText);
11756         }
11757     },
11758
11759     /**
11760      * Adds unique parameter to query string if disableCaching = true
11761      * @private
11762      */
11763     prepareUrl : function(url){
11764         if(this.disableCaching){
11765             var append = "_dc=" + (new Date().getTime());
11766             if(url.indexOf("?") !== -1){
11767                 url += "&" + append;
11768             }else{
11769                 url += "?" + append;
11770             }
11771         }
11772         return url;
11773     },
11774
11775     /**
11776      * @private
11777      */
11778     processSuccess : function(response){
11779         this.transaction = null;
11780         if(response.argument.form && response.argument.reset){
11781             try{ // put in try/catch since some older FF releases had problems with this
11782                 response.argument.form.reset();
11783             }catch(e){}
11784         }
11785         if(this.loadScripts){
11786             this.renderer.render(this.el, response, this,
11787                 this.updateComplete.createDelegate(this, [response]));
11788         }else{
11789             this.renderer.render(this.el, response, this);
11790             this.updateComplete(response);
11791         }
11792     },
11793
11794     updateComplete : function(response){
11795         this.fireEvent("update", this.el, response);
11796         if(typeof response.argument.callback == "function"){
11797             response.argument.callback(this.el, true, response);
11798         }
11799     },
11800
11801     /**
11802      * @private
11803      */
11804     processFailure : function(response){
11805         this.transaction = null;
11806         this.fireEvent("failure", this.el, response);
11807         if(typeof response.argument.callback == "function"){
11808             response.argument.callback(this.el, false, response);
11809         }
11810     },
11811
11812     /**
11813      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11814      * @param {Object} renderer The object implementing the render() method
11815      */
11816     setRenderer : function(renderer){
11817         this.renderer = renderer;
11818     },
11819
11820     getRenderer : function(){
11821        return this.renderer;
11822     },
11823
11824     /**
11825      * Set the defaultUrl used for updates
11826      * @param {String/Function} defaultUrl The url or a function to call to get the url
11827      */
11828     setDefaultUrl : function(defaultUrl){
11829         this.defaultUrl = defaultUrl;
11830     },
11831
11832     /**
11833      * Aborts the executing transaction
11834      */
11835     abort : function(){
11836         if(this.transaction){
11837             Roo.Ajax.abort(this.transaction);
11838         }
11839     },
11840
11841     /**
11842      * Returns true if an update is in progress
11843      * @return {Boolean}
11844      */
11845     isUpdating : function(){
11846         if(this.transaction){
11847             return Roo.Ajax.isLoading(this.transaction);
11848         }
11849         return false;
11850     }
11851 });
11852
11853 /**
11854  * @class Roo.UpdateManager.defaults
11855  * @static (not really - but it helps the doc tool)
11856  * The defaults collection enables customizing the default properties of UpdateManager
11857  */
11858    Roo.UpdateManager.defaults = {
11859        /**
11860          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11861          * @type Number
11862          */
11863          timeout : 30,
11864
11865          /**
11866          * True to process scripts by default (Defaults to false).
11867          * @type Boolean
11868          */
11869         loadScripts : false,
11870
11871         /**
11872         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11873         * @type String
11874         */
11875         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11876         /**
11877          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11878          * @type Boolean
11879          */
11880         disableCaching : false,
11881         /**
11882          * Whether to show indicatorText when loading (Defaults to true).
11883          * @type Boolean
11884          */
11885         showLoadIndicator : true,
11886         /**
11887          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11888          * @type String
11889          */
11890         indicatorText : '<div class="loading-indicator">Loading...</div>'
11891    };
11892
11893 /**
11894  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11895  *Usage:
11896  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11897  * @param {String/HTMLElement/Roo.Element} el The element to update
11898  * @param {String} url The url
11899  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11900  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11901  * @static
11902  * @deprecated
11903  * @member Roo.UpdateManager
11904  */
11905 Roo.UpdateManager.updateElement = function(el, url, params, options){
11906     var um = Roo.get(el, true).getUpdateManager();
11907     Roo.apply(um, options);
11908     um.update(url, params, options ? options.callback : null);
11909 };
11910 // alias for backwards compat
11911 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11912 /**
11913  * @class Roo.UpdateManager.BasicRenderer
11914  * Default Content renderer. Updates the elements innerHTML with the responseText.
11915  */
11916 Roo.UpdateManager.BasicRenderer = function(){};
11917
11918 Roo.UpdateManager.BasicRenderer.prototype = {
11919     /**
11920      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11921      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11922      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11923      * @param {Roo.Element} el The element being rendered
11924      * @param {Object} response The YUI Connect response object
11925      * @param {UpdateManager} updateManager The calling update manager
11926      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
11927      */
11928      render : function(el, response, updateManager, callback){
11929         el.update(response.responseText, updateManager.loadScripts, callback);
11930     }
11931 };
11932 /*
11933  * Based on:
11934  * Ext JS Library 1.1.1
11935  * Copyright(c) 2006-2007, Ext JS, LLC.
11936  *
11937  * Originally Released Under LGPL - original licence link has changed is not relivant.
11938  *
11939  * Fork - LGPL
11940  * <script type="text/javascript">
11941  */
11942
11943 /**
11944  * @class Roo.util.DelayedTask
11945  * Provides a convenient method of performing setTimeout where a new
11946  * timeout cancels the old timeout. An example would be performing validation on a keypress.
11947  * You can use this class to buffer
11948  * the keypress events for a certain number of milliseconds, and perform only if they stop
11949  * for that amount of time.
11950  * @constructor The parameters to this constructor serve as defaults and are not required.
11951  * @param {Function} fn (optional) The default function to timeout
11952  * @param {Object} scope (optional) The default scope of that timeout
11953  * @param {Array} args (optional) The default Array of arguments
11954  */
11955 Roo.util.DelayedTask = function(fn, scope, args){
11956     var id = null, d, t;
11957
11958     var call = function(){
11959         var now = new Date().getTime();
11960         if(now - t >= d){
11961             clearInterval(id);
11962             id = null;
11963             fn.apply(scope, args || []);
11964         }
11965     };
11966     /**
11967      * Cancels any pending timeout and queues a new one
11968      * @param {Number} delay The milliseconds to delay
11969      * @param {Function} newFn (optional) Overrides function passed to constructor
11970      * @param {Object} newScope (optional) Overrides scope passed to constructor
11971      * @param {Array} newArgs (optional) Overrides args passed to constructor
11972      */
11973     this.delay = function(delay, newFn, newScope, newArgs){
11974         if(id && delay != d){
11975             this.cancel();
11976         }
11977         d = delay;
11978         t = new Date().getTime();
11979         fn = newFn || fn;
11980         scope = newScope || scope;
11981         args = newArgs || args;
11982         if(!id){
11983             id = setInterval(call, d);
11984         }
11985     };
11986
11987     /**
11988      * Cancel the last queued timeout
11989      */
11990     this.cancel = function(){
11991         if(id){
11992             clearInterval(id);
11993             id = null;
11994         }
11995     };
11996 };/*
11997  * Based on:
11998  * Ext JS Library 1.1.1
11999  * Copyright(c) 2006-2007, Ext JS, LLC.
12000  *
12001  * Originally Released Under LGPL - original licence link has changed is not relivant.
12002  *
12003  * Fork - LGPL
12004  * <script type="text/javascript">
12005  */
12006  
12007  
12008 Roo.util.TaskRunner = function(interval){
12009     interval = interval || 10;
12010     var tasks = [], removeQueue = [];
12011     var id = 0;
12012     var running = false;
12013
12014     var stopThread = function(){
12015         running = false;
12016         clearInterval(id);
12017         id = 0;
12018     };
12019
12020     var startThread = function(){
12021         if(!running){
12022             running = true;
12023             id = setInterval(runTasks, interval);
12024         }
12025     };
12026
12027     var removeTask = function(task){
12028         removeQueue.push(task);
12029         if(task.onStop){
12030             task.onStop();
12031         }
12032     };
12033
12034     var runTasks = function(){
12035         if(removeQueue.length > 0){
12036             for(var i = 0, len = removeQueue.length; i < len; i++){
12037                 tasks.remove(removeQueue[i]);
12038             }
12039             removeQueue = [];
12040             if(tasks.length < 1){
12041                 stopThread();
12042                 return;
12043             }
12044         }
12045         var now = new Date().getTime();
12046         for(var i = 0, len = tasks.length; i < len; ++i){
12047             var t = tasks[i];
12048             var itime = now - t.taskRunTime;
12049             if(t.interval <= itime){
12050                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12051                 t.taskRunTime = now;
12052                 if(rt === false || t.taskRunCount === t.repeat){
12053                     removeTask(t);
12054                     return;
12055                 }
12056             }
12057             if(t.duration && t.duration <= (now - t.taskStartTime)){
12058                 removeTask(t);
12059             }
12060         }
12061     };
12062
12063     /**
12064      * Queues a new task.
12065      * @param {Object} task
12066      */
12067     this.start = function(task){
12068         tasks.push(task);
12069         task.taskStartTime = new Date().getTime();
12070         task.taskRunTime = 0;
12071         task.taskRunCount = 0;
12072         startThread();
12073         return task;
12074     };
12075
12076     this.stop = function(task){
12077         removeTask(task);
12078         return task;
12079     };
12080
12081     this.stopAll = function(){
12082         stopThread();
12083         for(var i = 0, len = tasks.length; i < len; i++){
12084             if(tasks[i].onStop){
12085                 tasks[i].onStop();
12086             }
12087         }
12088         tasks = [];
12089         removeQueue = [];
12090     };
12091 };
12092
12093 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12094  * Based on:
12095  * Ext JS Library 1.1.1
12096  * Copyright(c) 2006-2007, Ext JS, LLC.
12097  *
12098  * Originally Released Under LGPL - original licence link has changed is not relivant.
12099  *
12100  * Fork - LGPL
12101  * <script type="text/javascript">
12102  */
12103
12104  
12105 /**
12106  * @class Roo.util.MixedCollection
12107  * @extends Roo.util.Observable
12108  * A Collection class that maintains both numeric indexes and keys and exposes events.
12109  * @constructor
12110  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12111  * collection (defaults to false)
12112  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12113  * and return the key value for that item.  This is used when available to look up the key on items that
12114  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12115  * equivalent to providing an implementation for the {@link #getKey} method.
12116  */
12117 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12118     this.items = [];
12119     this.map = {};
12120     this.keys = [];
12121     this.length = 0;
12122     this.addEvents({
12123         /**
12124          * @event clear
12125          * Fires when the collection is cleared.
12126          */
12127         "clear" : true,
12128         /**
12129          * @event add
12130          * Fires when an item is added to the collection.
12131          * @param {Number} index The index at which the item was added.
12132          * @param {Object} o The item added.
12133          * @param {String} key The key associated with the added item.
12134          */
12135         "add" : true,
12136         /**
12137          * @event replace
12138          * Fires when an item is replaced in the collection.
12139          * @param {String} key he key associated with the new added.
12140          * @param {Object} old The item being replaced.
12141          * @param {Object} new The new item.
12142          */
12143         "replace" : true,
12144         /**
12145          * @event remove
12146          * Fires when an item is removed from the collection.
12147          * @param {Object} o The item being removed.
12148          * @param {String} key (optional) The key associated with the removed item.
12149          */
12150         "remove" : true,
12151         "sort" : true
12152     });
12153     this.allowFunctions = allowFunctions === true;
12154     if(keyFn){
12155         this.getKey = keyFn;
12156     }
12157     Roo.util.MixedCollection.superclass.constructor.call(this);
12158 };
12159
12160 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12161     allowFunctions : false,
12162     
12163 /**
12164  * Adds an item to the collection.
12165  * @param {String} key The key to associate with the item
12166  * @param {Object} o The item to add.
12167  * @return {Object} The item added.
12168  */
12169     add : function(key, o){
12170         if(arguments.length == 1){
12171             o = arguments[0];
12172             key = this.getKey(o);
12173         }
12174         if(typeof key == "undefined" || key === null){
12175             this.length++;
12176             this.items.push(o);
12177             this.keys.push(null);
12178         }else{
12179             var old = this.map[key];
12180             if(old){
12181                 return this.replace(key, o);
12182             }
12183             this.length++;
12184             this.items.push(o);
12185             this.map[key] = o;
12186             this.keys.push(key);
12187         }
12188         this.fireEvent("add", this.length-1, o, key);
12189         return o;
12190     },
12191    
12192 /**
12193   * MixedCollection has a generic way to fetch keys if you implement getKey.
12194 <pre><code>
12195 // normal way
12196 var mc = new Roo.util.MixedCollection();
12197 mc.add(someEl.dom.id, someEl);
12198 mc.add(otherEl.dom.id, otherEl);
12199 //and so on
12200
12201 // using getKey
12202 var mc = new Roo.util.MixedCollection();
12203 mc.getKey = function(el){
12204    return el.dom.id;
12205 };
12206 mc.add(someEl);
12207 mc.add(otherEl);
12208
12209 // or via the constructor
12210 var mc = new Roo.util.MixedCollection(false, function(el){
12211    return el.dom.id;
12212 });
12213 mc.add(someEl);
12214 mc.add(otherEl);
12215 </code></pre>
12216  * @param o {Object} The item for which to find the key.
12217  * @return {Object} The key for the passed item.
12218  */
12219     getKey : function(o){
12220          return o.id; 
12221     },
12222    
12223 /**
12224  * Replaces an item in the collection.
12225  * @param {String} key The key associated with the item to replace, or the item to replace.
12226  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12227  * @return {Object}  The new item.
12228  */
12229     replace : function(key, o){
12230         if(arguments.length == 1){
12231             o = arguments[0];
12232             key = this.getKey(o);
12233         }
12234         var old = this.item(key);
12235         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12236              return this.add(key, o);
12237         }
12238         var index = this.indexOfKey(key);
12239         this.items[index] = o;
12240         this.map[key] = o;
12241         this.fireEvent("replace", key, old, o);
12242         return o;
12243     },
12244    
12245 /**
12246  * Adds all elements of an Array or an Object to the collection.
12247  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12248  * an Array of values, each of which are added to the collection.
12249  */
12250     addAll : function(objs){
12251         if(arguments.length > 1 || objs instanceof Array){
12252             var args = arguments.length > 1 ? arguments : objs;
12253             for(var i = 0, len = args.length; i < len; i++){
12254                 this.add(args[i]);
12255             }
12256         }else{
12257             for(var key in objs){
12258                 if(this.allowFunctions || typeof objs[key] != "function"){
12259                     this.add(key, objs[key]);
12260                 }
12261             }
12262         }
12263     },
12264    
12265 /**
12266  * Executes the specified function once for every item in the collection, passing each
12267  * item as the first and only parameter. returning false from the function will stop the iteration.
12268  * @param {Function} fn The function to execute for each item.
12269  * @param {Object} scope (optional) The scope in which to execute the function.
12270  */
12271     each : function(fn, scope){
12272         var items = [].concat(this.items); // each safe for removal
12273         for(var i = 0, len = items.length; i < len; i++){
12274             if(fn.call(scope || items[i], items[i], i, len) === false){
12275                 break;
12276             }
12277         }
12278     },
12279    
12280 /**
12281  * Executes the specified function once for every key in the collection, passing each
12282  * key, and its associated item as the first two parameters.
12283  * @param {Function} fn The function to execute for each item.
12284  * @param {Object} scope (optional) The scope in which to execute the function.
12285  */
12286     eachKey : function(fn, scope){
12287         for(var i = 0, len = this.keys.length; i < len; i++){
12288             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12289         }
12290     },
12291    
12292 /**
12293  * Returns the first item in the collection which elicits a true return value from the
12294  * passed selection function.
12295  * @param {Function} fn The selection function to execute for each item.
12296  * @param {Object} scope (optional) The scope in which to execute the function.
12297  * @return {Object} The first item in the collection which returned true from the selection function.
12298  */
12299     find : function(fn, scope){
12300         for(var i = 0, len = this.items.length; i < len; i++){
12301             if(fn.call(scope || window, this.items[i], this.keys[i])){
12302                 return this.items[i];
12303             }
12304         }
12305         return null;
12306     },
12307    
12308 /**
12309  * Inserts an item at the specified index in the collection.
12310  * @param {Number} index The index to insert the item at.
12311  * @param {String} key The key to associate with the new item, or the item itself.
12312  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12313  * @return {Object} The item inserted.
12314  */
12315     insert : function(index, key, o){
12316         if(arguments.length == 2){
12317             o = arguments[1];
12318             key = this.getKey(o);
12319         }
12320         if(index >= this.length){
12321             return this.add(key, o);
12322         }
12323         this.length++;
12324         this.items.splice(index, 0, o);
12325         if(typeof key != "undefined" && key != null){
12326             this.map[key] = o;
12327         }
12328         this.keys.splice(index, 0, key);
12329         this.fireEvent("add", index, o, key);
12330         return o;
12331     },
12332    
12333 /**
12334  * Removed an item from the collection.
12335  * @param {Object} o The item to remove.
12336  * @return {Object} The item removed.
12337  */
12338     remove : function(o){
12339         return this.removeAt(this.indexOf(o));
12340     },
12341    
12342 /**
12343  * Remove an item from a specified index in the collection.
12344  * @param {Number} index The index within the collection of the item to remove.
12345  */
12346     removeAt : function(index){
12347         if(index < this.length && index >= 0){
12348             this.length--;
12349             var o = this.items[index];
12350             this.items.splice(index, 1);
12351             var key = this.keys[index];
12352             if(typeof key != "undefined"){
12353                 delete this.map[key];
12354             }
12355             this.keys.splice(index, 1);
12356             this.fireEvent("remove", o, key);
12357         }
12358     },
12359    
12360 /**
12361  * Removed an item associated with the passed key fom the collection.
12362  * @param {String} key The key of the item to remove.
12363  */
12364     removeKey : function(key){
12365         return this.removeAt(this.indexOfKey(key));
12366     },
12367    
12368 /**
12369  * Returns the number of items in the collection.
12370  * @return {Number} the number of items in the collection.
12371  */
12372     getCount : function(){
12373         return this.length; 
12374     },
12375    
12376 /**
12377  * Returns index within the collection of the passed Object.
12378  * @param {Object} o The item to find the index of.
12379  * @return {Number} index of the item.
12380  */
12381     indexOf : function(o){
12382         if(!this.items.indexOf){
12383             for(var i = 0, len = this.items.length; i < len; i++){
12384                 if(this.items[i] == o) return i;
12385             }
12386             return -1;
12387         }else{
12388             return this.items.indexOf(o);
12389         }
12390     },
12391    
12392 /**
12393  * Returns index within the collection of the passed key.
12394  * @param {String} key The key to find the index of.
12395  * @return {Number} index of the key.
12396  */
12397     indexOfKey : function(key){
12398         if(!this.keys.indexOf){
12399             for(var i = 0, len = this.keys.length; i < len; i++){
12400                 if(this.keys[i] == key) return i;
12401             }
12402             return -1;
12403         }else{
12404             return this.keys.indexOf(key);
12405         }
12406     },
12407    
12408 /**
12409  * Returns the item associated with the passed key OR index. Key has priority over index.
12410  * @param {String/Number} key The key or index of the item.
12411  * @return {Object} The item associated with the passed key.
12412  */
12413     item : function(key){
12414         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12415         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12416     },
12417     
12418 /**
12419  * Returns the item at the specified index.
12420  * @param {Number} index The index of the item.
12421  * @return {Object}
12422  */
12423     itemAt : function(index){
12424         return this.items[index];
12425     },
12426     
12427 /**
12428  * Returns the item associated with the passed key.
12429  * @param {String/Number} key The key of the item.
12430  * @return {Object} The item associated with the passed key.
12431  */
12432     key : function(key){
12433         return this.map[key];
12434     },
12435    
12436 /**
12437  * Returns true if the collection contains the passed Object as an item.
12438  * @param {Object} o  The Object to look for in the collection.
12439  * @return {Boolean} True if the collection contains the Object as an item.
12440  */
12441     contains : function(o){
12442         return this.indexOf(o) != -1;
12443     },
12444    
12445 /**
12446  * Returns true if the collection contains the passed Object as a key.
12447  * @param {String} key The key to look for in the collection.
12448  * @return {Boolean} True if the collection contains the Object as a key.
12449  */
12450     containsKey : function(key){
12451         return typeof this.map[key] != "undefined";
12452     },
12453    
12454 /**
12455  * Removes all items from the collection.
12456  */
12457     clear : function(){
12458         this.length = 0;
12459         this.items = [];
12460         this.keys = [];
12461         this.map = {};
12462         this.fireEvent("clear");
12463     },
12464    
12465 /**
12466  * Returns the first item in the collection.
12467  * @return {Object} the first item in the collection..
12468  */
12469     first : function(){
12470         return this.items[0]; 
12471     },
12472    
12473 /**
12474  * Returns the last item in the collection.
12475  * @return {Object} the last item in the collection..
12476  */
12477     last : function(){
12478         return this.items[this.length-1];   
12479     },
12480     
12481     _sort : function(property, dir, fn){
12482         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12483         fn = fn || function(a, b){
12484             return a-b;
12485         };
12486         var c = [], k = this.keys, items = this.items;
12487         for(var i = 0, len = items.length; i < len; i++){
12488             c[c.length] = {key: k[i], value: items[i], index: i};
12489         }
12490         c.sort(function(a, b){
12491             var v = fn(a[property], b[property]) * dsc;
12492             if(v == 0){
12493                 v = (a.index < b.index ? -1 : 1);
12494             }
12495             return v;
12496         });
12497         for(var i = 0, len = c.length; i < len; i++){
12498             items[i] = c[i].value;
12499             k[i] = c[i].key;
12500         }
12501         this.fireEvent("sort", this);
12502     },
12503     
12504     /**
12505      * Sorts this collection with the passed comparison function
12506      * @param {String} direction (optional) "ASC" or "DESC"
12507      * @param {Function} fn (optional) comparison function
12508      */
12509     sort : function(dir, fn){
12510         this._sort("value", dir, fn);
12511     },
12512     
12513     /**
12514      * Sorts this collection by keys
12515      * @param {String} direction (optional) "ASC" or "DESC"
12516      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12517      */
12518     keySort : function(dir, fn){
12519         this._sort("key", dir, fn || function(a, b){
12520             return String(a).toUpperCase()-String(b).toUpperCase();
12521         });
12522     },
12523     
12524     /**
12525      * Returns a range of items in this collection
12526      * @param {Number} startIndex (optional) defaults to 0
12527      * @param {Number} endIndex (optional) default to the last item
12528      * @return {Array} An array of items
12529      */
12530     getRange : function(start, end){
12531         var items = this.items;
12532         if(items.length < 1){
12533             return [];
12534         }
12535         start = start || 0;
12536         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12537         var r = [];
12538         if(start <= end){
12539             for(var i = start; i <= end; i++) {
12540                     r[r.length] = items[i];
12541             }
12542         }else{
12543             for(var i = start; i >= end; i--) {
12544                     r[r.length] = items[i];
12545             }
12546         }
12547         return r;
12548     },
12549         
12550     /**
12551      * Filter the <i>objects</i> in this collection by a specific property. 
12552      * Returns a new collection that has been filtered.
12553      * @param {String} property A property on your objects
12554      * @param {String/RegExp} value Either string that the property values 
12555      * should start with or a RegExp to test against the property
12556      * @return {MixedCollection} The new filtered collection
12557      */
12558     filter : function(property, value){
12559         if(!value.exec){ // not a regex
12560             value = String(value);
12561             if(value.length == 0){
12562                 return this.clone();
12563             }
12564             value = new RegExp("^" + Roo.escapeRe(value), "i");
12565         }
12566         return this.filterBy(function(o){
12567             return o && value.test(o[property]);
12568         });
12569         },
12570     
12571     /**
12572      * Filter by a function. * Returns a new collection that has been filtered.
12573      * The passed function will be called with each 
12574      * object in the collection. If the function returns true, the value is included 
12575      * otherwise it is filtered.
12576      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12577      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12578      * @return {MixedCollection} The new filtered collection
12579      */
12580     filterBy : function(fn, scope){
12581         var r = new Roo.util.MixedCollection();
12582         r.getKey = this.getKey;
12583         var k = this.keys, it = this.items;
12584         for(var i = 0, len = it.length; i < len; i++){
12585             if(fn.call(scope||this, it[i], k[i])){
12586                                 r.add(k[i], it[i]);
12587                         }
12588         }
12589         return r;
12590     },
12591     
12592     /**
12593      * Creates a duplicate of this collection
12594      * @return {MixedCollection}
12595      */
12596     clone : function(){
12597         var r = new Roo.util.MixedCollection();
12598         var k = this.keys, it = this.items;
12599         for(var i = 0, len = it.length; i < len; i++){
12600             r.add(k[i], it[i]);
12601         }
12602         r.getKey = this.getKey;
12603         return r;
12604     }
12605 });
12606 /**
12607  * Returns the item associated with the passed key or index.
12608  * @method
12609  * @param {String/Number} key The key or index of the item.
12610  * @return {Object} The item associated with the passed key.
12611  */
12612 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12613  * Based on:
12614  * Ext JS Library 1.1.1
12615  * Copyright(c) 2006-2007, Ext JS, LLC.
12616  *
12617  * Originally Released Under LGPL - original licence link has changed is not relivant.
12618  *
12619  * Fork - LGPL
12620  * <script type="text/javascript">
12621  */
12622 /**
12623  * @class Roo.util.JSON
12624  * Modified version of Douglas Crockford"s json.js that doesn"t
12625  * mess with the Object prototype 
12626  * http://www.json.org/js.html
12627  * @singleton
12628  */
12629 Roo.util.JSON = new (function(){
12630     var useHasOwn = {}.hasOwnProperty ? true : false;
12631     
12632     // crashes Safari in some instances
12633     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12634     
12635     var pad = function(n) {
12636         return n < 10 ? "0" + n : n;
12637     };
12638     
12639     var m = {
12640         "\b": '\\b',
12641         "\t": '\\t',
12642         "\n": '\\n',
12643         "\f": '\\f',
12644         "\r": '\\r',
12645         '"' : '\\"',
12646         "\\": '\\\\'
12647     };
12648
12649     var encodeString = function(s){
12650         if (/["\\\x00-\x1f]/.test(s)) {
12651             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12652                 var c = m[b];
12653                 if(c){
12654                     return c;
12655                 }
12656                 c = b.charCodeAt();
12657                 return "\\u00" +
12658                     Math.floor(c / 16).toString(16) +
12659                     (c % 16).toString(16);
12660             }) + '"';
12661         }
12662         return '"' + s + '"';
12663     };
12664     
12665     var encodeArray = function(o){
12666         var a = ["["], b, i, l = o.length, v;
12667             for (i = 0; i < l; i += 1) {
12668                 v = o[i];
12669                 switch (typeof v) {
12670                     case "undefined":
12671                     case "function":
12672                     case "unknown":
12673                         break;
12674                     default:
12675                         if (b) {
12676                             a.push(',');
12677                         }
12678                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12679                         b = true;
12680                 }
12681             }
12682             a.push("]");
12683             return a.join("");
12684     };
12685     
12686     var encodeDate = function(o){
12687         return '"' + o.getFullYear() + "-" +
12688                 pad(o.getMonth() + 1) + "-" +
12689                 pad(o.getDate()) + "T" +
12690                 pad(o.getHours()) + ":" +
12691                 pad(o.getMinutes()) + ":" +
12692                 pad(o.getSeconds()) + '"';
12693     };
12694     
12695     /**
12696      * Encodes an Object, Array or other value
12697      * @param {Mixed} o The variable to encode
12698      * @return {String} The JSON string
12699      */
12700     this.encode = function(o){
12701         if(typeof o == "undefined" || o === null){
12702             return "null";
12703         }else if(o instanceof Array){
12704             return encodeArray(o);
12705         }else if(o instanceof Date){
12706             return encodeDate(o);
12707         }else if(typeof o == "string"){
12708             return encodeString(o);
12709         }else if(typeof o == "number"){
12710             return isFinite(o) ? String(o) : "null";
12711         }else if(typeof o == "boolean"){
12712             return String(o);
12713         }else {
12714             var a = ["{"], b, i, v;
12715             for (i in o) {
12716                 if(!useHasOwn || o.hasOwnProperty(i)) {
12717                     v = o[i];
12718                     switch (typeof v) {
12719                     case "undefined":
12720                     case "function":
12721                     case "unknown":
12722                         break;
12723                     default:
12724                         if(b){
12725                             a.push(',');
12726                         }
12727                         a.push(this.encode(i), ":",
12728                                 v === null ? "null" : this.encode(v));
12729                         b = true;
12730                     }
12731                 }
12732             }
12733             a.push("}");
12734             return a.join("");
12735         }
12736     };
12737     
12738     /**
12739      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12740      * @param {String} json The JSON string
12741      * @return {Object} The resulting object
12742      */
12743     this.decode = function(json){
12744         /**
12745          * eval:var:json
12746          */
12747         return eval("(" + json + ')');
12748     };
12749 })();
12750 /** 
12751  * Shorthand for {@link Roo.util.JSON#encode}
12752  * @member Roo encode 
12753  * @method */
12754 Roo.encode = Roo.util.JSON.encode;
12755 /** 
12756  * Shorthand for {@link Roo.util.JSON#decode}
12757  * @member Roo decode 
12758  * @method */
12759 Roo.decode = Roo.util.JSON.decode;
12760 /*
12761  * Based on:
12762  * Ext JS Library 1.1.1
12763  * Copyright(c) 2006-2007, Ext JS, LLC.
12764  *
12765  * Originally Released Under LGPL - original licence link has changed is not relivant.
12766  *
12767  * Fork - LGPL
12768  * <script type="text/javascript">
12769  */
12770  
12771 /**
12772  * @class Roo.util.Format
12773  * Reusable data formatting functions
12774  * @singleton
12775  */
12776 Roo.util.Format = function(){
12777     var trimRe = /^\s+|\s+$/g;
12778     return {
12779         /**
12780          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12781          * @param {String} value The string to truncate
12782          * @param {Number} length The maximum length to allow before truncating
12783          * @return {String} The converted text
12784          */
12785         ellipsis : function(value, len){
12786             if(value && value.length > len){
12787                 return value.substr(0, len-3)+"...";
12788             }
12789             return value;
12790         },
12791
12792         /**
12793          * Checks a reference and converts it to empty string if it is undefined
12794          * @param {Mixed} value Reference to check
12795          * @return {Mixed} Empty string if converted, otherwise the original value
12796          */
12797         undef : function(value){
12798             return typeof value != "undefined" ? value : "";
12799         },
12800
12801         /**
12802          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12803          * @param {String} value The string to encode
12804          * @return {String} The encoded text
12805          */
12806         htmlEncode : function(value){
12807             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12808         },
12809
12810         /**
12811          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12812          * @param {String} value The string to decode
12813          * @return {String} The decoded text
12814          */
12815         htmlDecode : function(value){
12816             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12817         },
12818
12819         /**
12820          * Trims any whitespace from either side of a string
12821          * @param {String} value The text to trim
12822          * @return {String} The trimmed text
12823          */
12824         trim : function(value){
12825             return String(value).replace(trimRe, "");
12826         },
12827
12828         /**
12829          * Returns a substring from within an original string
12830          * @param {String} value The original text
12831          * @param {Number} start The start index of the substring
12832          * @param {Number} length The length of the substring
12833          * @return {String} The substring
12834          */
12835         substr : function(value, start, length){
12836             return String(value).substr(start, length);
12837         },
12838
12839         /**
12840          * Converts a string to all lower case letters
12841          * @param {String} value The text to convert
12842          * @return {String} The converted text
12843          */
12844         lowercase : function(value){
12845             return String(value).toLowerCase();
12846         },
12847
12848         /**
12849          * Converts a string to all upper case letters
12850          * @param {String} value The text to convert
12851          * @return {String} The converted text
12852          */
12853         uppercase : function(value){
12854             return String(value).toUpperCase();
12855         },
12856
12857         /**
12858          * Converts the first character only of a string to upper case
12859          * @param {String} value The text to convert
12860          * @return {String} The converted text
12861          */
12862         capitalize : function(value){
12863             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12864         },
12865
12866         // private
12867         call : function(value, fn){
12868             if(arguments.length > 2){
12869                 var args = Array.prototype.slice.call(arguments, 2);
12870                 args.unshift(value);
12871                  
12872                 return /** eval:var:value */  eval(fn).apply(window, args);
12873             }else{
12874                 /** eval:var:value */
12875                 return /** eval:var:value */ eval(fn).call(window, value);
12876             }
12877         },
12878
12879         /**
12880          * Format a number as US currency
12881          * @param {Number/String} value The numeric value to format
12882          * @return {String} The formatted currency string
12883          */
12884         usMoney : function(v){
12885             v = (Math.round((v-0)*100))/100;
12886             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12887             v = String(v);
12888             var ps = v.split('.');
12889             var whole = ps[0];
12890             var sub = ps[1] ? '.'+ ps[1] : '.00';
12891             var r = /(\d+)(\d{3})/;
12892             while (r.test(whole)) {
12893                 whole = whole.replace(r, '$1' + ',' + '$2');
12894             }
12895             return "$" + whole + sub ;
12896         },
12897
12898         /**
12899          * Parse a value into a formatted date using the specified format pattern.
12900          * @param {Mixed} value The value to format
12901          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12902          * @return {String} The formatted date string
12903          */
12904         date : function(v, format){
12905             if(!v){
12906                 return "";
12907             }
12908             if(!(v instanceof Date)){
12909                 v = new Date(Date.parse(v));
12910             }
12911             return v.dateFormat(format || "m/d/Y");
12912         },
12913
12914         /**
12915          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12916          * @param {String} format Any valid date format string
12917          * @return {Function} The date formatting function
12918          */
12919         dateRenderer : function(format){
12920             return function(v){
12921                 return Roo.util.Format.date(v, format);  
12922             };
12923         },
12924
12925         // private
12926         stripTagsRE : /<\/?[^>]+>/gi,
12927         
12928         /**
12929          * Strips all HTML tags
12930          * @param {Mixed} value The text from which to strip tags
12931          * @return {String} The stripped text
12932          */
12933         stripTags : function(v){
12934             return !v ? v : String(v).replace(this.stripTagsRE, "");
12935         }
12936     };
12937 }();/*
12938  * Based on:
12939  * Ext JS Library 1.1.1
12940  * Copyright(c) 2006-2007, Ext JS, LLC.
12941  *
12942  * Originally Released Under LGPL - original licence link has changed is not relivant.
12943  *
12944  * Fork - LGPL
12945  * <script type="text/javascript">
12946  */
12947
12948
12949  
12950
12951 /**
12952  * @class Roo.MasterTemplate
12953  * @extends Roo.Template
12954  * Provides a template that can have child templates. The syntax is:
12955 <pre><code>
12956 var t = new Roo.MasterTemplate(
12957         '&lt;select name="{name}"&gt;',
12958                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
12959         '&lt;/select&gt;'
12960 );
12961 t.add('options', {value: 'foo', text: 'bar'});
12962 // or you can add multiple child elements in one shot
12963 t.addAll('options', [
12964     {value: 'foo', text: 'bar'},
12965     {value: 'foo2', text: 'bar2'},
12966     {value: 'foo3', text: 'bar3'}
12967 ]);
12968 // then append, applying the master template values
12969 t.append('my-form', {name: 'my-select'});
12970 </code></pre>
12971 * A name attribute for the child template is not required if you have only one child
12972 * template or you want to refer to them by index.
12973  */
12974 Roo.MasterTemplate = function(){
12975     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
12976     this.originalHtml = this.html;
12977     var st = {};
12978     var m, re = this.subTemplateRe;
12979     re.lastIndex = 0;
12980     var subIndex = 0;
12981     while(m = re.exec(this.html)){
12982         var name = m[1], content = m[2];
12983         st[subIndex] = {
12984             name: name,
12985             index: subIndex,
12986             buffer: [],
12987             tpl : new Roo.Template(content)
12988         };
12989         if(name){
12990             st[name] = st[subIndex];
12991         }
12992         st[subIndex].tpl.compile();
12993         st[subIndex].tpl.call = this.call.createDelegate(this);
12994         subIndex++;
12995     }
12996     this.subCount = subIndex;
12997     this.subs = st;
12998 };
12999 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13000     /**
13001     * The regular expression used to match sub templates
13002     * @type RegExp
13003     * @property
13004     */
13005     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13006
13007     /**
13008      * Applies the passed values to a child template.
13009      * @param {String/Number} name (optional) The name or index of the child template
13010      * @param {Array/Object} values The values to be applied to the template
13011      * @return {MasterTemplate} this
13012      */
13013      add : function(name, values){
13014         if(arguments.length == 1){
13015             values = arguments[0];
13016             name = 0;
13017         }
13018         var s = this.subs[name];
13019         s.buffer[s.buffer.length] = s.tpl.apply(values);
13020         return this;
13021     },
13022
13023     /**
13024      * Applies all the passed values to a child template.
13025      * @param {String/Number} name (optional) The name or index of the child template
13026      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13027      * @param {Boolean} reset (optional) True to reset the template first
13028      * @return {MasterTemplate} this
13029      */
13030     fill : function(name, values, reset){
13031         var a = arguments;
13032         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13033             values = a[0];
13034             name = 0;
13035             reset = a[1];
13036         }
13037         if(reset){
13038             this.reset();
13039         }
13040         for(var i = 0, len = values.length; i < len; i++){
13041             this.add(name, values[i]);
13042         }
13043         return this;
13044     },
13045
13046     /**
13047      * Resets the template for reuse
13048      * @return {MasterTemplate} this
13049      */
13050      reset : function(){
13051         var s = this.subs;
13052         for(var i = 0; i < this.subCount; i++){
13053             s[i].buffer = [];
13054         }
13055         return this;
13056     },
13057
13058     applyTemplate : function(values){
13059         var s = this.subs;
13060         var replaceIndex = -1;
13061         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13062             return s[++replaceIndex].buffer.join("");
13063         });
13064         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13065     },
13066
13067     apply : function(){
13068         return this.applyTemplate.apply(this, arguments);
13069     },
13070
13071     compile : function(){return this;}
13072 });
13073
13074 /**
13075  * Alias for fill().
13076  * @method
13077  */
13078 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13079  /**
13080  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13081  * var tpl = Roo.MasterTemplate.from('element-id');
13082  * @param {String/HTMLElement} el
13083  * @param {Object} config
13084  * @static
13085  */
13086 Roo.MasterTemplate.from = function(el, config){
13087     el = Roo.getDom(el);
13088     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13089 };/*
13090  * Based on:
13091  * Ext JS Library 1.1.1
13092  * Copyright(c) 2006-2007, Ext JS, LLC.
13093  *
13094  * Originally Released Under LGPL - original licence link has changed is not relivant.
13095  *
13096  * Fork - LGPL
13097  * <script type="text/javascript">
13098  */
13099
13100  
13101 /**
13102  * @class Roo.util.CSS
13103  * Utility class for manipulating CSS rules
13104  * @singleton
13105  */
13106 Roo.util.CSS = function(){
13107         var rules = null;
13108         var doc = document;
13109
13110     var camelRe = /(-[a-z])/gi;
13111     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13112
13113    return {
13114    /**
13115     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13116     * tag and appended to the HEAD of the document.
13117     * @param {String} cssText The text containing the css rules
13118     * @param {String} id An id to add to the stylesheet for later removal
13119     * @return {StyleSheet}
13120     */
13121    createStyleSheet : function(cssText, id){
13122        var ss;
13123        var head = doc.getElementsByTagName("head")[0];
13124        var rules = doc.createElement("style");
13125        rules.setAttribute("type", "text/css");
13126        if(id){
13127            rules.setAttribute("id", id);
13128        }
13129        if(Roo.isIE){
13130            head.appendChild(rules);
13131            ss = rules.styleSheet;
13132            ss.cssText = cssText;
13133        }else{
13134            try{
13135                 rules.appendChild(doc.createTextNode(cssText));
13136            }catch(e){
13137                rules.cssText = cssText; 
13138            }
13139            head.appendChild(rules);
13140            ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13141        }
13142        this.cacheStyleSheet(ss);
13143        return ss;
13144    },
13145
13146    /**
13147     * Removes a style or link tag by id
13148     * @param {String} id The id of the tag
13149     */
13150    removeStyleSheet : function(id){
13151        var existing = doc.getElementById(id);
13152        if(existing){
13153            existing.parentNode.removeChild(existing);
13154        }
13155    },
13156
13157    /**
13158     * Dynamically swaps an existing stylesheet reference for a new one
13159     * @param {String} id The id of an existing link tag to remove
13160     * @param {String} url The href of the new stylesheet to include
13161     */
13162    swapStyleSheet : function(id, url){
13163        this.removeStyleSheet(id);
13164        var ss = doc.createElement("link");
13165        ss.setAttribute("rel", "stylesheet");
13166        ss.setAttribute("type", "text/css");
13167        ss.setAttribute("id", id);
13168        ss.setAttribute("href", url);
13169        doc.getElementsByTagName("head")[0].appendChild(ss);
13170    },
13171    
13172    /**
13173     * Refresh the rule cache if you have dynamically added stylesheets
13174     * @return {Object} An object (hash) of rules indexed by selector
13175     */
13176    refreshCache : function(){
13177        return this.getRules(true);
13178    },
13179
13180    // private
13181    cacheStyleSheet : function(ss){
13182        if(!rules){
13183            rules = {};
13184        }
13185        try{// try catch for cross domain access issue
13186            var ssRules = ss.cssRules || ss.rules;
13187            for(var j = ssRules.length-1; j >= 0; --j){
13188                rules[ssRules[j].selectorText] = ssRules[j];
13189            }
13190        }catch(e){}
13191    },
13192    
13193    /**
13194     * Gets all css rules for the document
13195     * @param {Boolean} refreshCache true to refresh the internal cache
13196     * @return {Object} An object (hash) of rules indexed by selector
13197     */
13198    getRules : function(refreshCache){
13199                 if(rules == null || refreshCache){
13200                         rules = {};
13201                         var ds = doc.styleSheets;
13202                         for(var i =0, len = ds.length; i < len; i++){
13203                             try{
13204                         this.cacheStyleSheet(ds[i]);
13205                     }catch(e){} 
13206                 }
13207                 }
13208                 return rules;
13209         },
13210         
13211         /**
13212     * Gets an an individual CSS rule by selector(s)
13213     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13214     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13215     * @return {CSSRule} The CSS rule or null if one is not found
13216     */
13217    getRule : function(selector, refreshCache){
13218                 var rs = this.getRules(refreshCache);
13219                 if(!(selector instanceof Array)){
13220                     return rs[selector];
13221                 }
13222                 for(var i = 0; i < selector.length; i++){
13223                         if(rs[selector[i]]){
13224                                 return rs[selector[i]];
13225                         }
13226                 }
13227                 return null;
13228         },
13229         
13230         
13231         /**
13232     * Updates a rule property
13233     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13234     * @param {String} property The css property
13235     * @param {String} value The new value for the property
13236     * @return {Boolean} true If a rule was found and updated
13237     */
13238    updateRule : function(selector, property, value){
13239                 if(!(selector instanceof Array)){
13240                         var rule = this.getRule(selector);
13241                         if(rule){
13242                                 rule.style[property.replace(camelRe, camelFn)] = value;
13243                                 return true;
13244                         }
13245                 }else{
13246                         for(var i = 0; i < selector.length; i++){
13247                                 if(this.updateRule(selector[i], property, value)){
13248                                         return true;
13249                                 }
13250                         }
13251                 }
13252                 return false;
13253         }
13254    };   
13255 }();/*
13256  * Based on:
13257  * Ext JS Library 1.1.1
13258  * Copyright(c) 2006-2007, Ext JS, LLC.
13259  *
13260  * Originally Released Under LGPL - original licence link has changed is not relivant.
13261  *
13262  * Fork - LGPL
13263  * <script type="text/javascript">
13264  */
13265
13266  
13267
13268 /**
13269  * @class Roo.util.ClickRepeater
13270  * @extends Roo.util.Observable
13271  * 
13272  * A wrapper class which can be applied to any element. Fires a "click" event while the
13273  * mouse is pressed. The interval between firings may be specified in the config but
13274  * defaults to 10 milliseconds.
13275  * 
13276  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13277  * 
13278  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13279  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13280  * Similar to an autorepeat key delay.
13281  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13282  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13283  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13284  *           "interval" and "delay" are ignored. "immediate" is honored.
13285  * @cfg {Boolean} preventDefault True to prevent the default click event
13286  * @cfg {Boolean} stopDefault True to stop the default click event
13287  * 
13288  * @history
13289  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13290  *     2007-02-02 jvs Renamed to ClickRepeater
13291  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13292  *
13293  *  @constructor
13294  * @param {String/HTMLElement/Element} el The element to listen on
13295  * @param {Object} config
13296  **/
13297 Roo.util.ClickRepeater = function(el, config)
13298 {
13299     this.el = Roo.get(el);
13300     this.el.unselectable();
13301
13302     Roo.apply(this, config);
13303
13304     this.addEvents({
13305     /**
13306      * @event mousedown
13307      * Fires when the mouse button is depressed.
13308      * @param {Roo.util.ClickRepeater} this
13309      */
13310         "mousedown" : true,
13311     /**
13312      * @event click
13313      * Fires on a specified interval during the time the element is pressed.
13314      * @param {Roo.util.ClickRepeater} this
13315      */
13316         "click" : true,
13317     /**
13318      * @event mouseup
13319      * Fires when the mouse key is released.
13320      * @param {Roo.util.ClickRepeater} this
13321      */
13322         "mouseup" : true
13323     });
13324
13325     this.el.on("mousedown", this.handleMouseDown, this);
13326     if(this.preventDefault || this.stopDefault){
13327         this.el.on("click", function(e){
13328             if(this.preventDefault){
13329                 e.preventDefault();
13330             }
13331             if(this.stopDefault){
13332                 e.stopEvent();
13333             }
13334         }, this);
13335     }
13336
13337     // allow inline handler
13338     if(this.handler){
13339         this.on("click", this.handler,  this.scope || this);
13340     }
13341
13342     Roo.util.ClickRepeater.superclass.constructor.call(this);
13343 };
13344
13345 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13346     interval : 20,
13347     delay: 250,
13348     preventDefault : true,
13349     stopDefault : false,
13350     timer : 0,
13351
13352     // private
13353     handleMouseDown : function(){
13354         clearTimeout(this.timer);
13355         this.el.blur();
13356         if(this.pressClass){
13357             this.el.addClass(this.pressClass);
13358         }
13359         this.mousedownTime = new Date();
13360
13361         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13362         this.el.on("mouseout", this.handleMouseOut, this);
13363
13364         this.fireEvent("mousedown", this);
13365         this.fireEvent("click", this);
13366         
13367         this.timer = this.click.defer(this.delay || this.interval, this);
13368     },
13369
13370     // private
13371     click : function(){
13372         this.fireEvent("click", this);
13373         this.timer = this.click.defer(this.getInterval(), this);
13374     },
13375
13376     // private
13377     getInterval: function(){
13378         if(!this.accelerate){
13379             return this.interval;
13380         }
13381         var pressTime = this.mousedownTime.getElapsed();
13382         if(pressTime < 500){
13383             return 400;
13384         }else if(pressTime < 1700){
13385             return 320;
13386         }else if(pressTime < 2600){
13387             return 250;
13388         }else if(pressTime < 3500){
13389             return 180;
13390         }else if(pressTime < 4400){
13391             return 140;
13392         }else if(pressTime < 5300){
13393             return 80;
13394         }else if(pressTime < 6200){
13395             return 50;
13396         }else{
13397             return 10;
13398         }
13399     },
13400
13401     // private
13402     handleMouseOut : function(){
13403         clearTimeout(this.timer);
13404         if(this.pressClass){
13405             this.el.removeClass(this.pressClass);
13406         }
13407         this.el.on("mouseover", this.handleMouseReturn, this);
13408     },
13409
13410     // private
13411     handleMouseReturn : function(){
13412         this.el.un("mouseover", this.handleMouseReturn);
13413         if(this.pressClass){
13414             this.el.addClass(this.pressClass);
13415         }
13416         this.click();
13417     },
13418
13419     // private
13420     handleMouseUp : function(){
13421         clearTimeout(this.timer);
13422         this.el.un("mouseover", this.handleMouseReturn);
13423         this.el.un("mouseout", this.handleMouseOut);
13424         Roo.get(document).un("mouseup", this.handleMouseUp);
13425         this.el.removeClass(this.pressClass);
13426         this.fireEvent("mouseup", this);
13427     }
13428 });/*
13429  * Based on:
13430  * Ext JS Library 1.1.1
13431  * Copyright(c) 2006-2007, Ext JS, LLC.
13432  *
13433  * Originally Released Under LGPL - original licence link has changed is not relivant.
13434  *
13435  * Fork - LGPL
13436  * <script type="text/javascript">
13437  */
13438
13439  
13440 /**
13441  * @class Roo.KeyNav
13442  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13443  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13444  * way to implement custom navigation schemes for any UI component.</p>
13445  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13446  * pageUp, pageDown, del, home, end.  Usage:</p>
13447  <pre><code>
13448 var nav = new Roo.KeyNav("my-element", {
13449     "left" : function(e){
13450         this.moveLeft(e.ctrlKey);
13451     },
13452     "right" : function(e){
13453         this.moveRight(e.ctrlKey);
13454     },
13455     "enter" : function(e){
13456         this.save();
13457     },
13458     scope : this
13459 });
13460 </code></pre>
13461  * @constructor
13462  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13463  * @param {Object} config The config
13464  */
13465 Roo.KeyNav = function(el, config){
13466     this.el = Roo.get(el);
13467     Roo.apply(this, config);
13468     if(!this.disabled){
13469         this.disabled = true;
13470         this.enable();
13471     }
13472 };
13473
13474 Roo.KeyNav.prototype = {
13475     /**
13476      * @cfg {Boolean} disabled
13477      * True to disable this KeyNav instance (defaults to false)
13478      */
13479     disabled : false,
13480     /**
13481      * @cfg {String} defaultEventAction
13482      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13483      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13484      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13485      */
13486     defaultEventAction: "stopEvent",
13487     /**
13488      * @cfg {Boolean} forceKeyDown
13489      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13490      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13491      * handle keydown instead of keypress.
13492      */
13493     forceKeyDown : false,
13494
13495     // private
13496     prepareEvent : function(e){
13497         var k = e.getKey();
13498         var h = this.keyToHandler[k];
13499         //if(h && this[h]){
13500         //    e.stopPropagation();
13501         //}
13502         if(Roo.isSafari && h && k >= 37 && k <= 40){
13503             e.stopEvent();
13504         }
13505     },
13506
13507     // private
13508     relay : function(e){
13509         var k = e.getKey();
13510         var h = this.keyToHandler[k];
13511         if(h && this[h]){
13512             if(this.doRelay(e, this[h], h) !== true){
13513                 e[this.defaultEventAction]();
13514             }
13515         }
13516     },
13517
13518     // private
13519     doRelay : function(e, h, hname){
13520         return h.call(this.scope || this, e);
13521     },
13522
13523     // possible handlers
13524     enter : false,
13525     left : false,
13526     right : false,
13527     up : false,
13528     down : false,
13529     tab : false,
13530     esc : false,
13531     pageUp : false,
13532     pageDown : false,
13533     del : false,
13534     home : false,
13535     end : false,
13536
13537     // quick lookup hash
13538     keyToHandler : {
13539         37 : "left",
13540         39 : "right",
13541         38 : "up",
13542         40 : "down",
13543         33 : "pageUp",
13544         34 : "pageDown",
13545         46 : "del",
13546         36 : "home",
13547         35 : "end",
13548         13 : "enter",
13549         27 : "esc",
13550         9  : "tab"
13551     },
13552
13553         /**
13554          * Enable this KeyNav
13555          */
13556         enable: function(){
13557                 if(this.disabled){
13558             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13559             // the EventObject will normalize Safari automatically
13560             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13561                 this.el.on("keydown", this.relay,  this);
13562             }else{
13563                 this.el.on("keydown", this.prepareEvent,  this);
13564                 this.el.on("keypress", this.relay,  this);
13565             }
13566                     this.disabled = false;
13567                 }
13568         },
13569
13570         /**
13571          * Disable this KeyNav
13572          */
13573         disable: function(){
13574                 if(!this.disabled){
13575                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13576                 this.el.un("keydown", this.relay);
13577             }else{
13578                 this.el.un("keydown", this.prepareEvent);
13579                 this.el.un("keypress", this.relay);
13580             }
13581                     this.disabled = true;
13582                 }
13583         }
13584 };/*
13585  * Based on:
13586  * Ext JS Library 1.1.1
13587  * Copyright(c) 2006-2007, Ext JS, LLC.
13588  *
13589  * Originally Released Under LGPL - original licence link has changed is not relivant.
13590  *
13591  * Fork - LGPL
13592  * <script type="text/javascript">
13593  */
13594
13595  
13596 /**
13597  * @class Roo.KeyMap
13598  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13599  * The constructor accepts the same config object as defined by {@link #addBinding}.
13600  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13601  * combination it will call the function with this signature (if the match is a multi-key
13602  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13603  * A KeyMap can also handle a string representation of keys.<br />
13604  * Usage:
13605  <pre><code>
13606 // map one key by key code
13607 var map = new Roo.KeyMap("my-element", {
13608     key: 13, // or Roo.EventObject.ENTER
13609     fn: myHandler,
13610     scope: myObject
13611 });
13612
13613 // map multiple keys to one action by string
13614 var map = new Roo.KeyMap("my-element", {
13615     key: "a\r\n\t",
13616     fn: myHandler,
13617     scope: myObject
13618 });
13619
13620 // map multiple keys to multiple actions by strings and array of codes
13621 var map = new Roo.KeyMap("my-element", [
13622     {
13623         key: [10,13],
13624         fn: function(){ alert("Return was pressed"); }
13625     }, {
13626         key: "abc",
13627         fn: function(){ alert('a, b or c was pressed'); }
13628     }, {
13629         key: "\t",
13630         ctrl:true,
13631         shift:true,
13632         fn: function(){ alert('Control + shift + tab was pressed.'); }
13633     }
13634 ]);
13635 </code></pre>
13636  * <b>Note: A KeyMap starts enabled</b>
13637  * @constructor
13638  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13639  * @param {Object} config The config (see {@link #addBinding})
13640  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13641  */
13642 Roo.KeyMap = function(el, config, eventName){
13643     this.el  = Roo.get(el);
13644     this.eventName = eventName || "keydown";
13645     this.bindings = [];
13646     if(config){
13647         this.addBinding(config);
13648     }
13649     this.enable();
13650 };
13651
13652 Roo.KeyMap.prototype = {
13653     /**
13654      * True to stop the event from bubbling and prevent the default browser action if the
13655      * key was handled by the KeyMap (defaults to false)
13656      * @type Boolean
13657      */
13658     stopEvent : false,
13659
13660     /**
13661      * Add a new binding to this KeyMap. The following config object properties are supported:
13662      * <pre>
13663 Property    Type             Description
13664 ----------  ---------------  ----------------------------------------------------------------------
13665 key         String/Array     A single keycode or an array of keycodes to handle
13666 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13667 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13668 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13669 fn          Function         The function to call when KeyMap finds the expected key combination
13670 scope       Object           The scope of the callback function
13671 </pre>
13672      *
13673      * Usage:
13674      * <pre><code>
13675 // Create a KeyMap
13676 var map = new Roo.KeyMap(document, {
13677     key: Roo.EventObject.ENTER,
13678     fn: handleKey,
13679     scope: this
13680 });
13681
13682 //Add a new binding to the existing KeyMap later
13683 map.addBinding({
13684     key: 'abc',
13685     shift: true,
13686     fn: handleKey,
13687     scope: this
13688 });
13689 </code></pre>
13690      * @param {Object/Array} config A single KeyMap config or an array of configs
13691      */
13692         addBinding : function(config){
13693         if(config instanceof Array){
13694             for(var i = 0, len = config.length; i < len; i++){
13695                 this.addBinding(config[i]);
13696             }
13697             return;
13698         }
13699         var keyCode = config.key,
13700             shift = config.shift, 
13701             ctrl = config.ctrl, 
13702             alt = config.alt,
13703             fn = config.fn,
13704             scope = config.scope;
13705         if(typeof keyCode == "string"){
13706             var ks = [];
13707             var keyString = keyCode.toUpperCase();
13708             for(var j = 0, len = keyString.length; j < len; j++){
13709                 ks.push(keyString.charCodeAt(j));
13710             }
13711             keyCode = ks;
13712         }
13713         var keyArray = keyCode instanceof Array;
13714         var handler = function(e){
13715             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13716                 var k = e.getKey();
13717                 if(keyArray){
13718                     for(var i = 0, len = keyCode.length; i < len; i++){
13719                         if(keyCode[i] == k){
13720                           if(this.stopEvent){
13721                               e.stopEvent();
13722                           }
13723                           fn.call(scope || window, k, e);
13724                           return;
13725                         }
13726                     }
13727                 }else{
13728                     if(k == keyCode){
13729                         if(this.stopEvent){
13730                            e.stopEvent();
13731                         }
13732                         fn.call(scope || window, k, e);
13733                     }
13734                 }
13735             }
13736         };
13737         this.bindings.push(handler);  
13738         },
13739
13740     /**
13741      * Shorthand for adding a single key listener
13742      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13743      * following options:
13744      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13745      * @param {Function} fn The function to call
13746      * @param {Object} scope (optional) The scope of the function
13747      */
13748     on : function(key, fn, scope){
13749         var keyCode, shift, ctrl, alt;
13750         if(typeof key == "object" && !(key instanceof Array)){
13751             keyCode = key.key;
13752             shift = key.shift;
13753             ctrl = key.ctrl;
13754             alt = key.alt;
13755         }else{
13756             keyCode = key;
13757         }
13758         this.addBinding({
13759             key: keyCode,
13760             shift: shift,
13761             ctrl: ctrl,
13762             alt: alt,
13763             fn: fn,
13764             scope: scope
13765         })
13766     },
13767
13768     // private
13769     handleKeyDown : function(e){
13770             if(this.enabled){ //just in case
13771             var b = this.bindings;
13772             for(var i = 0, len = b.length; i < len; i++){
13773                 b[i].call(this, e);
13774             }
13775             }
13776         },
13777         
13778         /**
13779          * Returns true if this KeyMap is enabled
13780          * @return {Boolean} 
13781          */
13782         isEnabled : function(){
13783             return this.enabled;  
13784         },
13785         
13786         /**
13787          * Enables this KeyMap
13788          */
13789         enable: function(){
13790                 if(!this.enabled){
13791                     this.el.on(this.eventName, this.handleKeyDown, this);
13792                     this.enabled = true;
13793                 }
13794         },
13795
13796         /**
13797          * Disable this KeyMap
13798          */
13799         disable: function(){
13800                 if(this.enabled){
13801                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13802                     this.enabled = false;
13803                 }
13804         }
13805 };/*
13806  * Based on:
13807  * Ext JS Library 1.1.1
13808  * Copyright(c) 2006-2007, Ext JS, LLC.
13809  *
13810  * Originally Released Under LGPL - original licence link has changed is not relivant.
13811  *
13812  * Fork - LGPL
13813  * <script type="text/javascript">
13814  */
13815
13816  
13817 /**
13818  * @class Roo.util.TextMetrics
13819  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13820  * wide, in pixels, a given block of text will be.
13821  * @singleton
13822  */
13823 Roo.util.TextMetrics = function(){
13824     var shared;
13825     return {
13826         /**
13827          * Measures the size of the specified text
13828          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13829          * that can affect the size of the rendered text
13830          * @param {String} text The text to measure
13831          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13832          * in order to accurately measure the text height
13833          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13834          */
13835         measure : function(el, text, fixedWidth){
13836             if(!shared){
13837                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13838             }
13839             shared.bind(el);
13840             shared.setFixedWidth(fixedWidth || 'auto');
13841             return shared.getSize(text);
13842         },
13843
13844         /**
13845          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13846          * the overhead of multiple calls to initialize the style properties on each measurement.
13847          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13848          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13849          * in order to accurately measure the text height
13850          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13851          */
13852         createInstance : function(el, fixedWidth){
13853             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13854         }
13855     };
13856 }();
13857
13858 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13859     var ml = new Roo.Element(document.createElement('div'));
13860     document.body.appendChild(ml.dom);
13861     ml.position('absolute');
13862     ml.setLeftTop(-1000, -1000);
13863     ml.hide();
13864
13865     if(fixedWidth){
13866         ml.setWidth(fixedWidth);
13867     }
13868
13869     var instance = {
13870         /**
13871          * Returns the size of the specified text based on the internal element's style and width properties
13872          * @param {String} text The text to measure
13873          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13874          */
13875         getSize : function(text){
13876             ml.update(text);
13877             var s = ml.getSize();
13878             ml.update('');
13879             return s;
13880         },
13881
13882         /**
13883          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13884          * that can affect the size of the rendered text
13885          * @param {String/HTMLElement} el The element, dom node or id
13886          */
13887         bind : function(el){
13888             ml.setStyle(
13889                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13890             );
13891         },
13892
13893         /**
13894          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13895          * to set a fixed width in order to accurately measure the text height.
13896          * @param {Number} width The width to set on the element
13897          */
13898         setFixedWidth : function(width){
13899             ml.setWidth(width);
13900         },
13901
13902         /**
13903          * Returns the measured width of the specified text
13904          * @param {String} text The text to measure
13905          * @return {Number} width The width in pixels
13906          */
13907         getWidth : function(text){
13908             ml.dom.style.width = 'auto';
13909             return this.getSize(text).width;
13910         },
13911
13912         /**
13913          * Returns the measured height of the specified text.  For multiline text, be sure to call
13914          * {@link #setFixedWidth} if necessary.
13915          * @param {String} text The text to measure
13916          * @return {Number} height The height in pixels
13917          */
13918         getHeight : function(text){
13919             return this.getSize(text).height;
13920         }
13921     };
13922
13923     instance.bind(bindTo);
13924
13925     return instance;
13926 };
13927
13928 // backwards compat
13929 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
13930  * Based on:
13931  * Ext JS Library 1.1.1
13932  * Copyright(c) 2006-2007, Ext JS, LLC.
13933  *
13934  * Originally Released Under LGPL - original licence link has changed is not relivant.
13935  *
13936  * Fork - LGPL
13937  * <script type="text/javascript">
13938  */
13939
13940 /**
13941  * @class Roo.state.Provider
13942  * Abstract base class for state provider implementations. This class provides methods
13943  * for encoding and decoding <b>typed</b> variables including dates and defines the 
13944  * Provider interface.
13945  */
13946 Roo.state.Provider = function(){
13947     /**
13948      * @event statechange
13949      * Fires when a state change occurs.
13950      * @param {Provider} this This state provider
13951      * @param {String} key The state key which was changed
13952      * @param {String} value The encoded value for the state
13953      */
13954     this.addEvents({
13955         "statechange": true
13956     });
13957     this.state = {};
13958     Roo.state.Provider.superclass.constructor.call(this);
13959 };
13960 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
13961     /**
13962      * Returns the current value for a key
13963      * @param {String} name The key name
13964      * @param {Mixed} defaultValue A default value to return if the key's value is not found
13965      * @return {Mixed} The state data
13966      */
13967     get : function(name, defaultValue){
13968         return typeof this.state[name] == "undefined" ?
13969             defaultValue : this.state[name];
13970     },
13971     
13972     /**
13973      * Clears a value from the state
13974      * @param {String} name The key name
13975      */
13976     clear : function(name){
13977         delete this.state[name];
13978         this.fireEvent("statechange", this, name, null);
13979     },
13980     
13981     /**
13982      * Sets the value for a key
13983      * @param {String} name The key name
13984      * @param {Mixed} value The value to set
13985      */
13986     set : function(name, value){
13987         this.state[name] = value;
13988         this.fireEvent("statechange", this, name, value);
13989     },
13990     
13991     /**
13992      * Decodes a string previously encoded with {@link #encodeValue}.
13993      * @param {String} value The value to decode
13994      * @return {Mixed} The decoded value
13995      */
13996     decodeValue : function(cookie){
13997         var re = /^(a|n|d|b|s|o)\:(.*)$/;
13998         var matches = re.exec(unescape(cookie));
13999         if(!matches || !matches[1]) return; // non state cookie
14000         var type = matches[1];
14001         var v = matches[2];
14002         switch(type){
14003             case "n":
14004                 return parseFloat(v);
14005             case "d":
14006                 return new Date(Date.parse(v));
14007             case "b":
14008                 return (v == "1");
14009             case "a":
14010                 var all = [];
14011                 var values = v.split("^");
14012                 for(var i = 0, len = values.length; i < len; i++){
14013                     all.push(this.decodeValue(values[i]));
14014                 }
14015                 return all;
14016            case "o":
14017                 var all = {};
14018                 var values = v.split("^");
14019                 for(var i = 0, len = values.length; i < len; i++){
14020                     var kv = values[i].split("=");
14021                     all[kv[0]] = this.decodeValue(kv[1]);
14022                 }
14023                 return all;
14024            default:
14025                 return v;
14026         }
14027     },
14028     
14029     /**
14030      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14031      * @param {Mixed} value The value to encode
14032      * @return {String} The encoded value
14033      */
14034     encodeValue : function(v){
14035         var enc;
14036         if(typeof v == "number"){
14037             enc = "n:" + v;
14038         }else if(typeof v == "boolean"){
14039             enc = "b:" + (v ? "1" : "0");
14040         }else if(v instanceof Date){
14041             enc = "d:" + v.toGMTString();
14042         }else if(v instanceof Array){
14043             var flat = "";
14044             for(var i = 0, len = v.length; i < len; i++){
14045                 flat += this.encodeValue(v[i]);
14046                 if(i != len-1) flat += "^";
14047             }
14048             enc = "a:" + flat;
14049         }else if(typeof v == "object"){
14050             var flat = "";
14051             for(var key in v){
14052                 if(typeof v[key] != "function"){
14053                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14054                 }
14055             }
14056             enc = "o:" + flat.substring(0, flat.length-1);
14057         }else{
14058             enc = "s:" + v;
14059         }
14060         return escape(enc);        
14061     }
14062 });
14063
14064 /*
14065  * Based on:
14066  * Ext JS Library 1.1.1
14067  * Copyright(c) 2006-2007, Ext JS, LLC.
14068  *
14069  * Originally Released Under LGPL - original licence link has changed is not relivant.
14070  *
14071  * Fork - LGPL
14072  * <script type="text/javascript">
14073  */
14074 /**
14075  * @class Roo.state.Manager
14076  * This is the global state manager. By default all components that are "state aware" check this class
14077  * for state information if you don't pass them a custom state provider. In order for this class
14078  * to be useful, it must be initialized with a provider when your application initializes.
14079  <pre><code>
14080 // in your initialization function
14081 init : function(){
14082    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14083    ...
14084    // supposed you have a {@link Roo.BorderLayout}
14085    var layout = new Roo.BorderLayout(...);
14086    layout.restoreState();
14087    // or a {Roo.BasicDialog}
14088    var dialog = new Roo.BasicDialog(...);
14089    dialog.restoreState();
14090  </code></pre>
14091  * @singleton
14092  */
14093 Roo.state.Manager = function(){
14094     var provider = new Roo.state.Provider();
14095     
14096     return {
14097         /**
14098          * Configures the default state provider for your application
14099          * @param {Provider} stateProvider The state provider to set
14100          */
14101         setProvider : function(stateProvider){
14102             provider = stateProvider;
14103         },
14104         
14105         /**
14106          * Returns the current value for a key
14107          * @param {String} name The key name
14108          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14109          * @return {Mixed} The state data
14110          */
14111         get : function(key, defaultValue){
14112             return provider.get(key, defaultValue);
14113         },
14114         
14115         /**
14116          * Sets the value for a key
14117          * @param {String} name The key name
14118          * @param {Mixed} value The state data
14119          */
14120          set : function(key, value){
14121             provider.set(key, value);
14122         },
14123         
14124         /**
14125          * Clears a value from the state
14126          * @param {String} name The key name
14127          */
14128         clear : function(key){
14129             provider.clear(key);
14130         },
14131         
14132         /**
14133          * Gets the currently configured state provider
14134          * @return {Provider} The state provider
14135          */
14136         getProvider : function(){
14137             return provider;
14138         }
14139     };
14140 }();
14141 /*
14142  * Based on:
14143  * Ext JS Library 1.1.1
14144  * Copyright(c) 2006-2007, Ext JS, LLC.
14145  *
14146  * Originally Released Under LGPL - original licence link has changed is not relivant.
14147  *
14148  * Fork - LGPL
14149  * <script type="text/javascript">
14150  */
14151 /**
14152  * @class Roo.state.CookieProvider
14153  * @extends Roo.state.Provider
14154  * The default Provider implementation which saves state via cookies.
14155  * <br />Usage:
14156  <pre><code>
14157    var cp = new Roo.state.CookieProvider({
14158        path: "/cgi-bin/",
14159        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14160        domain: "roojs.com"
14161    })
14162    Roo.state.Manager.setProvider(cp);
14163  </code></pre>
14164  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14165  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14166  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14167  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14168  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14169  * domain the page is running on including the 'www' like 'www.roojs.com')
14170  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14171  * @constructor
14172  * Create a new CookieProvider
14173  * @param {Object} config The configuration object
14174  */
14175 Roo.state.CookieProvider = function(config){
14176     Roo.state.CookieProvider.superclass.constructor.call(this);
14177     this.path = "/";
14178     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14179     this.domain = null;
14180     this.secure = false;
14181     Roo.apply(this, config);
14182     this.state = this.readCookies();
14183 };
14184
14185 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14186     // private
14187     set : function(name, value){
14188         if(typeof value == "undefined" || value === null){
14189             this.clear(name);
14190             return;
14191         }
14192         this.setCookie(name, value);
14193         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14194     },
14195
14196     // private
14197     clear : function(name){
14198         this.clearCookie(name);
14199         Roo.state.CookieProvider.superclass.clear.call(this, name);
14200     },
14201
14202     // private
14203     readCookies : function(){
14204         var cookies = {};
14205         var c = document.cookie + ";";
14206         var re = /\s?(.*?)=(.*?);/g;
14207         var matches;
14208         while((matches = re.exec(c)) != null){
14209             var name = matches[1];
14210             var value = matches[2];
14211             if(name && name.substring(0,3) == "ys-"){
14212                 cookies[name.substr(3)] = this.decodeValue(value);
14213             }
14214         }
14215         return cookies;
14216     },
14217
14218     // private
14219     setCookie : function(name, value){
14220         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14221            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14222            ((this.path == null) ? "" : ("; path=" + this.path)) +
14223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14224            ((this.secure == true) ? "; secure" : "");
14225     },
14226
14227     // private
14228     clearCookie : function(name){
14229         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14230            ((this.path == null) ? "" : ("; path=" + this.path)) +
14231            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14232            ((this.secure == true) ? "; secure" : "");
14233     }
14234 });/*
14235  * Based on:
14236  * Ext JS Library 1.1.1
14237  * Copyright(c) 2006-2007, Ext JS, LLC.
14238  *
14239  * Originally Released Under LGPL - original licence link has changed is not relivant.
14240  *
14241  * Fork - LGPL
14242  * <script type="text/javascript">
14243  */
14244
14245
14246
14247 /*
14248  * These classes are derivatives of the similarly named classes in the YUI Library.
14249  * The original license:
14250  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14251  * Code licensed under the BSD License:
14252  * http://developer.yahoo.net/yui/license.txt
14253  */
14254
14255 (function() {
14256
14257 var Event=Roo.EventManager;
14258 var Dom=Roo.lib.Dom;
14259
14260 /**
14261  * @class Roo.dd.DragDrop
14262  * Defines the interface and base operation of items that that can be
14263  * dragged or can be drop targets.  It was designed to be extended, overriding
14264  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14265  * Up to three html elements can be associated with a DragDrop instance:
14266  * <ul>
14267  * <li>linked element: the element that is passed into the constructor.
14268  * This is the element which defines the boundaries for interaction with
14269  * other DragDrop objects.</li>
14270  * <li>handle element(s): The drag operation only occurs if the element that
14271  * was clicked matches a handle element.  By default this is the linked
14272  * element, but there are times that you will want only a portion of the
14273  * linked element to initiate the drag operation, and the setHandleElId()
14274  * method provides a way to define this.</li>
14275  * <li>drag element: this represents the element that would be moved along
14276  * with the cursor during a drag operation.  By default, this is the linked
14277  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14278  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14279  * </li>
14280  * </ul>
14281  * This class should not be instantiated until the onload event to ensure that
14282  * the associated elements are available.
14283  * The following would define a DragDrop obj that would interact with any
14284  * other DragDrop obj in the "group1" group:
14285  * <pre>
14286  *  dd = new Roo.dd.DragDrop("div1", "group1");
14287  * </pre>
14288  * Since none of the event handlers have been implemented, nothing would
14289  * actually happen if you were to run the code above.  Normally you would
14290  * override this class or one of the default implementations, but you can
14291  * also override the methods you want on an instance of the class...
14292  * <pre>
14293  *  dd.onDragDrop = function(e, id) {
14294  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14295  *  }
14296  * </pre>
14297  * @constructor
14298  * @param {String} id of the element that is linked to this instance
14299  * @param {String} sGroup the group of related DragDrop objects
14300  * @param {object} config an object containing configurable attributes
14301  *                Valid properties for DragDrop:
14302  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14303  */
14304 Roo.dd.DragDrop = function(id, sGroup, config) {
14305     if (id) {
14306         this.init(id, sGroup, config);
14307     }
14308 };
14309
14310 Roo.dd.DragDrop.prototype = {
14311
14312     /**
14313      * The id of the element associated with this object.  This is what we
14314      * refer to as the "linked element" because the size and position of
14315      * this element is used to determine when the drag and drop objects have
14316      * interacted.
14317      * @property id
14318      * @type String
14319      */
14320     id: null,
14321
14322     /**
14323      * Configuration attributes passed into the constructor
14324      * @property config
14325      * @type object
14326      */
14327     config: null,
14328
14329     /**
14330      * The id of the element that will be dragged.  By default this is same
14331      * as the linked element , but could be changed to another element. Ex:
14332      * Roo.dd.DDProxy
14333      * @property dragElId
14334      * @type String
14335      * @private
14336      */
14337     dragElId: null,
14338
14339     /**
14340      * the id of the element that initiates the drag operation.  By default
14341      * this is the linked element, but could be changed to be a child of this
14342      * element.  This lets us do things like only starting the drag when the
14343      * header element within the linked html element is clicked.
14344      * @property handleElId
14345      * @type String
14346      * @private
14347      */
14348     handleElId: null,
14349
14350     /**
14351      * An associative array of HTML tags that will be ignored if clicked.
14352      * @property invalidHandleTypes
14353      * @type {string: string}
14354      */
14355     invalidHandleTypes: null,
14356
14357     /**
14358      * An associative array of ids for elements that will be ignored if clicked
14359      * @property invalidHandleIds
14360      * @type {string: string}
14361      */
14362     invalidHandleIds: null,
14363
14364     /**
14365      * An indexted array of css class names for elements that will be ignored
14366      * if clicked.
14367      * @property invalidHandleClasses
14368      * @type string[]
14369      */
14370     invalidHandleClasses: null,
14371
14372     /**
14373      * The linked element's absolute X position at the time the drag was
14374      * started
14375      * @property startPageX
14376      * @type int
14377      * @private
14378      */
14379     startPageX: 0,
14380
14381     /**
14382      * The linked element's absolute X position at the time the drag was
14383      * started
14384      * @property startPageY
14385      * @type int
14386      * @private
14387      */
14388     startPageY: 0,
14389
14390     /**
14391      * The group defines a logical collection of DragDrop objects that are
14392      * related.  Instances only get events when interacting with other
14393      * DragDrop object in the same group.  This lets us define multiple
14394      * groups using a single DragDrop subclass if we want.
14395      * @property groups
14396      * @type {string: string}
14397      */
14398     groups: null,
14399
14400     /**
14401      * Individual drag/drop instances can be locked.  This will prevent
14402      * onmousedown start drag.
14403      * @property locked
14404      * @type boolean
14405      * @private
14406      */
14407     locked: false,
14408
14409     /**
14410      * Lock this instance
14411      * @method lock
14412      */
14413     lock: function() { this.locked = true; },
14414
14415     /**
14416      * Unlock this instace
14417      * @method unlock
14418      */
14419     unlock: function() { this.locked = false; },
14420
14421     /**
14422      * By default, all insances can be a drop target.  This can be disabled by
14423      * setting isTarget to false.
14424      * @method isTarget
14425      * @type boolean
14426      */
14427     isTarget: true,
14428
14429     /**
14430      * The padding configured for this drag and drop object for calculating
14431      * the drop zone intersection with this object.
14432      * @method padding
14433      * @type int[]
14434      */
14435     padding: null,
14436
14437     /**
14438      * Cached reference to the linked element
14439      * @property _domRef
14440      * @private
14441      */
14442     _domRef: null,
14443
14444     /**
14445      * Internal typeof flag
14446      * @property __ygDragDrop
14447      * @private
14448      */
14449     __ygDragDrop: true,
14450
14451     /**
14452      * Set to true when horizontal contraints are applied
14453      * @property constrainX
14454      * @type boolean
14455      * @private
14456      */
14457     constrainX: false,
14458
14459     /**
14460      * Set to true when vertical contraints are applied
14461      * @property constrainY
14462      * @type boolean
14463      * @private
14464      */
14465     constrainY: false,
14466
14467     /**
14468      * The left constraint
14469      * @property minX
14470      * @type int
14471      * @private
14472      */
14473     minX: 0,
14474
14475     /**
14476      * The right constraint
14477      * @property maxX
14478      * @type int
14479      * @private
14480      */
14481     maxX: 0,
14482
14483     /**
14484      * The up constraint
14485      * @property minY
14486      * @type int
14487      * @type int
14488      * @private
14489      */
14490     minY: 0,
14491
14492     /**
14493      * The down constraint
14494      * @property maxY
14495      * @type int
14496      * @private
14497      */
14498     maxY: 0,
14499
14500     /**
14501      * Maintain offsets when we resetconstraints.  Set to true when you want
14502      * the position of the element relative to its parent to stay the same
14503      * when the page changes
14504      *
14505      * @property maintainOffset
14506      * @type boolean
14507      */
14508     maintainOffset: false,
14509
14510     /**
14511      * Array of pixel locations the element will snap to if we specified a
14512      * horizontal graduation/interval.  This array is generated automatically
14513      * when you define a tick interval.
14514      * @property xTicks
14515      * @type int[]
14516      */
14517     xTicks: null,
14518
14519     /**
14520      * Array of pixel locations the element will snap to if we specified a
14521      * vertical graduation/interval.  This array is generated automatically
14522      * when you define a tick interval.
14523      * @property yTicks
14524      * @type int[]
14525      */
14526     yTicks: null,
14527
14528     /**
14529      * By default the drag and drop instance will only respond to the primary
14530      * button click (left button for a right-handed mouse).  Set to true to
14531      * allow drag and drop to start with any mouse click that is propogated
14532      * by the browser
14533      * @property primaryButtonOnly
14534      * @type boolean
14535      */
14536     primaryButtonOnly: true,
14537
14538     /**
14539      * The availabe property is false until the linked dom element is accessible.
14540      * @property available
14541      * @type boolean
14542      */
14543     available: false,
14544
14545     /**
14546      * By default, drags can only be initiated if the mousedown occurs in the
14547      * region the linked element is.  This is done in part to work around a
14548      * bug in some browsers that mis-report the mousedown if the previous
14549      * mouseup happened outside of the window.  This property is set to true
14550      * if outer handles are defined.
14551      *
14552      * @property hasOuterHandles
14553      * @type boolean
14554      * @default false
14555      */
14556     hasOuterHandles: false,
14557
14558     /**
14559      * Code that executes immediately before the startDrag event
14560      * @method b4StartDrag
14561      * @private
14562      */
14563     b4StartDrag: function(x, y) { },
14564
14565     /**
14566      * Abstract method called after a drag/drop object is clicked
14567      * and the drag or mousedown time thresholds have beeen met.
14568      * @method startDrag
14569      * @param {int} X click location
14570      * @param {int} Y click location
14571      */
14572     startDrag: function(x, y) { /* override this */ },
14573
14574     /**
14575      * Code that executes immediately before the onDrag event
14576      * @method b4Drag
14577      * @private
14578      */
14579     b4Drag: function(e) { },
14580
14581     /**
14582      * Abstract method called during the onMouseMove event while dragging an
14583      * object.
14584      * @method onDrag
14585      * @param {Event} e the mousemove event
14586      */
14587     onDrag: function(e) { /* override this */ },
14588
14589     /**
14590      * Abstract method called when this element fist begins hovering over
14591      * another DragDrop obj
14592      * @method onDragEnter
14593      * @param {Event} e the mousemove event
14594      * @param {String|DragDrop[]} id In POINT mode, the element
14595      * id this is hovering over.  In INTERSECT mode, an array of one or more
14596      * dragdrop items being hovered over.
14597      */
14598     onDragEnter: function(e, id) { /* override this */ },
14599
14600     /**
14601      * Code that executes immediately before the onDragOver event
14602      * @method b4DragOver
14603      * @private
14604      */
14605     b4DragOver: function(e) { },
14606
14607     /**
14608      * Abstract method called when this element is hovering over another
14609      * DragDrop obj
14610      * @method onDragOver
14611      * @param {Event} e the mousemove event
14612      * @param {String|DragDrop[]} id In POINT mode, the element
14613      * id this is hovering over.  In INTERSECT mode, an array of dd items
14614      * being hovered over.
14615      */
14616     onDragOver: function(e, id) { /* override this */ },
14617
14618     /**
14619      * Code that executes immediately before the onDragOut event
14620      * @method b4DragOut
14621      * @private
14622      */
14623     b4DragOut: function(e) { },
14624
14625     /**
14626      * Abstract method called when we are no longer hovering over an element
14627      * @method onDragOut
14628      * @param {Event} e the mousemove event
14629      * @param {String|DragDrop[]} id In POINT mode, the element
14630      * id this was hovering over.  In INTERSECT mode, an array of dd items
14631      * that the mouse is no longer over.
14632      */
14633     onDragOut: function(e, id) { /* override this */ },
14634
14635     /**
14636      * Code that executes immediately before the onDragDrop event
14637      * @method b4DragDrop
14638      * @private
14639      */
14640     b4DragDrop: function(e) { },
14641
14642     /**
14643      * Abstract method called when this item is dropped on another DragDrop
14644      * obj
14645      * @method onDragDrop
14646      * @param {Event} e the mouseup event
14647      * @param {String|DragDrop[]} id In POINT mode, the element
14648      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14649      * was dropped on.
14650      */
14651     onDragDrop: function(e, id) { /* override this */ },
14652
14653     /**
14654      * Abstract method called when this item is dropped on an area with no
14655      * drop target
14656      * @method onInvalidDrop
14657      * @param {Event} e the mouseup event
14658      */
14659     onInvalidDrop: function(e) { /* override this */ },
14660
14661     /**
14662      * Code that executes immediately before the endDrag event
14663      * @method b4EndDrag
14664      * @private
14665      */
14666     b4EndDrag: function(e) { },
14667
14668     /**
14669      * Fired when we are done dragging the object
14670      * @method endDrag
14671      * @param {Event} e the mouseup event
14672      */
14673     endDrag: function(e) { /* override this */ },
14674
14675     /**
14676      * Code executed immediately before the onMouseDown event
14677      * @method b4MouseDown
14678      * @param {Event} e the mousedown event
14679      * @private
14680      */
14681     b4MouseDown: function(e) {  },
14682
14683     /**
14684      * Event handler that fires when a drag/drop obj gets a mousedown
14685      * @method onMouseDown
14686      * @param {Event} e the mousedown event
14687      */
14688     onMouseDown: function(e) { /* override this */ },
14689
14690     /**
14691      * Event handler that fires when a drag/drop obj gets a mouseup
14692      * @method onMouseUp
14693      * @param {Event} e the mouseup event
14694      */
14695     onMouseUp: function(e) { /* override this */ },
14696
14697     /**
14698      * Override the onAvailable method to do what is needed after the initial
14699      * position was determined.
14700      * @method onAvailable
14701      */
14702     onAvailable: function () {
14703     },
14704
14705     /*
14706      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14707      * @type Object
14708      */
14709     defaultPadding : {left:0, right:0, top:0, bottom:0},
14710
14711     /*
14712      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14713  *
14714  * Usage:
14715  <pre><code>
14716  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14717                 { dragElId: "existingProxyDiv" });
14718  dd.startDrag = function(){
14719      this.constrainTo("parent-id");
14720  };
14721  </code></pre>
14722  * Or you can initalize it using the {@link Roo.Element} object:
14723  <pre><code>
14724  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14725      startDrag : function(){
14726          this.constrainTo("parent-id");
14727      }
14728  });
14729  </code></pre>
14730      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14731      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14732      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14733      * an object containing the sides to pad. For example: {right:10, bottom:10}
14734      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14735      */
14736     constrainTo : function(constrainTo, pad, inContent){
14737         if(typeof pad == "number"){
14738             pad = {left: pad, right:pad, top:pad, bottom:pad};
14739         }
14740         pad = pad || this.defaultPadding;
14741         var b = Roo.get(this.getEl()).getBox();
14742         var ce = Roo.get(constrainTo);
14743         var s = ce.getScroll();
14744         var c, cd = ce.dom;
14745         if(cd == document.body){
14746             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14747         }else{
14748             xy = ce.getXY();
14749             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14750         }
14751
14752
14753         var topSpace = b.y - c.y;
14754         var leftSpace = b.x - c.x;
14755
14756         this.resetConstraints();
14757         this.setXConstraint(leftSpace - (pad.left||0), // left
14758                 c.width - leftSpace - b.width - (pad.right||0) //right
14759         );
14760         this.setYConstraint(topSpace - (pad.top||0), //top
14761                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14762         );
14763     },
14764
14765     /**
14766      * Returns a reference to the linked element
14767      * @method getEl
14768      * @return {HTMLElement} the html element
14769      */
14770     getEl: function() {
14771         if (!this._domRef) {
14772             this._domRef = Roo.getDom(this.id);
14773         }
14774
14775         return this._domRef;
14776     },
14777
14778     /**
14779      * Returns a reference to the actual element to drag.  By default this is
14780      * the same as the html element, but it can be assigned to another
14781      * element. An example of this can be found in Roo.dd.DDProxy
14782      * @method getDragEl
14783      * @return {HTMLElement} the html element
14784      */
14785     getDragEl: function() {
14786         return Roo.getDom(this.dragElId);
14787     },
14788
14789     /**
14790      * Sets up the DragDrop object.  Must be called in the constructor of any
14791      * Roo.dd.DragDrop subclass
14792      * @method init
14793      * @param id the id of the linked element
14794      * @param {String} sGroup the group of related items
14795      * @param {object} config configuration attributes
14796      */
14797     init: function(id, sGroup, config) {
14798         this.initTarget(id, sGroup, config);
14799         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14800         // Event.on(this.id, "selectstart", Event.preventDefault);
14801     },
14802
14803     /**
14804      * Initializes Targeting functionality only... the object does not
14805      * get a mousedown handler.
14806      * @method initTarget
14807      * @param id the id of the linked element
14808      * @param {String} sGroup the group of related items
14809      * @param {object} config configuration attributes
14810      */
14811     initTarget: function(id, sGroup, config) {
14812
14813         // configuration attributes
14814         this.config = config || {};
14815
14816         // create a local reference to the drag and drop manager
14817         this.DDM = Roo.dd.DDM;
14818         // initialize the groups array
14819         this.groups = {};
14820
14821         // assume that we have an element reference instead of an id if the
14822         // parameter is not a string
14823         if (typeof id !== "string") {
14824             id = Roo.id(id);
14825         }
14826
14827         // set the id
14828         this.id = id;
14829
14830         // add to an interaction group
14831         this.addToGroup((sGroup) ? sGroup : "default");
14832
14833         // We don't want to register this as the handle with the manager
14834         // so we just set the id rather than calling the setter.
14835         this.handleElId = id;
14836
14837         // the linked element is the element that gets dragged by default
14838         this.setDragElId(id);
14839
14840         // by default, clicked anchors will not start drag operations.
14841         this.invalidHandleTypes = { A: "A" };
14842         this.invalidHandleIds = {};
14843         this.invalidHandleClasses = [];
14844
14845         this.applyConfig();
14846
14847         this.handleOnAvailable();
14848     },
14849
14850     /**
14851      * Applies the configuration parameters that were passed into the constructor.
14852      * This is supposed to happen at each level through the inheritance chain.  So
14853      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14854      * DragDrop in order to get all of the parameters that are available in
14855      * each object.
14856      * @method applyConfig
14857      */
14858     applyConfig: function() {
14859
14860         // configurable properties:
14861         //    padding, isTarget, maintainOffset, primaryButtonOnly
14862         this.padding           = this.config.padding || [0, 0, 0, 0];
14863         this.isTarget          = (this.config.isTarget !== false);
14864         this.maintainOffset    = (this.config.maintainOffset);
14865         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14866
14867     },
14868
14869     /**
14870      * Executed when the linked element is available
14871      * @method handleOnAvailable
14872      * @private
14873      */
14874     handleOnAvailable: function() {
14875         this.available = true;
14876         this.resetConstraints();
14877         this.onAvailable();
14878     },
14879
14880      /**
14881      * Configures the padding for the target zone in px.  Effectively expands
14882      * (or reduces) the virtual object size for targeting calculations.
14883      * Supports css-style shorthand; if only one parameter is passed, all sides
14884      * will have that padding, and if only two are passed, the top and bottom
14885      * will have the first param, the left and right the second.
14886      * @method setPadding
14887      * @param {int} iTop    Top pad
14888      * @param {int} iRight  Right pad
14889      * @param {int} iBot    Bot pad
14890      * @param {int} iLeft   Left pad
14891      */
14892     setPadding: function(iTop, iRight, iBot, iLeft) {
14893         // this.padding = [iLeft, iRight, iTop, iBot];
14894         if (!iRight && 0 !== iRight) {
14895             this.padding = [iTop, iTop, iTop, iTop];
14896         } else if (!iBot && 0 !== iBot) {
14897             this.padding = [iTop, iRight, iTop, iRight];
14898         } else {
14899             this.padding = [iTop, iRight, iBot, iLeft];
14900         }
14901     },
14902
14903     /**
14904      * Stores the initial placement of the linked element.
14905      * @method setInitialPosition
14906      * @param {int} diffX   the X offset, default 0
14907      * @param {int} diffY   the Y offset, default 0
14908      */
14909     setInitPosition: function(diffX, diffY) {
14910         var el = this.getEl();
14911
14912         if (!this.DDM.verifyEl(el)) {
14913             return;
14914         }
14915
14916         var dx = diffX || 0;
14917         var dy = diffY || 0;
14918
14919         var p = Dom.getXY( el );
14920
14921         this.initPageX = p[0] - dx;
14922         this.initPageY = p[1] - dy;
14923
14924         this.lastPageX = p[0];
14925         this.lastPageY = p[1];
14926
14927
14928         this.setStartPosition(p);
14929     },
14930
14931     /**
14932      * Sets the start position of the element.  This is set when the obj
14933      * is initialized, the reset when a drag is started.
14934      * @method setStartPosition
14935      * @param pos current position (from previous lookup)
14936      * @private
14937      */
14938     setStartPosition: function(pos) {
14939         var p = pos || Dom.getXY( this.getEl() );
14940         this.deltaSetXY = null;
14941
14942         this.startPageX = p[0];
14943         this.startPageY = p[1];
14944     },
14945
14946     /**
14947      * Add this instance to a group of related drag/drop objects.  All
14948      * instances belong to at least one group, and can belong to as many
14949      * groups as needed.
14950      * @method addToGroup
14951      * @param sGroup {string} the name of the group
14952      */
14953     addToGroup: function(sGroup) {
14954         this.groups[sGroup] = true;
14955         this.DDM.regDragDrop(this, sGroup);
14956     },
14957
14958     /**
14959      * Remove's this instance from the supplied interaction group
14960      * @method removeFromGroup
14961      * @param {string}  sGroup  The group to drop
14962      */
14963     removeFromGroup: function(sGroup) {
14964         if (this.groups[sGroup]) {
14965             delete this.groups[sGroup];
14966         }
14967
14968         this.DDM.removeDDFromGroup(this, sGroup);
14969     },
14970
14971     /**
14972      * Allows you to specify that an element other than the linked element
14973      * will be moved with the cursor during a drag
14974      * @method setDragElId
14975      * @param id {string} the id of the element that will be used to initiate the drag
14976      */
14977     setDragElId: function(id) {
14978         this.dragElId = id;
14979     },
14980
14981     /**
14982      * Allows you to specify a child of the linked element that should be
14983      * used to initiate the drag operation.  An example of this would be if
14984      * you have a content div with text and links.  Clicking anywhere in the
14985      * content area would normally start the drag operation.  Use this method
14986      * to specify that an element inside of the content div is the element
14987      * that starts the drag operation.
14988      * @method setHandleElId
14989      * @param id {string} the id of the element that will be used to
14990      * initiate the drag.
14991      */
14992     setHandleElId: function(id) {
14993         if (typeof id !== "string") {
14994             id = Roo.id(id);
14995         }
14996         this.handleElId = id;
14997         this.DDM.regHandle(this.id, id);
14998     },
14999
15000     /**
15001      * Allows you to set an element outside of the linked element as a drag
15002      * handle
15003      * @method setOuterHandleElId
15004      * @param id the id of the element that will be used to initiate the drag
15005      */
15006     setOuterHandleElId: function(id) {
15007         if (typeof id !== "string") {
15008             id = Roo.id(id);
15009         }
15010         Event.on(id, "mousedown",
15011                 this.handleMouseDown, this);
15012         this.setHandleElId(id);
15013
15014         this.hasOuterHandles = true;
15015     },
15016
15017     /**
15018      * Remove all drag and drop hooks for this element
15019      * @method unreg
15020      */
15021     unreg: function() {
15022         Event.un(this.id, "mousedown",
15023                 this.handleMouseDown);
15024         this._domRef = null;
15025         this.DDM._remove(this);
15026     },
15027
15028     destroy : function(){
15029         this.unreg();
15030     },
15031
15032     /**
15033      * Returns true if this instance is locked, or the drag drop mgr is locked
15034      * (meaning that all drag/drop is disabled on the page.)
15035      * @method isLocked
15036      * @return {boolean} true if this obj or all drag/drop is locked, else
15037      * false
15038      */
15039     isLocked: function() {
15040         return (this.DDM.isLocked() || this.locked);
15041     },
15042
15043     /**
15044      * Fired when this object is clicked
15045      * @method handleMouseDown
15046      * @param {Event} e
15047      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15048      * @private
15049      */
15050     handleMouseDown: function(e, oDD){
15051         if (this.primaryButtonOnly && e.button != 0) {
15052             return;
15053         }
15054
15055         if (this.isLocked()) {
15056             return;
15057         }
15058
15059         this.DDM.refreshCache(this.groups);
15060
15061         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15062         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15063         } else {
15064             if (this.clickValidator(e)) {
15065
15066                 // set the initial element position
15067                 this.setStartPosition();
15068
15069
15070                 this.b4MouseDown(e);
15071                 this.onMouseDown(e);
15072
15073                 this.DDM.handleMouseDown(e, this);
15074
15075                 this.DDM.stopEvent(e);
15076             } else {
15077
15078
15079             }
15080         }
15081     },
15082
15083     clickValidator: function(e) {
15084         var target = e.getTarget();
15085         return ( this.isValidHandleChild(target) &&
15086                     (this.id == this.handleElId ||
15087                         this.DDM.handleWasClicked(target, this.id)) );
15088     },
15089
15090     /**
15091      * Allows you to specify a tag name that should not start a drag operation
15092      * when clicked.  This is designed to facilitate embedding links within a
15093      * drag handle that do something other than start the drag.
15094      * @method addInvalidHandleType
15095      * @param {string} tagName the type of element to exclude
15096      */
15097     addInvalidHandleType: function(tagName) {
15098         var type = tagName.toUpperCase();
15099         this.invalidHandleTypes[type] = type;
15100     },
15101
15102     /**
15103      * Lets you to specify an element id for a child of a drag handle
15104      * that should not initiate a drag
15105      * @method addInvalidHandleId
15106      * @param {string} id the element id of the element you wish to ignore
15107      */
15108     addInvalidHandleId: function(id) {
15109         if (typeof id !== "string") {
15110             id = Roo.id(id);
15111         }
15112         this.invalidHandleIds[id] = id;
15113     },
15114
15115     /**
15116      * Lets you specify a css class of elements that will not initiate a drag
15117      * @method addInvalidHandleClass
15118      * @param {string} cssClass the class of the elements you wish to ignore
15119      */
15120     addInvalidHandleClass: function(cssClass) {
15121         this.invalidHandleClasses.push(cssClass);
15122     },
15123
15124     /**
15125      * Unsets an excluded tag name set by addInvalidHandleType
15126      * @method removeInvalidHandleType
15127      * @param {string} tagName the type of element to unexclude
15128      */
15129     removeInvalidHandleType: function(tagName) {
15130         var type = tagName.toUpperCase();
15131         // this.invalidHandleTypes[type] = null;
15132         delete this.invalidHandleTypes[type];
15133     },
15134
15135     /**
15136      * Unsets an invalid handle id
15137      * @method removeInvalidHandleId
15138      * @param {string} id the id of the element to re-enable
15139      */
15140     removeInvalidHandleId: function(id) {
15141         if (typeof id !== "string") {
15142             id = Roo.id(id);
15143         }
15144         delete this.invalidHandleIds[id];
15145     },
15146
15147     /**
15148      * Unsets an invalid css class
15149      * @method removeInvalidHandleClass
15150      * @param {string} cssClass the class of the element(s) you wish to
15151      * re-enable
15152      */
15153     removeInvalidHandleClass: function(cssClass) {
15154         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15155             if (this.invalidHandleClasses[i] == cssClass) {
15156                 delete this.invalidHandleClasses[i];
15157             }
15158         }
15159     },
15160
15161     /**
15162      * Checks the tag exclusion list to see if this click should be ignored
15163      * @method isValidHandleChild
15164      * @param {HTMLElement} node the HTMLElement to evaluate
15165      * @return {boolean} true if this is a valid tag type, false if not
15166      */
15167     isValidHandleChild: function(node) {
15168
15169         var valid = true;
15170         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15171         var nodeName;
15172         try {
15173             nodeName = node.nodeName.toUpperCase();
15174         } catch(e) {
15175             nodeName = node.nodeName;
15176         }
15177         valid = valid && !this.invalidHandleTypes[nodeName];
15178         valid = valid && !this.invalidHandleIds[node.id];
15179
15180         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15181             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15182         }
15183
15184
15185         return valid;
15186
15187     },
15188
15189     /**
15190      * Create the array of horizontal tick marks if an interval was specified
15191      * in setXConstraint().
15192      * @method setXTicks
15193      * @private
15194      */
15195     setXTicks: function(iStartX, iTickSize) {
15196         this.xTicks = [];
15197         this.xTickSize = iTickSize;
15198
15199         var tickMap = {};
15200
15201         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15202             if (!tickMap[i]) {
15203                 this.xTicks[this.xTicks.length] = i;
15204                 tickMap[i] = true;
15205             }
15206         }
15207
15208         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15209             if (!tickMap[i]) {
15210                 this.xTicks[this.xTicks.length] = i;
15211                 tickMap[i] = true;
15212             }
15213         }
15214
15215         this.xTicks.sort(this.DDM.numericSort) ;
15216     },
15217
15218     /**
15219      * Create the array of vertical tick marks if an interval was specified in
15220      * setYConstraint().
15221      * @method setYTicks
15222      * @private
15223      */
15224     setYTicks: function(iStartY, iTickSize) {
15225         this.yTicks = [];
15226         this.yTickSize = iTickSize;
15227
15228         var tickMap = {};
15229
15230         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15231             if (!tickMap[i]) {
15232                 this.yTicks[this.yTicks.length] = i;
15233                 tickMap[i] = true;
15234             }
15235         }
15236
15237         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15238             if (!tickMap[i]) {
15239                 this.yTicks[this.yTicks.length] = i;
15240                 tickMap[i] = true;
15241             }
15242         }
15243
15244         this.yTicks.sort(this.DDM.numericSort) ;
15245     },
15246
15247     /**
15248      * By default, the element can be dragged any place on the screen.  Use
15249      * this method to limit the horizontal travel of the element.  Pass in
15250      * 0,0 for the parameters if you want to lock the drag to the y axis.
15251      * @method setXConstraint
15252      * @param {int} iLeft the number of pixels the element can move to the left
15253      * @param {int} iRight the number of pixels the element can move to the
15254      * right
15255      * @param {int} iTickSize optional parameter for specifying that the
15256      * element
15257      * should move iTickSize pixels at a time.
15258      */
15259     setXConstraint: function(iLeft, iRight, iTickSize) {
15260         this.leftConstraint = iLeft;
15261         this.rightConstraint = iRight;
15262
15263         this.minX = this.initPageX - iLeft;
15264         this.maxX = this.initPageX + iRight;
15265         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15266
15267         this.constrainX = true;
15268     },
15269
15270     /**
15271      * Clears any constraints applied to this instance.  Also clears ticks
15272      * since they can't exist independent of a constraint at this time.
15273      * @method clearConstraints
15274      */
15275     clearConstraints: function() {
15276         this.constrainX = false;
15277         this.constrainY = false;
15278         this.clearTicks();
15279     },
15280
15281     /**
15282      * Clears any tick interval defined for this instance
15283      * @method clearTicks
15284      */
15285     clearTicks: function() {
15286         this.xTicks = null;
15287         this.yTicks = null;
15288         this.xTickSize = 0;
15289         this.yTickSize = 0;
15290     },
15291
15292     /**
15293      * By default, the element can be dragged any place on the screen.  Set
15294      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15295      * parameters if you want to lock the drag to the x axis.
15296      * @method setYConstraint
15297      * @param {int} iUp the number of pixels the element can move up
15298      * @param {int} iDown the number of pixels the element can move down
15299      * @param {int} iTickSize optional parameter for specifying that the
15300      * element should move iTickSize pixels at a time.
15301      */
15302     setYConstraint: function(iUp, iDown, iTickSize) {
15303         this.topConstraint = iUp;
15304         this.bottomConstraint = iDown;
15305
15306         this.minY = this.initPageY - iUp;
15307         this.maxY = this.initPageY + iDown;
15308         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15309
15310         this.constrainY = true;
15311
15312     },
15313
15314     /**
15315      * resetConstraints must be called if you manually reposition a dd element.
15316      * @method resetConstraints
15317      * @param {boolean} maintainOffset
15318      */
15319     resetConstraints: function() {
15320
15321
15322         // Maintain offsets if necessary
15323         if (this.initPageX || this.initPageX === 0) {
15324             // figure out how much this thing has moved
15325             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15326             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15327
15328             this.setInitPosition(dx, dy);
15329
15330         // This is the first time we have detected the element's position
15331         } else {
15332             this.setInitPosition();
15333         }
15334
15335         if (this.constrainX) {
15336             this.setXConstraint( this.leftConstraint,
15337                                  this.rightConstraint,
15338                                  this.xTickSize        );
15339         }
15340
15341         if (this.constrainY) {
15342             this.setYConstraint( this.topConstraint,
15343                                  this.bottomConstraint,
15344                                  this.yTickSize         );
15345         }
15346     },
15347
15348     /**
15349      * Normally the drag element is moved pixel by pixel, but we can specify
15350      * that it move a number of pixels at a time.  This method resolves the
15351      * location when we have it set up like this.
15352      * @method getTick
15353      * @param {int} val where we want to place the object
15354      * @param {int[]} tickArray sorted array of valid points
15355      * @return {int} the closest tick
15356      * @private
15357      */
15358     getTick: function(val, tickArray) {
15359
15360         if (!tickArray) {
15361             // If tick interval is not defined, it is effectively 1 pixel,
15362             // so we return the value passed to us.
15363             return val;
15364         } else if (tickArray[0] >= val) {
15365             // The value is lower than the first tick, so we return the first
15366             // tick.
15367             return tickArray[0];
15368         } else {
15369             for (var i=0, len=tickArray.length; i<len; ++i) {
15370                 var next = i + 1;
15371                 if (tickArray[next] && tickArray[next] >= val) {
15372                     var diff1 = val - tickArray[i];
15373                     var diff2 = tickArray[next] - val;
15374                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15375                 }
15376             }
15377
15378             // The value is larger than the last tick, so we return the last
15379             // tick.
15380             return tickArray[tickArray.length - 1];
15381         }
15382     },
15383
15384     /**
15385      * toString method
15386      * @method toString
15387      * @return {string} string representation of the dd obj
15388      */
15389     toString: function() {
15390         return ("DragDrop " + this.id);
15391     }
15392
15393 };
15394
15395 })();
15396 /*
15397  * Based on:
15398  * Ext JS Library 1.1.1
15399  * Copyright(c) 2006-2007, Ext JS, LLC.
15400  *
15401  * Originally Released Under LGPL - original licence link has changed is not relivant.
15402  *
15403  * Fork - LGPL
15404  * <script type="text/javascript">
15405  */
15406
15407
15408 /**
15409  * The drag and drop utility provides a framework for building drag and drop
15410  * applications.  In addition to enabling drag and drop for specific elements,
15411  * the drag and drop elements are tracked by the manager class, and the
15412  * interactions between the various elements are tracked during the drag and
15413  * the implementing code is notified about these important moments.
15414  */
15415
15416 // Only load the library once.  Rewriting the manager class would orphan
15417 // existing drag and drop instances.
15418 if (!Roo.dd.DragDropMgr) {
15419
15420 /**
15421  * @class Roo.dd.DragDropMgr
15422  * DragDropMgr is a singleton that tracks the element interaction for
15423  * all DragDrop items in the window.  Generally, you will not call
15424  * this class directly, but it does have helper methods that could
15425  * be useful in your DragDrop implementations.
15426  * @singleton
15427  */
15428 Roo.dd.DragDropMgr = function() {
15429
15430     var Event = Roo.EventManager;
15431
15432     return {
15433
15434         /**
15435          * Two dimensional Array of registered DragDrop objects.  The first
15436          * dimension is the DragDrop item group, the second the DragDrop
15437          * object.
15438          * @property ids
15439          * @type {string: string}
15440          * @private
15441          * @static
15442          */
15443         ids: {},
15444
15445         /**
15446          * Array of element ids defined as drag handles.  Used to determine
15447          * if the element that generated the mousedown event is actually the
15448          * handle and not the html element itself.
15449          * @property handleIds
15450          * @type {string: string}
15451          * @private
15452          * @static
15453          */
15454         handleIds: {},
15455
15456         /**
15457          * the DragDrop object that is currently being dragged
15458          * @property dragCurrent
15459          * @type DragDrop
15460          * @private
15461          * @static
15462          **/
15463         dragCurrent: null,
15464
15465         /**
15466          * the DragDrop object(s) that are being hovered over
15467          * @property dragOvers
15468          * @type Array
15469          * @private
15470          * @static
15471          */
15472         dragOvers: {},
15473
15474         /**
15475          * the X distance between the cursor and the object being dragged
15476          * @property deltaX
15477          * @type int
15478          * @private
15479          * @static
15480          */
15481         deltaX: 0,
15482
15483         /**
15484          * the Y distance between the cursor and the object being dragged
15485          * @property deltaY
15486          * @type int
15487          * @private
15488          * @static
15489          */
15490         deltaY: 0,
15491
15492         /**
15493          * Flag to determine if we should prevent the default behavior of the
15494          * events we define. By default this is true, but this can be set to
15495          * false if you need the default behavior (not recommended)
15496          * @property preventDefault
15497          * @type boolean
15498          * @static
15499          */
15500         preventDefault: true,
15501
15502         /**
15503          * Flag to determine if we should stop the propagation of the events
15504          * we generate. This is true by default but you may want to set it to
15505          * false if the html element contains other features that require the
15506          * mouse click.
15507          * @property stopPropagation
15508          * @type boolean
15509          * @static
15510          */
15511         stopPropagation: true,
15512
15513         /**
15514          * Internal flag that is set to true when drag and drop has been
15515          * intialized
15516          * @property initialized
15517          * @private
15518          * @static
15519          */
15520         initalized: false,
15521
15522         /**
15523          * All drag and drop can be disabled.
15524          * @property locked
15525          * @private
15526          * @static
15527          */
15528         locked: false,
15529
15530         /**
15531          * Called the first time an element is registered.
15532          * @method init
15533          * @private
15534          * @static
15535          */
15536         init: function() {
15537             this.initialized = true;
15538         },
15539
15540         /**
15541          * In point mode, drag and drop interaction is defined by the
15542          * location of the cursor during the drag/drop
15543          * @property POINT
15544          * @type int
15545          * @static
15546          */
15547         POINT: 0,
15548
15549         /**
15550          * In intersect mode, drag and drop interactio nis defined by the
15551          * overlap of two or more drag and drop objects.
15552          * @property INTERSECT
15553          * @type int
15554          * @static
15555          */
15556         INTERSECT: 1,
15557
15558         /**
15559          * The current drag and drop mode.  Default: POINT
15560          * @property mode
15561          * @type int
15562          * @static
15563          */
15564         mode: 0,
15565
15566         /**
15567          * Runs method on all drag and drop objects
15568          * @method _execOnAll
15569          * @private
15570          * @static
15571          */
15572         _execOnAll: function(sMethod, args) {
15573             for (var i in this.ids) {
15574                 for (var j in this.ids[i]) {
15575                     var oDD = this.ids[i][j];
15576                     if (! this.isTypeOfDD(oDD)) {
15577                         continue;
15578                     }
15579                     oDD[sMethod].apply(oDD, args);
15580                 }
15581             }
15582         },
15583
15584         /**
15585          * Drag and drop initialization.  Sets up the global event handlers
15586          * @method _onLoad
15587          * @private
15588          * @static
15589          */
15590         _onLoad: function() {
15591
15592             this.init();
15593
15594
15595             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15596             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15597             Event.on(window,   "unload",    this._onUnload, this, true);
15598             Event.on(window,   "resize",    this._onResize, this, true);
15599             // Event.on(window,   "mouseout",    this._test);
15600
15601         },
15602
15603         /**
15604          * Reset constraints on all drag and drop objs
15605          * @method _onResize
15606          * @private
15607          * @static
15608          */
15609         _onResize: function(e) {
15610             this._execOnAll("resetConstraints", []);
15611         },
15612
15613         /**
15614          * Lock all drag and drop functionality
15615          * @method lock
15616          * @static
15617          */
15618         lock: function() { this.locked = true; },
15619
15620         /**
15621          * Unlock all drag and drop functionality
15622          * @method unlock
15623          * @static
15624          */
15625         unlock: function() { this.locked = false; },
15626
15627         /**
15628          * Is drag and drop locked?
15629          * @method isLocked
15630          * @return {boolean} True if drag and drop is locked, false otherwise.
15631          * @static
15632          */
15633         isLocked: function() { return this.locked; },
15634
15635         /**
15636          * Location cache that is set for all drag drop objects when a drag is
15637          * initiated, cleared when the drag is finished.
15638          * @property locationCache
15639          * @private
15640          * @static
15641          */
15642         locationCache: {},
15643
15644         /**
15645          * Set useCache to false if you want to force object the lookup of each
15646          * drag and drop linked element constantly during a drag.
15647          * @property useCache
15648          * @type boolean
15649          * @static
15650          */
15651         useCache: true,
15652
15653         /**
15654          * The number of pixels that the mouse needs to move after the
15655          * mousedown before the drag is initiated.  Default=3;
15656          * @property clickPixelThresh
15657          * @type int
15658          * @static
15659          */
15660         clickPixelThresh: 3,
15661
15662         /**
15663          * The number of milliseconds after the mousedown event to initiate the
15664          * drag if we don't get a mouseup event. Default=1000
15665          * @property clickTimeThresh
15666          * @type int
15667          * @static
15668          */
15669         clickTimeThresh: 350,
15670
15671         /**
15672          * Flag that indicates that either the drag pixel threshold or the
15673          * mousdown time threshold has been met
15674          * @property dragThreshMet
15675          * @type boolean
15676          * @private
15677          * @static
15678          */
15679         dragThreshMet: false,
15680
15681         /**
15682          * Timeout used for the click time threshold
15683          * @property clickTimeout
15684          * @type Object
15685          * @private
15686          * @static
15687          */
15688         clickTimeout: null,
15689
15690         /**
15691          * The X position of the mousedown event stored for later use when a
15692          * drag threshold is met.
15693          * @property startX
15694          * @type int
15695          * @private
15696          * @static
15697          */
15698         startX: 0,
15699
15700         /**
15701          * The Y position of the mousedown event stored for later use when a
15702          * drag threshold is met.
15703          * @property startY
15704          * @type int
15705          * @private
15706          * @static
15707          */
15708         startY: 0,
15709
15710         /**
15711          * Each DragDrop instance must be registered with the DragDropMgr.
15712          * This is executed in DragDrop.init()
15713          * @method regDragDrop
15714          * @param {DragDrop} oDD the DragDrop object to register
15715          * @param {String} sGroup the name of the group this element belongs to
15716          * @static
15717          */
15718         regDragDrop: function(oDD, sGroup) {
15719             if (!this.initialized) { this.init(); }
15720
15721             if (!this.ids[sGroup]) {
15722                 this.ids[sGroup] = {};
15723             }
15724             this.ids[sGroup][oDD.id] = oDD;
15725         },
15726
15727         /**
15728          * Removes the supplied dd instance from the supplied group. Executed
15729          * by DragDrop.removeFromGroup, so don't call this function directly.
15730          * @method removeDDFromGroup
15731          * @private
15732          * @static
15733          */
15734         removeDDFromGroup: function(oDD, sGroup) {
15735             if (!this.ids[sGroup]) {
15736                 this.ids[sGroup] = {};
15737             }
15738
15739             var obj = this.ids[sGroup];
15740             if (obj && obj[oDD.id]) {
15741                 delete obj[oDD.id];
15742             }
15743         },
15744
15745         /**
15746          * Unregisters a drag and drop item.  This is executed in
15747          * DragDrop.unreg, use that method instead of calling this directly.
15748          * @method _remove
15749          * @private
15750          * @static
15751          */
15752         _remove: function(oDD) {
15753             for (var g in oDD.groups) {
15754                 if (g && this.ids[g][oDD.id]) {
15755                     delete this.ids[g][oDD.id];
15756                 }
15757             }
15758             delete this.handleIds[oDD.id];
15759         },
15760
15761         /**
15762          * Each DragDrop handle element must be registered.  This is done
15763          * automatically when executing DragDrop.setHandleElId()
15764          * @method regHandle
15765          * @param {String} sDDId the DragDrop id this element is a handle for
15766          * @param {String} sHandleId the id of the element that is the drag
15767          * handle
15768          * @static
15769          */
15770         regHandle: function(sDDId, sHandleId) {
15771             if (!this.handleIds[sDDId]) {
15772                 this.handleIds[sDDId] = {};
15773             }
15774             this.handleIds[sDDId][sHandleId] = sHandleId;
15775         },
15776
15777         /**
15778          * Utility function to determine if a given element has been
15779          * registered as a drag drop item.
15780          * @method isDragDrop
15781          * @param {String} id the element id to check
15782          * @return {boolean} true if this element is a DragDrop item,
15783          * false otherwise
15784          * @static
15785          */
15786         isDragDrop: function(id) {
15787             return ( this.getDDById(id) ) ? true : false;
15788         },
15789
15790         /**
15791          * Returns the drag and drop instances that are in all groups the
15792          * passed in instance belongs to.
15793          * @method getRelated
15794          * @param {DragDrop} p_oDD the obj to get related data for
15795          * @param {boolean} bTargetsOnly if true, only return targetable objs
15796          * @return {DragDrop[]} the related instances
15797          * @static
15798          */
15799         getRelated: function(p_oDD, bTargetsOnly) {
15800             var oDDs = [];
15801             for (var i in p_oDD.groups) {
15802                 for (j in this.ids[i]) {
15803                     var dd = this.ids[i][j];
15804                     if (! this.isTypeOfDD(dd)) {
15805                         continue;
15806                     }
15807                     if (!bTargetsOnly || dd.isTarget) {
15808                         oDDs[oDDs.length] = dd;
15809                     }
15810                 }
15811             }
15812
15813             return oDDs;
15814         },
15815
15816         /**
15817          * Returns true if the specified dd target is a legal target for
15818          * the specifice drag obj
15819          * @method isLegalTarget
15820          * @param {DragDrop} the drag obj
15821          * @param {DragDrop} the target
15822          * @return {boolean} true if the target is a legal target for the
15823          * dd obj
15824          * @static
15825          */
15826         isLegalTarget: function (oDD, oTargetDD) {
15827             var targets = this.getRelated(oDD, true);
15828             for (var i=0, len=targets.length;i<len;++i) {
15829                 if (targets[i].id == oTargetDD.id) {
15830                     return true;
15831                 }
15832             }
15833
15834             return false;
15835         },
15836
15837         /**
15838          * My goal is to be able to transparently determine if an object is
15839          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15840          * returns "object", oDD.constructor.toString() always returns
15841          * "DragDrop" and not the name of the subclass.  So for now it just
15842          * evaluates a well-known variable in DragDrop.
15843          * @method isTypeOfDD
15844          * @param {Object} the object to evaluate
15845          * @return {boolean} true if typeof oDD = DragDrop
15846          * @static
15847          */
15848         isTypeOfDD: function (oDD) {
15849             return (oDD && oDD.__ygDragDrop);
15850         },
15851
15852         /**
15853          * Utility function to determine if a given element has been
15854          * registered as a drag drop handle for the given Drag Drop object.
15855          * @method isHandle
15856          * @param {String} id the element id to check
15857          * @return {boolean} true if this element is a DragDrop handle, false
15858          * otherwise
15859          * @static
15860          */
15861         isHandle: function(sDDId, sHandleId) {
15862             return ( this.handleIds[sDDId] &&
15863                             this.handleIds[sDDId][sHandleId] );
15864         },
15865
15866         /**
15867          * Returns the DragDrop instance for a given id
15868          * @method getDDById
15869          * @param {String} id the id of the DragDrop object
15870          * @return {DragDrop} the drag drop object, null if it is not found
15871          * @static
15872          */
15873         getDDById: function(id) {
15874             for (var i in this.ids) {
15875                 if (this.ids[i][id]) {
15876                     return this.ids[i][id];
15877                 }
15878             }
15879             return null;
15880         },
15881
15882         /**
15883          * Fired after a registered DragDrop object gets the mousedown event.
15884          * Sets up the events required to track the object being dragged
15885          * @method handleMouseDown
15886          * @param {Event} e the event
15887          * @param oDD the DragDrop object being dragged
15888          * @private
15889          * @static
15890          */
15891         handleMouseDown: function(e, oDD) {
15892             if(Roo.QuickTips){
15893                 Roo.QuickTips.disable();
15894             }
15895             this.currentTarget = e.getTarget();
15896
15897             this.dragCurrent = oDD;
15898
15899             var el = oDD.getEl();
15900
15901             // track start position
15902             this.startX = e.getPageX();
15903             this.startY = e.getPageY();
15904
15905             this.deltaX = this.startX - el.offsetLeft;
15906             this.deltaY = this.startY - el.offsetTop;
15907
15908             this.dragThreshMet = false;
15909
15910             this.clickTimeout = setTimeout(
15911                     function() {
15912                         var DDM = Roo.dd.DDM;
15913                         DDM.startDrag(DDM.startX, DDM.startY);
15914                     },
15915                     this.clickTimeThresh );
15916         },
15917
15918         /**
15919          * Fired when either the drag pixel threshol or the mousedown hold
15920          * time threshold has been met.
15921          * @method startDrag
15922          * @param x {int} the X position of the original mousedown
15923          * @param y {int} the Y position of the original mousedown
15924          * @static
15925          */
15926         startDrag: function(x, y) {
15927             clearTimeout(this.clickTimeout);
15928             if (this.dragCurrent) {
15929                 this.dragCurrent.b4StartDrag(x, y);
15930                 this.dragCurrent.startDrag(x, y);
15931             }
15932             this.dragThreshMet = true;
15933         },
15934
15935         /**
15936          * Internal function to handle the mouseup event.  Will be invoked
15937          * from the context of the document.
15938          * @method handleMouseUp
15939          * @param {Event} e the event
15940          * @private
15941          * @static
15942          */
15943         handleMouseUp: function(e) {
15944
15945             if(Roo.QuickTips){
15946                 Roo.QuickTips.enable();
15947             }
15948             if (! this.dragCurrent) {
15949                 return;
15950             }
15951
15952             clearTimeout(this.clickTimeout);
15953
15954             if (this.dragThreshMet) {
15955                 this.fireEvents(e, true);
15956             } else {
15957             }
15958
15959             this.stopDrag(e);
15960
15961             this.stopEvent(e);
15962         },
15963
15964         /**
15965          * Utility to stop event propagation and event default, if these
15966          * features are turned on.
15967          * @method stopEvent
15968          * @param {Event} e the event as returned by this.getEvent()
15969          * @static
15970          */
15971         stopEvent: function(e){
15972             if(this.stopPropagation) {
15973                 e.stopPropagation();
15974             }
15975
15976             if (this.preventDefault) {
15977                 e.preventDefault();
15978             }
15979         },
15980
15981         /**
15982          * Internal function to clean up event handlers after the drag
15983          * operation is complete
15984          * @method stopDrag
15985          * @param {Event} e the event
15986          * @private
15987          * @static
15988          */
15989         stopDrag: function(e) {
15990             // Fire the drag end event for the item that was dragged
15991             if (this.dragCurrent) {
15992                 if (this.dragThreshMet) {
15993                     this.dragCurrent.b4EndDrag(e);
15994                     this.dragCurrent.endDrag(e);
15995                 }
15996
15997                 this.dragCurrent.onMouseUp(e);
15998             }
15999
16000             this.dragCurrent = null;
16001             this.dragOvers = {};
16002         },
16003
16004         /**
16005          * Internal function to handle the mousemove event.  Will be invoked
16006          * from the context of the html element.
16007          *
16008          * @TODO figure out what we can do about mouse events lost when the
16009          * user drags objects beyond the window boundary.  Currently we can
16010          * detect this in internet explorer by verifying that the mouse is
16011          * down during the mousemove event.  Firefox doesn't give us the
16012          * button state on the mousemove event.
16013          * @method handleMouseMove
16014          * @param {Event} e the event
16015          * @private
16016          * @static
16017          */
16018         handleMouseMove: function(e) {
16019             if (! this.dragCurrent) {
16020                 return true;
16021             }
16022
16023             // var button = e.which || e.button;
16024
16025             // check for IE mouseup outside of page boundary
16026             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16027                 this.stopEvent(e);
16028                 return this.handleMouseUp(e);
16029             }
16030
16031             if (!this.dragThreshMet) {
16032                 var diffX = Math.abs(this.startX - e.getPageX());
16033                 var diffY = Math.abs(this.startY - e.getPageY());
16034                 if (diffX > this.clickPixelThresh ||
16035                             diffY > this.clickPixelThresh) {
16036                     this.startDrag(this.startX, this.startY);
16037                 }
16038             }
16039
16040             if (this.dragThreshMet) {
16041                 this.dragCurrent.b4Drag(e);
16042                 this.dragCurrent.onDrag(e);
16043                 if(!this.dragCurrent.moveOnly){
16044                     this.fireEvents(e, false);
16045                 }
16046             }
16047
16048             this.stopEvent(e);
16049
16050             return true;
16051         },
16052
16053         /**
16054          * Iterates over all of the DragDrop elements to find ones we are
16055          * hovering over or dropping on
16056          * @method fireEvents
16057          * @param {Event} e the event
16058          * @param {boolean} isDrop is this a drop op or a mouseover op?
16059          * @private
16060          * @static
16061          */
16062         fireEvents: function(e, isDrop) {
16063             var dc = this.dragCurrent;
16064
16065             // If the user did the mouse up outside of the window, we could
16066             // get here even though we have ended the drag.
16067             if (!dc || dc.isLocked()) {
16068                 return;
16069             }
16070
16071             var pt = e.getPoint();
16072
16073             // cache the previous dragOver array
16074             var oldOvers = [];
16075
16076             var outEvts   = [];
16077             var overEvts  = [];
16078             var dropEvts  = [];
16079             var enterEvts = [];
16080
16081             // Check to see if the object(s) we were hovering over is no longer
16082             // being hovered over so we can fire the onDragOut event
16083             for (var i in this.dragOvers) {
16084
16085                 var ddo = this.dragOvers[i];
16086
16087                 if (! this.isTypeOfDD(ddo)) {
16088                     continue;
16089                 }
16090
16091                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16092                     outEvts.push( ddo );
16093                 }
16094
16095                 oldOvers[i] = true;
16096                 delete this.dragOvers[i];
16097             }
16098
16099             for (var sGroup in dc.groups) {
16100
16101                 if ("string" != typeof sGroup) {
16102                     continue;
16103                 }
16104
16105                 for (i in this.ids[sGroup]) {
16106                     var oDD = this.ids[sGroup][i];
16107                     if (! this.isTypeOfDD(oDD)) {
16108                         continue;
16109                     }
16110
16111                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16112                         if (this.isOverTarget(pt, oDD, this.mode)) {
16113                             // look for drop interactions
16114                             if (isDrop) {
16115                                 dropEvts.push( oDD );
16116                             // look for drag enter and drag over interactions
16117                             } else {
16118
16119                                 // initial drag over: dragEnter fires
16120                                 if (!oldOvers[oDD.id]) {
16121                                     enterEvts.push( oDD );
16122                                 // subsequent drag overs: dragOver fires
16123                                 } else {
16124                                     overEvts.push( oDD );
16125                                 }
16126
16127                                 this.dragOvers[oDD.id] = oDD;
16128                             }
16129                         }
16130                     }
16131                 }
16132             }
16133
16134             if (this.mode) {
16135                 if (outEvts.length) {
16136                     dc.b4DragOut(e, outEvts);
16137                     dc.onDragOut(e, outEvts);
16138                 }
16139
16140                 if (enterEvts.length) {
16141                     dc.onDragEnter(e, enterEvts);
16142                 }
16143
16144                 if (overEvts.length) {
16145                     dc.b4DragOver(e, overEvts);
16146                     dc.onDragOver(e, overEvts);
16147                 }
16148
16149                 if (dropEvts.length) {
16150                     dc.b4DragDrop(e, dropEvts);
16151                     dc.onDragDrop(e, dropEvts);
16152                 }
16153
16154             } else {
16155                 // fire dragout events
16156                 var len = 0;
16157                 for (i=0, len=outEvts.length; i<len; ++i) {
16158                     dc.b4DragOut(e, outEvts[i].id);
16159                     dc.onDragOut(e, outEvts[i].id);
16160                 }
16161
16162                 // fire enter events
16163                 for (i=0,len=enterEvts.length; i<len; ++i) {
16164                     // dc.b4DragEnter(e, oDD.id);
16165                     dc.onDragEnter(e, enterEvts[i].id);
16166                 }
16167
16168                 // fire over events
16169                 for (i=0,len=overEvts.length; i<len; ++i) {
16170                     dc.b4DragOver(e, overEvts[i].id);
16171                     dc.onDragOver(e, overEvts[i].id);
16172                 }
16173
16174                 // fire drop events
16175                 for (i=0, len=dropEvts.length; i<len; ++i) {
16176                     dc.b4DragDrop(e, dropEvts[i].id);
16177                     dc.onDragDrop(e, dropEvts[i].id);
16178                 }
16179
16180             }
16181
16182             // notify about a drop that did not find a target
16183             if (isDrop && !dropEvts.length) {
16184                 dc.onInvalidDrop(e);
16185             }
16186
16187         },
16188
16189         /**
16190          * Helper function for getting the best match from the list of drag
16191          * and drop objects returned by the drag and drop events when we are
16192          * in INTERSECT mode.  It returns either the first object that the
16193          * cursor is over, or the object that has the greatest overlap with
16194          * the dragged element.
16195          * @method getBestMatch
16196          * @param  {DragDrop[]} dds The array of drag and drop objects
16197          * targeted
16198          * @return {DragDrop}       The best single match
16199          * @static
16200          */
16201         getBestMatch: function(dds) {
16202             var winner = null;
16203             // Return null if the input is not what we expect
16204             //if (!dds || !dds.length || dds.length == 0) {
16205                // winner = null;
16206             // If there is only one item, it wins
16207             //} else if (dds.length == 1) {
16208
16209             var len = dds.length;
16210
16211             if (len == 1) {
16212                 winner = dds[0];
16213             } else {
16214                 // Loop through the targeted items
16215                 for (var i=0; i<len; ++i) {
16216                     var dd = dds[i];
16217                     // If the cursor is over the object, it wins.  If the
16218                     // cursor is over multiple matches, the first one we come
16219                     // to wins.
16220                     if (dd.cursorIsOver) {
16221                         winner = dd;
16222                         break;
16223                     // Otherwise the object with the most overlap wins
16224                     } else {
16225                         if (!winner ||
16226                             winner.overlap.getArea() < dd.overlap.getArea()) {
16227                             winner = dd;
16228                         }
16229                     }
16230                 }
16231             }
16232
16233             return winner;
16234         },
16235
16236         /**
16237          * Refreshes the cache of the top-left and bottom-right points of the
16238          * drag and drop objects in the specified group(s).  This is in the
16239          * format that is stored in the drag and drop instance, so typical
16240          * usage is:
16241          * <code>
16242          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16243          * </code>
16244          * Alternatively:
16245          * <code>
16246          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16247          * </code>
16248          * @TODO this really should be an indexed array.  Alternatively this
16249          * method could accept both.
16250          * @method refreshCache
16251          * @param {Object} groups an associative array of groups to refresh
16252          * @static
16253          */
16254         refreshCache: function(groups) {
16255             for (var sGroup in groups) {
16256                 if ("string" != typeof sGroup) {
16257                     continue;
16258                 }
16259                 for (var i in this.ids[sGroup]) {
16260                     var oDD = this.ids[sGroup][i];
16261
16262                     if (this.isTypeOfDD(oDD)) {
16263                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16264                         var loc = this.getLocation(oDD);
16265                         if (loc) {
16266                             this.locationCache[oDD.id] = loc;
16267                         } else {
16268                             delete this.locationCache[oDD.id];
16269                             // this will unregister the drag and drop object if
16270                             // the element is not in a usable state
16271                             // oDD.unreg();
16272                         }
16273                     }
16274                 }
16275             }
16276         },
16277
16278         /**
16279          * This checks to make sure an element exists and is in the DOM.  The
16280          * main purpose is to handle cases where innerHTML is used to remove
16281          * drag and drop objects from the DOM.  IE provides an 'unspecified
16282          * error' when trying to access the offsetParent of such an element
16283          * @method verifyEl
16284          * @param {HTMLElement} el the element to check
16285          * @return {boolean} true if the element looks usable
16286          * @static
16287          */
16288         verifyEl: function(el) {
16289             if (el) {
16290                 var parent;
16291                 if(Roo.isIE){
16292                     try{
16293                         parent = el.offsetParent;
16294                     }catch(e){}
16295                 }else{
16296                     parent = el.offsetParent;
16297                 }
16298                 if (parent) {
16299                     return true;
16300                 }
16301             }
16302
16303             return false;
16304         },
16305
16306         /**
16307          * Returns a Region object containing the drag and drop element's position
16308          * and size, including the padding configured for it
16309          * @method getLocation
16310          * @param {DragDrop} oDD the drag and drop object to get the
16311          *                       location for
16312          * @return {Roo.lib.Region} a Region object representing the total area
16313          *                             the element occupies, including any padding
16314          *                             the instance is configured for.
16315          * @static
16316          */
16317         getLocation: function(oDD) {
16318             if (! this.isTypeOfDD(oDD)) {
16319                 return null;
16320             }
16321
16322             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16323
16324             try {
16325                 pos= Roo.lib.Dom.getXY(el);
16326             } catch (e) { }
16327
16328             if (!pos) {
16329                 return null;
16330             }
16331
16332             x1 = pos[0];
16333             x2 = x1 + el.offsetWidth;
16334             y1 = pos[1];
16335             y2 = y1 + el.offsetHeight;
16336
16337             t = y1 - oDD.padding[0];
16338             r = x2 + oDD.padding[1];
16339             b = y2 + oDD.padding[2];
16340             l = x1 - oDD.padding[3];
16341
16342             return new Roo.lib.Region( t, r, b, l );
16343         },
16344
16345         /**
16346          * Checks the cursor location to see if it over the target
16347          * @method isOverTarget
16348          * @param {Roo.lib.Point} pt The point to evaluate
16349          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16350          * @return {boolean} true if the mouse is over the target
16351          * @private
16352          * @static
16353          */
16354         isOverTarget: function(pt, oTarget, intersect) {
16355             // use cache if available
16356             var loc = this.locationCache[oTarget.id];
16357             if (!loc || !this.useCache) {
16358                 loc = this.getLocation(oTarget);
16359                 this.locationCache[oTarget.id] = loc;
16360
16361             }
16362
16363             if (!loc) {
16364                 return false;
16365             }
16366
16367             oTarget.cursorIsOver = loc.contains( pt );
16368
16369             // DragDrop is using this as a sanity check for the initial mousedown
16370             // in this case we are done.  In POINT mode, if the drag obj has no
16371             // contraints, we are also done. Otherwise we need to evaluate the
16372             // location of the target as related to the actual location of the
16373             // dragged element.
16374             var dc = this.dragCurrent;
16375             if (!dc || !dc.getTargetCoord ||
16376                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16377                 return oTarget.cursorIsOver;
16378             }
16379
16380             oTarget.overlap = null;
16381
16382             // Get the current location of the drag element, this is the
16383             // location of the mouse event less the delta that represents
16384             // where the original mousedown happened on the element.  We
16385             // need to consider constraints and ticks as well.
16386             var pos = dc.getTargetCoord(pt.x, pt.y);
16387
16388             var el = dc.getDragEl();
16389             var curRegion = new Roo.lib.Region( pos.y,
16390                                                    pos.x + el.offsetWidth,
16391                                                    pos.y + el.offsetHeight,
16392                                                    pos.x );
16393
16394             var overlap = curRegion.intersect(loc);
16395
16396             if (overlap) {
16397                 oTarget.overlap = overlap;
16398                 return (intersect) ? true : oTarget.cursorIsOver;
16399             } else {
16400                 return false;
16401             }
16402         },
16403
16404         /**
16405          * unload event handler
16406          * @method _onUnload
16407          * @private
16408          * @static
16409          */
16410         _onUnload: function(e, me) {
16411             Roo.dd.DragDropMgr.unregAll();
16412         },
16413
16414         /**
16415          * Cleans up the drag and drop events and objects.
16416          * @method unregAll
16417          * @private
16418          * @static
16419          */
16420         unregAll: function() {
16421
16422             if (this.dragCurrent) {
16423                 this.stopDrag();
16424                 this.dragCurrent = null;
16425             }
16426
16427             this._execOnAll("unreg", []);
16428
16429             for (i in this.elementCache) {
16430                 delete this.elementCache[i];
16431             }
16432
16433             this.elementCache = {};
16434             this.ids = {};
16435         },
16436
16437         /**
16438          * A cache of DOM elements
16439          * @property elementCache
16440          * @private
16441          * @static
16442          */
16443         elementCache: {},
16444
16445         /**
16446          * Get the wrapper for the DOM element specified
16447          * @method getElWrapper
16448          * @param {String} id the id of the element to get
16449          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16450          * @private
16451          * @deprecated This wrapper isn't that useful
16452          * @static
16453          */
16454         getElWrapper: function(id) {
16455             var oWrapper = this.elementCache[id];
16456             if (!oWrapper || !oWrapper.el) {
16457                 oWrapper = this.elementCache[id] =
16458                     new this.ElementWrapper(Roo.getDom(id));
16459             }
16460             return oWrapper;
16461         },
16462
16463         /**
16464          * Returns the actual DOM element
16465          * @method getElement
16466          * @param {String} id the id of the elment to get
16467          * @return {Object} The element
16468          * @deprecated use Roo.getDom instead
16469          * @static
16470          */
16471         getElement: function(id) {
16472             return Roo.getDom(id);
16473         },
16474
16475         /**
16476          * Returns the style property for the DOM element (i.e.,
16477          * document.getElById(id).style)
16478          * @method getCss
16479          * @param {String} id the id of the elment to get
16480          * @return {Object} The style property of the element
16481          * @deprecated use Roo.getDom instead
16482          * @static
16483          */
16484         getCss: function(id) {
16485             var el = Roo.getDom(id);
16486             return (el) ? el.style : null;
16487         },
16488
16489         /**
16490          * Inner class for cached elements
16491          * @class DragDropMgr.ElementWrapper
16492          * @for DragDropMgr
16493          * @private
16494          * @deprecated
16495          */
16496         ElementWrapper: function(el) {
16497                 /**
16498                  * The element
16499                  * @property el
16500                  */
16501                 this.el = el || null;
16502                 /**
16503                  * The element id
16504                  * @property id
16505                  */
16506                 this.id = this.el && el.id;
16507                 /**
16508                  * A reference to the style property
16509                  * @property css
16510                  */
16511                 this.css = this.el && el.style;
16512             },
16513
16514         /**
16515          * Returns the X position of an html element
16516          * @method getPosX
16517          * @param el the element for which to get the position
16518          * @return {int} the X coordinate
16519          * @for DragDropMgr
16520          * @deprecated use Roo.lib.Dom.getX instead
16521          * @static
16522          */
16523         getPosX: function(el) {
16524             return Roo.lib.Dom.getX(el);
16525         },
16526
16527         /**
16528          * Returns the Y position of an html element
16529          * @method getPosY
16530          * @param el the element for which to get the position
16531          * @return {int} the Y coordinate
16532          * @deprecated use Roo.lib.Dom.getY instead
16533          * @static
16534          */
16535         getPosY: function(el) {
16536             return Roo.lib.Dom.getY(el);
16537         },
16538
16539         /**
16540          * Swap two nodes.  In IE, we use the native method, for others we
16541          * emulate the IE behavior
16542          * @method swapNode
16543          * @param n1 the first node to swap
16544          * @param n2 the other node to swap
16545          * @static
16546          */
16547         swapNode: function(n1, n2) {
16548             if (n1.swapNode) {
16549                 n1.swapNode(n2);
16550             } else {
16551                 var p = n2.parentNode;
16552                 var s = n2.nextSibling;
16553
16554                 if (s == n1) {
16555                     p.insertBefore(n1, n2);
16556                 } else if (n2 == n1.nextSibling) {
16557                     p.insertBefore(n2, n1);
16558                 } else {
16559                     n1.parentNode.replaceChild(n2, n1);
16560                     p.insertBefore(n1, s);
16561                 }
16562             }
16563         },
16564
16565         /**
16566          * Returns the current scroll position
16567          * @method getScroll
16568          * @private
16569          * @static
16570          */
16571         getScroll: function () {
16572             var t, l, dde=document.documentElement, db=document.body;
16573             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16574                 t = dde.scrollTop;
16575                 l = dde.scrollLeft;
16576             } else if (db) {
16577                 t = db.scrollTop;
16578                 l = db.scrollLeft;
16579             } else {
16580
16581             }
16582             return { top: t, left: l };
16583         },
16584
16585         /**
16586          * Returns the specified element style property
16587          * @method getStyle
16588          * @param {HTMLElement} el          the element
16589          * @param {string}      styleProp   the style property
16590          * @return {string} The value of the style property
16591          * @deprecated use Roo.lib.Dom.getStyle
16592          * @static
16593          */
16594         getStyle: function(el, styleProp) {
16595             return Roo.fly(el).getStyle(styleProp);
16596         },
16597
16598         /**
16599          * Gets the scrollTop
16600          * @method getScrollTop
16601          * @return {int} the document's scrollTop
16602          * @static
16603          */
16604         getScrollTop: function () { return this.getScroll().top; },
16605
16606         /**
16607          * Gets the scrollLeft
16608          * @method getScrollLeft
16609          * @return {int} the document's scrollTop
16610          * @static
16611          */
16612         getScrollLeft: function () { return this.getScroll().left; },
16613
16614         /**
16615          * Sets the x/y position of an element to the location of the
16616          * target element.
16617          * @method moveToEl
16618          * @param {HTMLElement} moveEl      The element to move
16619          * @param {HTMLElement} targetEl    The position reference element
16620          * @static
16621          */
16622         moveToEl: function (moveEl, targetEl) {
16623             var aCoord = Roo.lib.Dom.getXY(targetEl);
16624             Roo.lib.Dom.setXY(moveEl, aCoord);
16625         },
16626
16627         /**
16628          * Numeric array sort function
16629          * @method numericSort
16630          * @static
16631          */
16632         numericSort: function(a, b) { return (a - b); },
16633
16634         /**
16635          * Internal counter
16636          * @property _timeoutCount
16637          * @private
16638          * @static
16639          */
16640         _timeoutCount: 0,
16641
16642         /**
16643          * Trying to make the load order less important.  Without this we get
16644          * an error if this file is loaded before the Event Utility.
16645          * @method _addListeners
16646          * @private
16647          * @static
16648          */
16649         _addListeners: function() {
16650             var DDM = Roo.dd.DDM;
16651             if ( Roo.lib.Event && document ) {
16652                 DDM._onLoad();
16653             } else {
16654                 if (DDM._timeoutCount > 2000) {
16655                 } else {
16656                     setTimeout(DDM._addListeners, 10);
16657                     if (document && document.body) {
16658                         DDM._timeoutCount += 1;
16659                     }
16660                 }
16661             }
16662         },
16663
16664         /**
16665          * Recursively searches the immediate parent and all child nodes for
16666          * the handle element in order to determine wheter or not it was
16667          * clicked.
16668          * @method handleWasClicked
16669          * @param node the html element to inspect
16670          * @static
16671          */
16672         handleWasClicked: function(node, id) {
16673             if (this.isHandle(id, node.id)) {
16674                 return true;
16675             } else {
16676                 // check to see if this is a text node child of the one we want
16677                 var p = node.parentNode;
16678
16679                 while (p) {
16680                     if (this.isHandle(id, p.id)) {
16681                         return true;
16682                     } else {
16683                         p = p.parentNode;
16684                     }
16685                 }
16686             }
16687
16688             return false;
16689         }
16690
16691     };
16692
16693 }();
16694
16695 // shorter alias, save a few bytes
16696 Roo.dd.DDM = Roo.dd.DragDropMgr;
16697 Roo.dd.DDM._addListeners();
16698
16699 }/*
16700  * Based on:
16701  * Ext JS Library 1.1.1
16702  * Copyright(c) 2006-2007, Ext JS, LLC.
16703  *
16704  * Originally Released Under LGPL - original licence link has changed is not relivant.
16705  *
16706  * Fork - LGPL
16707  * <script type="text/javascript">
16708  */
16709
16710 /**
16711  * @class Roo.dd.DD
16712  * A DragDrop implementation where the linked element follows the
16713  * mouse cursor during a drag.
16714  * @extends Roo.dd.DragDrop
16715  * @constructor
16716  * @param {String} id the id of the linked element
16717  * @param {String} sGroup the group of related DragDrop items
16718  * @param {object} config an object containing configurable attributes
16719  *                Valid properties for DD:
16720  *                    scroll
16721  */
16722 Roo.dd.DD = function(id, sGroup, config) {
16723     if (id) {
16724         this.init(id, sGroup, config);
16725     }
16726 };
16727
16728 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16729
16730     /**
16731      * When set to true, the utility automatically tries to scroll the browser
16732      * window wehn a drag and drop element is dragged near the viewport boundary.
16733      * Defaults to true.
16734      * @property scroll
16735      * @type boolean
16736      */
16737     scroll: true,
16738
16739     /**
16740      * Sets the pointer offset to the distance between the linked element's top
16741      * left corner and the location the element was clicked
16742      * @method autoOffset
16743      * @param {int} iPageX the X coordinate of the click
16744      * @param {int} iPageY the Y coordinate of the click
16745      */
16746     autoOffset: function(iPageX, iPageY) {
16747         var x = iPageX - this.startPageX;
16748         var y = iPageY - this.startPageY;
16749         this.setDelta(x, y);
16750     },
16751
16752     /**
16753      * Sets the pointer offset.  You can call this directly to force the
16754      * offset to be in a particular location (e.g., pass in 0,0 to set it
16755      * to the center of the object)
16756      * @method setDelta
16757      * @param {int} iDeltaX the distance from the left
16758      * @param {int} iDeltaY the distance from the top
16759      */
16760     setDelta: function(iDeltaX, iDeltaY) {
16761         this.deltaX = iDeltaX;
16762         this.deltaY = iDeltaY;
16763     },
16764
16765     /**
16766      * Sets the drag element to the location of the mousedown or click event,
16767      * maintaining the cursor location relative to the location on the element
16768      * that was clicked.  Override this if you want to place the element in a
16769      * location other than where the cursor is.
16770      * @method setDragElPos
16771      * @param {int} iPageX the X coordinate of the mousedown or drag event
16772      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16773      */
16774     setDragElPos: function(iPageX, iPageY) {
16775         // the first time we do this, we are going to check to make sure
16776         // the element has css positioning
16777
16778         var el = this.getDragEl();
16779         this.alignElWithMouse(el, iPageX, iPageY);
16780     },
16781
16782     /**
16783      * Sets the element to the location of the mousedown or click event,
16784      * maintaining the cursor location relative to the location on the element
16785      * that was clicked.  Override this if you want to place the element in a
16786      * location other than where the cursor is.
16787      * @method alignElWithMouse
16788      * @param {HTMLElement} el the element to move
16789      * @param {int} iPageX the X coordinate of the mousedown or drag event
16790      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16791      */
16792     alignElWithMouse: function(el, iPageX, iPageY) {
16793         var oCoord = this.getTargetCoord(iPageX, iPageY);
16794         var fly = el.dom ? el : Roo.fly(el);
16795         if (!this.deltaSetXY) {
16796             var aCoord = [oCoord.x, oCoord.y];
16797             fly.setXY(aCoord);
16798             var newLeft = fly.getLeft(true);
16799             var newTop  = fly.getTop(true);
16800             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16801         } else {
16802             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16803         }
16804
16805         this.cachePosition(oCoord.x, oCoord.y);
16806         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16807         return oCoord;
16808     },
16809
16810     /**
16811      * Saves the most recent position so that we can reset the constraints and
16812      * tick marks on-demand.  We need to know this so that we can calculate the
16813      * number of pixels the element is offset from its original position.
16814      * @method cachePosition
16815      * @param iPageX the current x position (optional, this just makes it so we
16816      * don't have to look it up again)
16817      * @param iPageY the current y position (optional, this just makes it so we
16818      * don't have to look it up again)
16819      */
16820     cachePosition: function(iPageX, iPageY) {
16821         if (iPageX) {
16822             this.lastPageX = iPageX;
16823             this.lastPageY = iPageY;
16824         } else {
16825             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16826             this.lastPageX = aCoord[0];
16827             this.lastPageY = aCoord[1];
16828         }
16829     },
16830
16831     /**
16832      * Auto-scroll the window if the dragged object has been moved beyond the
16833      * visible window boundary.
16834      * @method autoScroll
16835      * @param {int} x the drag element's x position
16836      * @param {int} y the drag element's y position
16837      * @param {int} h the height of the drag element
16838      * @param {int} w the width of the drag element
16839      * @private
16840      */
16841     autoScroll: function(x, y, h, w) {
16842
16843         if (this.scroll) {
16844             // The client height
16845             var clientH = Roo.lib.Dom.getViewWidth();
16846
16847             // The client width
16848             var clientW = Roo.lib.Dom.getViewHeight();
16849
16850             // The amt scrolled down
16851             var st = this.DDM.getScrollTop();
16852
16853             // The amt scrolled right
16854             var sl = this.DDM.getScrollLeft();
16855
16856             // Location of the bottom of the element
16857             var bot = h + y;
16858
16859             // Location of the right of the element
16860             var right = w + x;
16861
16862             // The distance from the cursor to the bottom of the visible area,
16863             // adjusted so that we don't scroll if the cursor is beyond the
16864             // element drag constraints
16865             var toBot = (clientH + st - y - this.deltaY);
16866
16867             // The distance from the cursor to the right of the visible area
16868             var toRight = (clientW + sl - x - this.deltaX);
16869
16870
16871             // How close to the edge the cursor must be before we scroll
16872             // var thresh = (document.all) ? 100 : 40;
16873             var thresh = 40;
16874
16875             // How many pixels to scroll per autoscroll op.  This helps to reduce
16876             // clunky scrolling. IE is more sensitive about this ... it needs this
16877             // value to be higher.
16878             var scrAmt = (document.all) ? 80 : 30;
16879
16880             // Scroll down if we are near the bottom of the visible page and the
16881             // obj extends below the crease
16882             if ( bot > clientH && toBot < thresh ) {
16883                 window.scrollTo(sl, st + scrAmt);
16884             }
16885
16886             // Scroll up if the window is scrolled down and the top of the object
16887             // goes above the top border
16888             if ( y < st && st > 0 && y - st < thresh ) {
16889                 window.scrollTo(sl, st - scrAmt);
16890             }
16891
16892             // Scroll right if the obj is beyond the right border and the cursor is
16893             // near the border.
16894             if ( right > clientW && toRight < thresh ) {
16895                 window.scrollTo(sl + scrAmt, st);
16896             }
16897
16898             // Scroll left if the window has been scrolled to the right and the obj
16899             // extends past the left border
16900             if ( x < sl && sl > 0 && x - sl < thresh ) {
16901                 window.scrollTo(sl - scrAmt, st);
16902             }
16903         }
16904     },
16905
16906     /**
16907      * Finds the location the element should be placed if we want to move
16908      * it to where the mouse location less the click offset would place us.
16909      * @method getTargetCoord
16910      * @param {int} iPageX the X coordinate of the click
16911      * @param {int} iPageY the Y coordinate of the click
16912      * @return an object that contains the coordinates (Object.x and Object.y)
16913      * @private
16914      */
16915     getTargetCoord: function(iPageX, iPageY) {
16916
16917
16918         var x = iPageX - this.deltaX;
16919         var y = iPageY - this.deltaY;
16920
16921         if (this.constrainX) {
16922             if (x < this.minX) { x = this.minX; }
16923             if (x > this.maxX) { x = this.maxX; }
16924         }
16925
16926         if (this.constrainY) {
16927             if (y < this.minY) { y = this.minY; }
16928             if (y > this.maxY) { y = this.maxY; }
16929         }
16930
16931         x = this.getTick(x, this.xTicks);
16932         y = this.getTick(y, this.yTicks);
16933
16934
16935         return {x:x, y:y};
16936     },
16937
16938     /*
16939      * Sets up config options specific to this class. Overrides
16940      * Roo.dd.DragDrop, but all versions of this method through the
16941      * inheritance chain are called
16942      */
16943     applyConfig: function() {
16944         Roo.dd.DD.superclass.applyConfig.call(this);
16945         this.scroll = (this.config.scroll !== false);
16946     },
16947
16948     /*
16949      * Event that fires prior to the onMouseDown event.  Overrides
16950      * Roo.dd.DragDrop.
16951      */
16952     b4MouseDown: function(e) {
16953         // this.resetConstraints();
16954         this.autoOffset(e.getPageX(),
16955                             e.getPageY());
16956     },
16957
16958     /*
16959      * Event that fires prior to the onDrag event.  Overrides
16960      * Roo.dd.DragDrop.
16961      */
16962     b4Drag: function(e) {
16963         this.setDragElPos(e.getPageX(),
16964                             e.getPageY());
16965     },
16966
16967     toString: function() {
16968         return ("DD " + this.id);
16969     }
16970
16971     //////////////////////////////////////////////////////////////////////////
16972     // Debugging ygDragDrop events that can be overridden
16973     //////////////////////////////////////////////////////////////////////////
16974     /*
16975     startDrag: function(x, y) {
16976     },
16977
16978     onDrag: function(e) {
16979     },
16980
16981     onDragEnter: function(e, id) {
16982     },
16983
16984     onDragOver: function(e, id) {
16985     },
16986
16987     onDragOut: function(e, id) {
16988     },
16989
16990     onDragDrop: function(e, id) {
16991     },
16992
16993     endDrag: function(e) {
16994     }
16995
16996     */
16997
16998 });/*
16999  * Based on:
17000  * Ext JS Library 1.1.1
17001  * Copyright(c) 2006-2007, Ext JS, LLC.
17002  *
17003  * Originally Released Under LGPL - original licence link has changed is not relivant.
17004  *
17005  * Fork - LGPL
17006  * <script type="text/javascript">
17007  */
17008
17009 /**
17010  * @class Roo.dd.DDProxy
17011  * A DragDrop implementation that inserts an empty, bordered div into
17012  * the document that follows the cursor during drag operations.  At the time of
17013  * the click, the frame div is resized to the dimensions of the linked html
17014  * element, and moved to the exact location of the linked element.
17015  *
17016  * References to the "frame" element refer to the single proxy element that
17017  * was created to be dragged in place of all DDProxy elements on the
17018  * page.
17019  *
17020  * @extends Roo.dd.DD
17021  * @constructor
17022  * @param {String} id the id of the linked html element
17023  * @param {String} sGroup the group of related DragDrop objects
17024  * @param {object} config an object containing configurable attributes
17025  *                Valid properties for DDProxy in addition to those in DragDrop:
17026  *                   resizeFrame, centerFrame, dragElId
17027  */
17028 Roo.dd.DDProxy = function(id, sGroup, config) {
17029     if (id) {
17030         this.init(id, sGroup, config);
17031         this.initFrame();
17032     }
17033 };
17034
17035 /**
17036  * The default drag frame div id
17037  * @property Roo.dd.DDProxy.dragElId
17038  * @type String
17039  * @static
17040  */
17041 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17042
17043 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17044
17045     /**
17046      * By default we resize the drag frame to be the same size as the element
17047      * we want to drag (this is to get the frame effect).  We can turn it off
17048      * if we want a different behavior.
17049      * @property resizeFrame
17050      * @type boolean
17051      */
17052     resizeFrame: true,
17053
17054     /**
17055      * By default the frame is positioned exactly where the drag element is, so
17056      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17057      * you do not have constraints on the obj is to have the drag frame centered
17058      * around the cursor.  Set centerFrame to true for this effect.
17059      * @property centerFrame
17060      * @type boolean
17061      */
17062     centerFrame: false,
17063
17064     /**
17065      * Creates the proxy element if it does not yet exist
17066      * @method createFrame
17067      */
17068     createFrame: function() {
17069         var self = this;
17070         var body = document.body;
17071
17072         if (!body || !body.firstChild) {
17073             setTimeout( function() { self.createFrame(); }, 50 );
17074             return;
17075         }
17076
17077         var div = this.getDragEl();
17078
17079         if (!div) {
17080             div    = document.createElement("div");
17081             div.id = this.dragElId;
17082             var s  = div.style;
17083
17084             s.position   = "absolute";
17085             s.visibility = "hidden";
17086             s.cursor     = "move";
17087             s.border     = "2px solid #aaa";
17088             s.zIndex     = 999;
17089
17090             // appendChild can blow up IE if invoked prior to the window load event
17091             // while rendering a table.  It is possible there are other scenarios
17092             // that would cause this to happen as well.
17093             body.insertBefore(div, body.firstChild);
17094         }
17095     },
17096
17097     /**
17098      * Initialization for the drag frame element.  Must be called in the
17099      * constructor of all subclasses
17100      * @method initFrame
17101      */
17102     initFrame: function() {
17103         this.createFrame();
17104     },
17105
17106     applyConfig: function() {
17107         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17108
17109         this.resizeFrame = (this.config.resizeFrame !== false);
17110         this.centerFrame = (this.config.centerFrame);
17111         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17112     },
17113
17114     /**
17115      * Resizes the drag frame to the dimensions of the clicked object, positions
17116      * it over the object, and finally displays it
17117      * @method showFrame
17118      * @param {int} iPageX X click position
17119      * @param {int} iPageY Y click position
17120      * @private
17121      */
17122     showFrame: function(iPageX, iPageY) {
17123         var el = this.getEl();
17124         var dragEl = this.getDragEl();
17125         var s = dragEl.style;
17126
17127         this._resizeProxy();
17128
17129         if (this.centerFrame) {
17130             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17131                            Math.round(parseInt(s.height, 10)/2) );
17132         }
17133
17134         this.setDragElPos(iPageX, iPageY);
17135
17136         Roo.fly(dragEl).show();
17137     },
17138
17139     /**
17140      * The proxy is automatically resized to the dimensions of the linked
17141      * element when a drag is initiated, unless resizeFrame is set to false
17142      * @method _resizeProxy
17143      * @private
17144      */
17145     _resizeProxy: function() {
17146         if (this.resizeFrame) {
17147             var el = this.getEl();
17148             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17149         }
17150     },
17151
17152     // overrides Roo.dd.DragDrop
17153     b4MouseDown: function(e) {
17154         var x = e.getPageX();
17155         var y = e.getPageY();
17156         this.autoOffset(x, y);
17157         this.setDragElPos(x, y);
17158     },
17159
17160     // overrides Roo.dd.DragDrop
17161     b4StartDrag: function(x, y) {
17162         // show the drag frame
17163         this.showFrame(x, y);
17164     },
17165
17166     // overrides Roo.dd.DragDrop
17167     b4EndDrag: function(e) {
17168         Roo.fly(this.getDragEl()).hide();
17169     },
17170
17171     // overrides Roo.dd.DragDrop
17172     // By default we try to move the element to the last location of the frame.
17173     // This is so that the default behavior mirrors that of Roo.dd.DD.
17174     endDrag: function(e) {
17175
17176         var lel = this.getEl();
17177         var del = this.getDragEl();
17178
17179         // Show the drag frame briefly so we can get its position
17180         del.style.visibility = "";
17181
17182         this.beforeMove();
17183         // Hide the linked element before the move to get around a Safari
17184         // rendering bug.
17185         lel.style.visibility = "hidden";
17186         Roo.dd.DDM.moveToEl(lel, del);
17187         del.style.visibility = "hidden";
17188         lel.style.visibility = "";
17189
17190         this.afterDrag();
17191     },
17192
17193     beforeMove : function(){
17194
17195     },
17196
17197     afterDrag : function(){
17198
17199     },
17200
17201     toString: function() {
17202         return ("DDProxy " + this.id);
17203     }
17204
17205 });
17206 /*
17207  * Based on:
17208  * Ext JS Library 1.1.1
17209  * Copyright(c) 2006-2007, Ext JS, LLC.
17210  *
17211  * Originally Released Under LGPL - original licence link has changed is not relivant.
17212  *
17213  * Fork - LGPL
17214  * <script type="text/javascript">
17215  */
17216
17217  /**
17218  * @class Roo.dd.DDTarget
17219  * A DragDrop implementation that does not move, but can be a drop
17220  * target.  You would get the same result by simply omitting implementation
17221  * for the event callbacks, but this way we reduce the processing cost of the
17222  * event listener and the callbacks.
17223  * @extends Roo.dd.DragDrop
17224  * @constructor
17225  * @param {String} id the id of the element that is a drop target
17226  * @param {String} sGroup the group of related DragDrop objects
17227  * @param {object} config an object containing configurable attributes
17228  *                 Valid properties for DDTarget in addition to those in
17229  *                 DragDrop:
17230  *                    none
17231  */
17232 Roo.dd.DDTarget = function(id, sGroup, config) {
17233     if (id) {
17234         this.initTarget(id, sGroup, config);
17235     }
17236 };
17237
17238 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17239 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17240     toString: function() {
17241         return ("DDTarget " + this.id);
17242     }
17243 });
17244 /*
17245  * Based on:
17246  * Ext JS Library 1.1.1
17247  * Copyright(c) 2006-2007, Ext JS, LLC.
17248  *
17249  * Originally Released Under LGPL - original licence link has changed is not relivant.
17250  *
17251  * Fork - LGPL
17252  * <script type="text/javascript">
17253  */
17254  
17255
17256 /**
17257  * @class Roo.dd.ScrollManager
17258  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17259  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17260  * @singleton
17261  */
17262 Roo.dd.ScrollManager = function(){
17263     var ddm = Roo.dd.DragDropMgr;
17264     var els = {};
17265     var dragEl = null;
17266     var proc = {};
17267     
17268     var onStop = function(e){
17269         dragEl = null;
17270         clearProc();
17271     };
17272     
17273     var triggerRefresh = function(){
17274         if(ddm.dragCurrent){
17275              ddm.refreshCache(ddm.dragCurrent.groups);
17276         }
17277     };
17278     
17279     var doScroll = function(){
17280         if(ddm.dragCurrent){
17281             var dds = Roo.dd.ScrollManager;
17282             if(!dds.animate){
17283                 if(proc.el.scroll(proc.dir, dds.increment)){
17284                     triggerRefresh();
17285                 }
17286             }else{
17287                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17288             }
17289         }
17290     };
17291     
17292     var clearProc = function(){
17293         if(proc.id){
17294             clearInterval(proc.id);
17295         }
17296         proc.id = 0;
17297         proc.el = null;
17298         proc.dir = "";
17299     };
17300     
17301     var startProc = function(el, dir){
17302         clearProc();
17303         proc.el = el;
17304         proc.dir = dir;
17305         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17306     };
17307     
17308     var onFire = function(e, isDrop){
17309         if(isDrop || !ddm.dragCurrent){ return; }
17310         var dds = Roo.dd.ScrollManager;
17311         if(!dragEl || dragEl != ddm.dragCurrent){
17312             dragEl = ddm.dragCurrent;
17313             // refresh regions on drag start
17314             dds.refreshCache();
17315         }
17316         
17317         var xy = Roo.lib.Event.getXY(e);
17318         var pt = new Roo.lib.Point(xy[0], xy[1]);
17319         for(var id in els){
17320             var el = els[id], r = el._region;
17321             if(r && r.contains(pt) && el.isScrollable()){
17322                 if(r.bottom - pt.y <= dds.thresh){
17323                     if(proc.el != el){
17324                         startProc(el, "down");
17325                     }
17326                     return;
17327                 }else if(r.right - pt.x <= dds.thresh){
17328                     if(proc.el != el){
17329                         startProc(el, "left");
17330                     }
17331                     return;
17332                 }else if(pt.y - r.top <= dds.thresh){
17333                     if(proc.el != el){
17334                         startProc(el, "up");
17335                     }
17336                     return;
17337                 }else if(pt.x - r.left <= dds.thresh){
17338                     if(proc.el != el){
17339                         startProc(el, "right");
17340                     }
17341                     return;
17342                 }
17343             }
17344         }
17345         clearProc();
17346     };
17347     
17348     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17349     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17350     
17351     return {
17352         /**
17353          * Registers new overflow element(s) to auto scroll
17354          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17355          */
17356         register : function(el){
17357             if(el instanceof Array){
17358                 for(var i = 0, len = el.length; i < len; i++) {
17359                         this.register(el[i]);
17360                 }
17361             }else{
17362                 el = Roo.get(el);
17363                 els[el.id] = el;
17364             }
17365         },
17366         
17367         /**
17368          * Unregisters overflow element(s) so they are no longer scrolled
17369          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17370          */
17371         unregister : function(el){
17372             if(el instanceof Array){
17373                 for(var i = 0, len = el.length; i < len; i++) {
17374                         this.unregister(el[i]);
17375                 }
17376             }else{
17377                 el = Roo.get(el);
17378                 delete els[el.id];
17379             }
17380         },
17381         
17382         /**
17383          * The number of pixels from the edge of a container the pointer needs to be to 
17384          * trigger scrolling (defaults to 25)
17385          * @type Number
17386          */
17387         thresh : 25,
17388         
17389         /**
17390          * The number of pixels to scroll in each scroll increment (defaults to 50)
17391          * @type Number
17392          */
17393         increment : 100,
17394         
17395         /**
17396          * The frequency of scrolls in milliseconds (defaults to 500)
17397          * @type Number
17398          */
17399         frequency : 500,
17400         
17401         /**
17402          * True to animate the scroll (defaults to true)
17403          * @type Boolean
17404          */
17405         animate: true,
17406         
17407         /**
17408          * The animation duration in seconds - 
17409          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17410          * @type Number
17411          */
17412         animDuration: .4,
17413         
17414         /**
17415          * Manually trigger a cache refresh.
17416          */
17417         refreshCache : function(){
17418             for(var id in els){
17419                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17420                     els[id]._region = els[id].getRegion();
17421                 }
17422             }
17423         }
17424     };
17425 }();/*
17426  * Based on:
17427  * Ext JS Library 1.1.1
17428  * Copyright(c) 2006-2007, Ext JS, LLC.
17429  *
17430  * Originally Released Under LGPL - original licence link has changed is not relivant.
17431  *
17432  * Fork - LGPL
17433  * <script type="text/javascript">
17434  */
17435  
17436
17437 /**
17438  * @class Roo.dd.Registry
17439  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17440  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17441  * @singleton
17442  */
17443 Roo.dd.Registry = function(){
17444     var elements = {}; 
17445     var handles = {}; 
17446     var autoIdSeed = 0;
17447
17448     var getId = function(el, autogen){
17449         if(typeof el == "string"){
17450             return el;
17451         }
17452         var id = el.id;
17453         if(!id && autogen !== false){
17454             id = "roodd-" + (++autoIdSeed);
17455             el.id = id;
17456         }
17457         return id;
17458     };
17459     
17460     return {
17461     /**
17462      * Register a drag drop element
17463      * @param {String|HTMLElement} element The id or DOM node to register
17464      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17465      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17466      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17467      * populated in the data object (if applicable):
17468      * <pre>
17469 Value      Description<br />
17470 ---------  ------------------------------------------<br />
17471 handles    Array of DOM nodes that trigger dragging<br />
17472            for the element being registered<br />
17473 isHandle   True if the element passed in triggers<br />
17474            dragging itself, else false
17475 </pre>
17476      */
17477         register : function(el, data){
17478             data = data || {};
17479             if(typeof el == "string"){
17480                 el = document.getElementById(el);
17481             }
17482             data.ddel = el;
17483             elements[getId(el)] = data;
17484             if(data.isHandle !== false){
17485                 handles[data.ddel.id] = data;
17486             }
17487             if(data.handles){
17488                 var hs = data.handles;
17489                 for(var i = 0, len = hs.length; i < len; i++){
17490                         handles[getId(hs[i])] = data;
17491                 }
17492             }
17493         },
17494
17495     /**
17496      * Unregister a drag drop element
17497      * @param {String|HTMLElement}  element The id or DOM node to unregister
17498      */
17499         unregister : function(el){
17500             var id = getId(el, false);
17501             var data = elements[id];
17502             if(data){
17503                 delete elements[id];
17504                 if(data.handles){
17505                     var hs = data.handles;
17506                     for(var i = 0, len = hs.length; i < len; i++){
17507                         delete handles[getId(hs[i], false)];
17508                     }
17509                 }
17510             }
17511         },
17512
17513     /**
17514      * Returns the handle registered for a DOM Node by id
17515      * @param {String|HTMLElement} id The DOM node or id to look up
17516      * @return {Object} handle The custom handle data
17517      */
17518         getHandle : function(id){
17519             if(typeof id != "string"){ // must be element?
17520                 id = id.id;
17521             }
17522             return handles[id];
17523         },
17524
17525     /**
17526      * Returns the handle that is registered for the DOM node that is the target of the event
17527      * @param {Event} e The event
17528      * @return {Object} handle The custom handle data
17529      */
17530         getHandleFromEvent : function(e){
17531             var t = Roo.lib.Event.getTarget(e);
17532             return t ? handles[t.id] : null;
17533         },
17534
17535     /**
17536      * Returns a custom data object that is registered for a DOM node by id
17537      * @param {String|HTMLElement} id The DOM node or id to look up
17538      * @return {Object} data The custom data
17539      */
17540         getTarget : function(id){
17541             if(typeof id != "string"){ // must be element?
17542                 id = id.id;
17543             }
17544             return elements[id];
17545         },
17546
17547     /**
17548      * Returns a custom data object that is registered for the DOM node that is the target of the event
17549      * @param {Event} e The event
17550      * @return {Object} data The custom data
17551      */
17552         getTargetFromEvent : function(e){
17553             var t = Roo.lib.Event.getTarget(e);
17554             return t ? elements[t.id] || handles[t.id] : null;
17555         }
17556     };
17557 }();/*
17558  * Based on:
17559  * Ext JS Library 1.1.1
17560  * Copyright(c) 2006-2007, Ext JS, LLC.
17561  *
17562  * Originally Released Under LGPL - original licence link has changed is not relivant.
17563  *
17564  * Fork - LGPL
17565  * <script type="text/javascript">
17566  */
17567  
17568
17569 /**
17570  * @class Roo.dd.StatusProxy
17571  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17572  * default drag proxy used by all Roo.dd components.
17573  * @constructor
17574  * @param {Object} config
17575  */
17576 Roo.dd.StatusProxy = function(config){
17577     Roo.apply(this, config);
17578     this.id = this.id || Roo.id();
17579     this.el = new Roo.Layer({
17580         dh: {
17581             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17582                 {tag: "div", cls: "x-dd-drop-icon"},
17583                 {tag: "div", cls: "x-dd-drag-ghost"}
17584             ]
17585         }, 
17586         shadow: !config || config.shadow !== false
17587     });
17588     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17589     this.dropStatus = this.dropNotAllowed;
17590 };
17591
17592 Roo.dd.StatusProxy.prototype = {
17593     /**
17594      * @cfg {String} dropAllowed
17595      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17596      */
17597     dropAllowed : "x-dd-drop-ok",
17598     /**
17599      * @cfg {String} dropNotAllowed
17600      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17601      */
17602     dropNotAllowed : "x-dd-drop-nodrop",
17603
17604     /**
17605      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17606      * over the current target element.
17607      * @param {String} cssClass The css class for the new drop status indicator image
17608      */
17609     setStatus : function(cssClass){
17610         cssClass = cssClass || this.dropNotAllowed;
17611         if(this.dropStatus != cssClass){
17612             this.el.replaceClass(this.dropStatus, cssClass);
17613             this.dropStatus = cssClass;
17614         }
17615     },
17616
17617     /**
17618      * Resets the status indicator to the default dropNotAllowed value
17619      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17620      */
17621     reset : function(clearGhost){
17622         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17623         this.dropStatus = this.dropNotAllowed;
17624         if(clearGhost){
17625             this.ghost.update("");
17626         }
17627     },
17628
17629     /**
17630      * Updates the contents of the ghost element
17631      * @param {String} html The html that will replace the current innerHTML of the ghost element
17632      */
17633     update : function(html){
17634         if(typeof html == "string"){
17635             this.ghost.update(html);
17636         }else{
17637             this.ghost.update("");
17638             html.style.margin = "0";
17639             this.ghost.dom.appendChild(html);
17640         }
17641         // ensure float = none set?? cant remember why though.
17642         var el = this.ghost.dom.firstChild;
17643                 if(el){
17644                         Roo.fly(el).setStyle('float', 'none');
17645                 }
17646     },
17647     
17648     /**
17649      * Returns the underlying proxy {@link Roo.Layer}
17650      * @return {Roo.Layer} el
17651     */
17652     getEl : function(){
17653         return this.el;
17654     },
17655
17656     /**
17657      * Returns the ghost element
17658      * @return {Roo.Element} el
17659      */
17660     getGhost : function(){
17661         return this.ghost;
17662     },
17663
17664     /**
17665      * Hides the proxy
17666      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17667      */
17668     hide : function(clear){
17669         this.el.hide();
17670         if(clear){
17671             this.reset(true);
17672         }
17673     },
17674
17675     /**
17676      * Stops the repair animation if it's currently running
17677      */
17678     stop : function(){
17679         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17680             this.anim.stop();
17681         }
17682     },
17683
17684     /**
17685      * Displays this proxy
17686      */
17687     show : function(){
17688         this.el.show();
17689     },
17690
17691     /**
17692      * Force the Layer to sync its shadow and shim positions to the element
17693      */
17694     sync : function(){
17695         this.el.sync();
17696     },
17697
17698     /**
17699      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17700      * invalid drop operation by the item being dragged.
17701      * @param {Array} xy The XY position of the element ([x, y])
17702      * @param {Function} callback The function to call after the repair is complete
17703      * @param {Object} scope The scope in which to execute the callback
17704      */
17705     repair : function(xy, callback, scope){
17706         this.callback = callback;
17707         this.scope = scope;
17708         if(xy && this.animRepair !== false){
17709             this.el.addClass("x-dd-drag-repair");
17710             this.el.hideUnders(true);
17711             this.anim = this.el.shift({
17712                 duration: this.repairDuration || .5,
17713                 easing: 'easeOut',
17714                 xy: xy,
17715                 stopFx: true,
17716                 callback: this.afterRepair,
17717                 scope: this
17718             });
17719         }else{
17720             this.afterRepair();
17721         }
17722     },
17723
17724     // private
17725     afterRepair : function(){
17726         this.hide(true);
17727         if(typeof this.callback == "function"){
17728             this.callback.call(this.scope || this);
17729         }
17730         this.callback = null;
17731         this.scope = null;
17732     }
17733 };/*
17734  * Based on:
17735  * Ext JS Library 1.1.1
17736  * Copyright(c) 2006-2007, Ext JS, LLC.
17737  *
17738  * Originally Released Under LGPL - original licence link has changed is not relivant.
17739  *
17740  * Fork - LGPL
17741  * <script type="text/javascript">
17742  */
17743
17744 /**
17745  * @class Roo.dd.DragSource
17746  * @extends Roo.dd.DDProxy
17747  * A simple class that provides the basic implementation needed to make any element draggable.
17748  * @constructor
17749  * @param {String/HTMLElement/Element} el The container element
17750  * @param {Object} config
17751  */
17752 Roo.dd.DragSource = function(el, config){
17753     this.el = Roo.get(el);
17754     this.dragData = {};
17755     
17756     Roo.apply(this, config);
17757     
17758     if(!this.proxy){
17759         this.proxy = new Roo.dd.StatusProxy();
17760     }
17761
17762     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17763           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17764     
17765     this.dragging = false;
17766 };
17767
17768 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17769     /**
17770      * @cfg {String} dropAllowed
17771      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17772      */
17773     dropAllowed : "x-dd-drop-ok",
17774     /**
17775      * @cfg {String} dropNotAllowed
17776      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17777      */
17778     dropNotAllowed : "x-dd-drop-nodrop",
17779
17780     /**
17781      * Returns the data object associated with this drag source
17782      * @return {Object} data An object containing arbitrary data
17783      */
17784     getDragData : function(e){
17785         return this.dragData;
17786     },
17787
17788     // private
17789     onDragEnter : function(e, id){
17790         var target = Roo.dd.DragDropMgr.getDDById(id);
17791         this.cachedTarget = target;
17792         if(this.beforeDragEnter(target, e, id) !== false){
17793             if(target.isNotifyTarget){
17794                 var status = target.notifyEnter(this, e, this.dragData);
17795                 this.proxy.setStatus(status);
17796             }else{
17797                 this.proxy.setStatus(this.dropAllowed);
17798             }
17799             
17800             if(this.afterDragEnter){
17801                 /**
17802                  * An empty function by default, but provided so that you can perform a custom action
17803                  * when the dragged item enters the drop target by providing an implementation.
17804                  * @param {Roo.dd.DragDrop} target The drop target
17805                  * @param {Event} e The event object
17806                  * @param {String} id The id of the dragged element
17807                  * @method afterDragEnter
17808                  */
17809                 this.afterDragEnter(target, e, id);
17810             }
17811         }
17812     },
17813
17814     /**
17815      * An empty function by default, but provided so that you can perform a custom action
17816      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17817      * @param {Roo.dd.DragDrop} target The drop target
17818      * @param {Event} e The event object
17819      * @param {String} id The id of the dragged element
17820      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17821      */
17822     beforeDragEnter : function(target, e, id){
17823         return true;
17824     },
17825
17826     // private
17827     alignElWithMouse: function() {
17828         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17829         this.proxy.sync();
17830     },
17831
17832     // private
17833     onDragOver : function(e, id){
17834         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17835         if(this.beforeDragOver(target, e, id) !== false){
17836             if(target.isNotifyTarget){
17837                 var status = target.notifyOver(this, e, this.dragData);
17838                 this.proxy.setStatus(status);
17839             }
17840
17841             if(this.afterDragOver){
17842                 /**
17843                  * An empty function by default, but provided so that you can perform a custom action
17844                  * while the dragged item is over the drop target by providing an implementation.
17845                  * @param {Roo.dd.DragDrop} target The drop target
17846                  * @param {Event} e The event object
17847                  * @param {String} id The id of the dragged element
17848                  * @method afterDragOver
17849                  */
17850                 this.afterDragOver(target, e, id);
17851             }
17852         }
17853     },
17854
17855     /**
17856      * An empty function by default, but provided so that you can perform a custom action
17857      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17858      * @param {Roo.dd.DragDrop} target The drop target
17859      * @param {Event} e The event object
17860      * @param {String} id The id of the dragged element
17861      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17862      */
17863     beforeDragOver : function(target, e, id){
17864         return true;
17865     },
17866
17867     // private
17868     onDragOut : function(e, id){
17869         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17870         if(this.beforeDragOut(target, e, id) !== false){
17871             if(target.isNotifyTarget){
17872                 target.notifyOut(this, e, this.dragData);
17873             }
17874             this.proxy.reset();
17875             if(this.afterDragOut){
17876                 /**
17877                  * An empty function by default, but provided so that you can perform a custom action
17878                  * after the dragged item is dragged out of the target without dropping.
17879                  * @param {Roo.dd.DragDrop} target The drop target
17880                  * @param {Event} e The event object
17881                  * @param {String} id The id of the dragged element
17882                  * @method afterDragOut
17883                  */
17884                 this.afterDragOut(target, e, id);
17885             }
17886         }
17887         this.cachedTarget = null;
17888     },
17889
17890     /**
17891      * An empty function by default, but provided so that you can perform a custom action before the dragged
17892      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17893      * @param {Roo.dd.DragDrop} target The drop target
17894      * @param {Event} e The event object
17895      * @param {String} id The id of the dragged element
17896      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17897      */
17898     beforeDragOut : function(target, e, id){
17899         return true;
17900     },
17901     
17902     // private
17903     onDragDrop : function(e, id){
17904         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17905         if(this.beforeDragDrop(target, e, id) !== false){
17906             if(target.isNotifyTarget){
17907                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
17908                     this.onValidDrop(target, e, id);
17909                 }else{
17910                     this.onInvalidDrop(target, e, id);
17911                 }
17912             }else{
17913                 this.onValidDrop(target, e, id);
17914             }
17915             
17916             if(this.afterDragDrop){
17917                 /**
17918                  * An empty function by default, but provided so that you can perform a custom action
17919                  * after a valid drag drop has occurred by providing an implementation.
17920                  * @param {Roo.dd.DragDrop} target The drop target
17921                  * @param {Event} e The event object
17922                  * @param {String} id The id of the dropped element
17923                  * @method afterDragDrop
17924                  */
17925                 this.afterDragDrop(target, e, id);
17926             }
17927         }
17928         delete this.cachedTarget;
17929     },
17930
17931     /**
17932      * An empty function by default, but provided so that you can perform a custom action before the dragged
17933      * item is dropped onto the target and optionally cancel the onDragDrop.
17934      * @param {Roo.dd.DragDrop} target The drop target
17935      * @param {Event} e The event object
17936      * @param {String} id The id of the dragged element
17937      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
17938      */
17939     beforeDragDrop : function(target, e, id){
17940         return true;
17941     },
17942
17943     // private
17944     onValidDrop : function(target, e, id){
17945         this.hideProxy();
17946         if(this.afterValidDrop){
17947             /**
17948              * An empty function by default, but provided so that you can perform a custom action
17949              * after a valid drop has occurred by providing an implementation.
17950              * @param {Object} target The target DD 
17951              * @param {Event} e The event object
17952              * @param {String} id The id of the dropped element
17953              * @method afterInvalidDrop
17954              */
17955             this.afterValidDrop(target, e, id);
17956         }
17957     },
17958
17959     // private
17960     getRepairXY : function(e, data){
17961         return this.el.getXY();  
17962     },
17963
17964     // private
17965     onInvalidDrop : function(target, e, id){
17966         this.beforeInvalidDrop(target, e, id);
17967         if(this.cachedTarget){
17968             if(this.cachedTarget.isNotifyTarget){
17969                 this.cachedTarget.notifyOut(this, e, this.dragData);
17970             }
17971             this.cacheTarget = null;
17972         }
17973         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
17974
17975         if(this.afterInvalidDrop){
17976             /**
17977              * An empty function by default, but provided so that you can perform a custom action
17978              * after an invalid drop has occurred by providing an implementation.
17979              * @param {Event} e The event object
17980              * @param {String} id The id of the dropped element
17981              * @method afterInvalidDrop
17982              */
17983             this.afterInvalidDrop(e, id);
17984         }
17985     },
17986
17987     // private
17988     afterRepair : function(){
17989         if(Roo.enableFx){
17990             this.el.highlight(this.hlColor || "c3daf9");
17991         }
17992         this.dragging = false;
17993     },
17994
17995     /**
17996      * An empty function by default, but provided so that you can perform a custom action after an invalid
17997      * drop has occurred.
17998      * @param {Roo.dd.DragDrop} target The drop target
17999      * @param {Event} e The event object
18000      * @param {String} id The id of the dragged element
18001      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18002      */
18003     beforeInvalidDrop : function(target, e, id){
18004         return true;
18005     },
18006
18007     // private
18008     handleMouseDown : function(e){
18009         if(this.dragging) {
18010             return;
18011         }
18012         var data = this.getDragData(e);
18013         if(data && this.onBeforeDrag(data, e) !== false){
18014             this.dragData = data;
18015             this.proxy.stop();
18016             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18017         } 
18018     },
18019
18020     /**
18021      * An empty function by default, but provided so that you can perform a custom action before the initial
18022      * drag event begins and optionally cancel it.
18023      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18024      * @param {Event} e The event object
18025      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18026      */
18027     onBeforeDrag : function(data, e){
18028         return true;
18029     },
18030
18031     /**
18032      * An empty function by default, but provided so that you can perform a custom action once the initial
18033      * drag event has begun.  The drag cannot be canceled from this function.
18034      * @param {Number} x The x position of the click on the dragged object
18035      * @param {Number} y The y position of the click on the dragged object
18036      */
18037     onStartDrag : Roo.emptyFn,
18038
18039     // private - YUI override
18040     startDrag : function(x, y){
18041         this.proxy.reset();
18042         this.dragging = true;
18043         this.proxy.update("");
18044         this.onInitDrag(x, y);
18045         this.proxy.show();
18046     },
18047
18048     // private
18049     onInitDrag : function(x, y){
18050         var clone = this.el.dom.cloneNode(true);
18051         clone.id = Roo.id(); // prevent duplicate ids
18052         this.proxy.update(clone);
18053         this.onStartDrag(x, y);
18054         return true;
18055     },
18056
18057     /**
18058      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18059      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18060      */
18061     getProxy : function(){
18062         return this.proxy;  
18063     },
18064
18065     /**
18066      * Hides the drag source's {@link Roo.dd.StatusProxy}
18067      */
18068     hideProxy : function(){
18069         this.proxy.hide();  
18070         this.proxy.reset(true);
18071         this.dragging = false;
18072     },
18073
18074     // private
18075     triggerCacheRefresh : function(){
18076         Roo.dd.DDM.refreshCache(this.groups);
18077     },
18078
18079     // private - override to prevent hiding
18080     b4EndDrag: function(e) {
18081     },
18082
18083     // private - override to prevent moving
18084     endDrag : function(e){
18085         this.onEndDrag(this.dragData, e);
18086     },
18087
18088     // private
18089     onEndDrag : function(data, e){
18090     },
18091     
18092     // private - pin to cursor
18093     autoOffset : function(x, y) {
18094         this.setDelta(-12, -20);
18095     }    
18096 });/*
18097  * Based on:
18098  * Ext JS Library 1.1.1
18099  * Copyright(c) 2006-2007, Ext JS, LLC.
18100  *
18101  * Originally Released Under LGPL - original licence link has changed is not relivant.
18102  *
18103  * Fork - LGPL
18104  * <script type="text/javascript">
18105  */
18106
18107
18108 /**
18109  * @class Roo.dd.DropTarget
18110  * @extends Roo.dd.DDTarget
18111  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18112  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18113  * @constructor
18114  * @param {String/HTMLElement/Element} el The container element
18115  * @param {Object} config
18116  */
18117 Roo.dd.DropTarget = function(el, config){
18118     this.el = Roo.get(el);
18119     
18120     Roo.apply(this, config);
18121     
18122     if(this.containerScroll){
18123         Roo.dd.ScrollManager.register(this.el);
18124     }
18125     
18126     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18127           {isTarget: true});
18128
18129 };
18130
18131 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18132     /**
18133      * @cfg {String} overClass
18134      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18135      */
18136     /**
18137      * @cfg {String} dropAllowed
18138      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18139      */
18140     dropAllowed : "x-dd-drop-ok",
18141     /**
18142      * @cfg {String} dropNotAllowed
18143      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18144      */
18145     dropNotAllowed : "x-dd-drop-nodrop",
18146
18147     // private
18148     isTarget : true,
18149
18150     // private
18151     isNotifyTarget : true,
18152
18153     /**
18154      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18155      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18156      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18157      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18158      * @param {Event} e The event
18159      * @param {Object} data An object containing arbitrary data supplied by the drag source
18160      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18161      * underlying {@link Roo.dd.StatusProxy} can be updated
18162      */
18163     notifyEnter : function(dd, e, data){
18164         if(this.overClass){
18165             this.el.addClass(this.overClass);
18166         }
18167         return this.dropAllowed;
18168     },
18169
18170     /**
18171      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18172      * This method will be called on every mouse movement while the drag source is over the drop target.
18173      * This default implementation simply returns the dropAllowed config value.
18174      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18175      * @param {Event} e The event
18176      * @param {Object} data An object containing arbitrary data supplied by the drag source
18177      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18178      * underlying {@link Roo.dd.StatusProxy} can be updated
18179      */
18180     notifyOver : function(dd, e, data){
18181         return this.dropAllowed;
18182     },
18183
18184     /**
18185      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18186      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18187      * overClass (if any) from the drop element.
18188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18189      * @param {Event} e The event
18190      * @param {Object} data An object containing arbitrary data supplied by the drag source
18191      */
18192     notifyOut : function(dd, e, data){
18193         if(this.overClass){
18194             this.el.removeClass(this.overClass);
18195         }
18196     },
18197
18198     /**
18199      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18200      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18201      * implementation that does something to process the drop event and returns true so that the drag source's
18202      * repair action does not run.
18203      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18204      * @param {Event} e The event
18205      * @param {Object} data An object containing arbitrary data supplied by the drag source
18206      * @return {Boolean} True if the drop was valid, else false
18207      */
18208     notifyDrop : function(dd, e, data){
18209         return false;
18210     }
18211 });/*
18212  * Based on:
18213  * Ext JS Library 1.1.1
18214  * Copyright(c) 2006-2007, Ext JS, LLC.
18215  *
18216  * Originally Released Under LGPL - original licence link has changed is not relivant.
18217  *
18218  * Fork - LGPL
18219  * <script type="text/javascript">
18220  */
18221
18222
18223 /**
18224  * @class Roo.dd.DragZone
18225  * @extends Roo.dd.DragSource
18226  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18227  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18228  * @constructor
18229  * @param {String/HTMLElement/Element} el The container element
18230  * @param {Object} config
18231  */
18232 Roo.dd.DragZone = function(el, config){
18233     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18234     if(this.containerScroll){
18235         Roo.dd.ScrollManager.register(this.el);
18236     }
18237 };
18238
18239 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18240     /**
18241      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18242      * for auto scrolling during drag operations.
18243      */
18244     /**
18245      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18246      * method after a failed drop (defaults to "c3daf9" - light blue)
18247      */
18248
18249     /**
18250      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18251      * for a valid target to drag based on the mouse down. Override this method
18252      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18253      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18254      * @param {EventObject} e The mouse down event
18255      * @return {Object} The dragData
18256      */
18257     getDragData : function(e){
18258         return Roo.dd.Registry.getHandleFromEvent(e);
18259     },
18260     
18261     /**
18262      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18263      * this.dragData.ddel
18264      * @param {Number} x The x position of the click on the dragged object
18265      * @param {Number} y The y position of the click on the dragged object
18266      * @return {Boolean} true to continue the drag, false to cancel
18267      */
18268     onInitDrag : function(x, y){
18269         this.proxy.update(this.dragData.ddel.cloneNode(true));
18270         this.onStartDrag(x, y);
18271         return true;
18272     },
18273     
18274     /**
18275      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18276      */
18277     afterRepair : function(){
18278         if(Roo.enableFx){
18279             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18280         }
18281         this.dragging = false;
18282     },
18283
18284     /**
18285      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18286      * the XY of this.dragData.ddel
18287      * @param {EventObject} e The mouse up event
18288      * @return {Array} The xy location (e.g. [100, 200])
18289      */
18290     getRepairXY : function(e){
18291         return Roo.Element.fly(this.dragData.ddel).getXY();  
18292     }
18293 });/*
18294  * Based on:
18295  * Ext JS Library 1.1.1
18296  * Copyright(c) 2006-2007, Ext JS, LLC.
18297  *
18298  * Originally Released Under LGPL - original licence link has changed is not relivant.
18299  *
18300  * Fork - LGPL
18301  * <script type="text/javascript">
18302  */
18303 /**
18304  * @class Roo.dd.DropZone
18305  * @extends Roo.dd.DropTarget
18306  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18307  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18308  * @constructor
18309  * @param {String/HTMLElement/Element} el The container element
18310  * @param {Object} config
18311  */
18312 Roo.dd.DropZone = function(el, config){
18313     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18314 };
18315
18316 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18317     /**
18318      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18319      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18320      * provide your own custom lookup.
18321      * @param {Event} e The event
18322      * @return {Object} data The custom data
18323      */
18324     getTargetFromEvent : function(e){
18325         return Roo.dd.Registry.getTargetFromEvent(e);
18326     },
18327
18328     /**
18329      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18330      * that it has registered.  This method has no default implementation and should be overridden to provide
18331      * node-specific processing if necessary.
18332      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18333      * {@link #getTargetFromEvent} for this node)
18334      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18335      * @param {Event} e The event
18336      * @param {Object} data An object containing arbitrary data supplied by the drag source
18337      */
18338     onNodeEnter : function(n, dd, e, data){
18339         
18340     },
18341
18342     /**
18343      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18344      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18345      * overridden to provide the proper feedback.
18346      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18347      * {@link #getTargetFromEvent} for this node)
18348      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18349      * @param {Event} e The event
18350      * @param {Object} data An object containing arbitrary data supplied by the drag source
18351      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18352      * underlying {@link Roo.dd.StatusProxy} can be updated
18353      */
18354     onNodeOver : function(n, dd, e, data){
18355         return this.dropAllowed;
18356     },
18357
18358     /**
18359      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18360      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18361      * node-specific processing if necessary.
18362      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18363      * {@link #getTargetFromEvent} for this node)
18364      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18365      * @param {Event} e The event
18366      * @param {Object} data An object containing arbitrary data supplied by the drag source
18367      */
18368     onNodeOut : function(n, dd, e, data){
18369         
18370     },
18371
18372     /**
18373      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18374      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18375      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18376      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18377      * {@link #getTargetFromEvent} for this node)
18378      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18379      * @param {Event} e The event
18380      * @param {Object} data An object containing arbitrary data supplied by the drag source
18381      * @return {Boolean} True if the drop was valid, else false
18382      */
18383     onNodeDrop : function(n, dd, e, data){
18384         return false;
18385     },
18386
18387     /**
18388      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18389      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18390      * it should be overridden to provide the proper feedback if necessary.
18391      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18392      * @param {Event} e The event
18393      * @param {Object} data An object containing arbitrary data supplied by the drag source
18394      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18395      * underlying {@link Roo.dd.StatusProxy} can be updated
18396      */
18397     onContainerOver : function(dd, e, data){
18398         return this.dropNotAllowed;
18399     },
18400
18401     /**
18402      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18403      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18404      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18405      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18406      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18407      * @param {Event} e The event
18408      * @param {Object} data An object containing arbitrary data supplied by the drag source
18409      * @return {Boolean} True if the drop was valid, else false
18410      */
18411     onContainerDrop : function(dd, e, data){
18412         return false;
18413     },
18414
18415     /**
18416      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18417      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18418      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18419      * you should override this method and provide a custom implementation.
18420      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18421      * @param {Event} e The event
18422      * @param {Object} data An object containing arbitrary data supplied by the drag source
18423      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18424      * underlying {@link Roo.dd.StatusProxy} can be updated
18425      */
18426     notifyEnter : function(dd, e, data){
18427         return this.dropNotAllowed;
18428     },
18429
18430     /**
18431      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18432      * This method will be called on every mouse movement while the drag source is over the drop zone.
18433      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18434      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18435      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18436      * registered node, it will call {@link #onContainerOver}.
18437      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18438      * @param {Event} e The event
18439      * @param {Object} data An object containing arbitrary data supplied by the drag source
18440      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18441      * underlying {@link Roo.dd.StatusProxy} can be updated
18442      */
18443     notifyOver : function(dd, e, data){
18444         var n = this.getTargetFromEvent(e);
18445         if(!n){ // not over valid drop target
18446             if(this.lastOverNode){
18447                 this.onNodeOut(this.lastOverNode, dd, e, data);
18448                 this.lastOverNode = null;
18449             }
18450             return this.onContainerOver(dd, e, data);
18451         }
18452         if(this.lastOverNode != n){
18453             if(this.lastOverNode){
18454                 this.onNodeOut(this.lastOverNode, dd, e, data);
18455             }
18456             this.onNodeEnter(n, dd, e, data);
18457             this.lastOverNode = n;
18458         }
18459         return this.onNodeOver(n, dd, e, data);
18460     },
18461
18462     /**
18463      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18464      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18465      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18466      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18467      * @param {Event} e The event
18468      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18469      */
18470     notifyOut : function(dd, e, data){
18471         if(this.lastOverNode){
18472             this.onNodeOut(this.lastOverNode, dd, e, data);
18473             this.lastOverNode = null;
18474         }
18475     },
18476
18477     /**
18478      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18479      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18480      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18481      * otherwise it will call {@link #onContainerDrop}.
18482      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18483      * @param {Event} e The event
18484      * @param {Object} data An object containing arbitrary data supplied by the drag source
18485      * @return {Boolean} True if the drop was valid, else false
18486      */
18487     notifyDrop : function(dd, e, data){
18488         if(this.lastOverNode){
18489             this.onNodeOut(this.lastOverNode, dd, e, data);
18490             this.lastOverNode = null;
18491         }
18492         var n = this.getTargetFromEvent(e);
18493         return n ?
18494             this.onNodeDrop(n, dd, e, data) :
18495             this.onContainerDrop(dd, e, data);
18496     },
18497
18498     // private
18499     triggerCacheRefresh : function(){
18500         Roo.dd.DDM.refreshCache(this.groups);
18501     }  
18502 });/*
18503  * Based on:
18504  * Ext JS Library 1.1.1
18505  * Copyright(c) 2006-2007, Ext JS, LLC.
18506  *
18507  * Originally Released Under LGPL - original licence link has changed is not relivant.
18508  *
18509  * Fork - LGPL
18510  * <script type="text/javascript">
18511  */
18512
18513
18514 /**
18515  * @class Roo.data.SortTypes
18516  * @singleton
18517  * Defines the default sorting (casting?) comparison functions used when sorting data.
18518  */
18519 Roo.data.SortTypes = {
18520     /**
18521      * Default sort that does nothing
18522      * @param {Mixed} s The value being converted
18523      * @return {Mixed} The comparison value
18524      */
18525     none : function(s){
18526         return s;
18527     },
18528     
18529     /**
18530      * The regular expression used to strip tags
18531      * @type {RegExp}
18532      * @property
18533      */
18534     stripTagsRE : /<\/?[^>]+>/gi,
18535     
18536     /**
18537      * Strips all HTML tags to sort on text only
18538      * @param {Mixed} s The value being converted
18539      * @return {String} The comparison value
18540      */
18541     asText : function(s){
18542         return String(s).replace(this.stripTagsRE, "");
18543     },
18544     
18545     /**
18546      * Strips all HTML tags to sort on text only - Case insensitive
18547      * @param {Mixed} s The value being converted
18548      * @return {String} The comparison value
18549      */
18550     asUCText : function(s){
18551         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18552     },
18553     
18554     /**
18555      * Case insensitive string
18556      * @param {Mixed} s The value being converted
18557      * @return {String} The comparison value
18558      */
18559     asUCString : function(s) {
18560         return String(s).toUpperCase();
18561     },
18562     
18563     /**
18564      * Date sorting
18565      * @param {Mixed} s The value being converted
18566      * @return {Number} The comparison value
18567      */
18568     asDate : function(s) {
18569         if(!s){
18570             return 0;
18571         }
18572         if(s instanceof Date){
18573             return s.getTime();
18574         }
18575         return Date.parse(String(s));
18576     },
18577     
18578     /**
18579      * Float sorting
18580      * @param {Mixed} s The value being converted
18581      * @return {Float} The comparison value
18582      */
18583     asFloat : function(s) {
18584         var val = parseFloat(String(s).replace(/,/g, ""));
18585         if(isNaN(val)) val = 0;
18586         return val;
18587     },
18588     
18589     /**
18590      * Integer sorting
18591      * @param {Mixed} s The value being converted
18592      * @return {Number} The comparison value
18593      */
18594     asInt : function(s) {
18595         var val = parseInt(String(s).replace(/,/g, ""));
18596         if(isNaN(val)) val = 0;
18597         return val;
18598     }
18599 };/*
18600  * Based on:
18601  * Ext JS Library 1.1.1
18602  * Copyright(c) 2006-2007, Ext JS, LLC.
18603  *
18604  * Originally Released Under LGPL - original licence link has changed is not relivant.
18605  *
18606  * Fork - LGPL
18607  * <script type="text/javascript">
18608  */
18609
18610 /**
18611 * @class Roo.data.Record
18612  * Instances of this class encapsulate both record <em>definition</em> information, and record
18613  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18614  * to access Records cached in an {@link Roo.data.Store} object.<br>
18615  * <p>
18616  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18617  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18618  * objects.<br>
18619  * <p>
18620  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18621  * @constructor
18622  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18623  * {@link #create}. The parameters are the same.
18624  * @param {Array} data An associative Array of data values keyed by the field name.
18625  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18626  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18627  * not specified an integer id is generated.
18628  */
18629 Roo.data.Record = function(data, id){
18630     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18631     this.data = data;
18632 };
18633
18634 /**
18635  * Generate a constructor for a specific record layout.
18636  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18637  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18638  * Each field definition object may contain the following properties: <ul>
18639  * <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,
18640  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18641  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18642  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18643  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18644  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18645  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18646  * this may be omitted.</p></li>
18647  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18648  * <ul><li>auto (Default, implies no conversion)</li>
18649  * <li>string</li>
18650  * <li>int</li>
18651  * <li>float</li>
18652  * <li>boolean</li>
18653  * <li>date</li></ul></p></li>
18654  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18655  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18656  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18657  * by the Reader into an object that will be stored in the Record. It is passed the
18658  * following parameters:<ul>
18659  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18660  * </ul></p></li>
18661  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18662  * </ul>
18663  * <br>usage:<br><pre><code>
18664 var TopicRecord = Roo.data.Record.create(
18665     {name: 'title', mapping: 'topic_title'},
18666     {name: 'author', mapping: 'username'},
18667     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18668     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18669     {name: 'lastPoster', mapping: 'user2'},
18670     {name: 'excerpt', mapping: 'post_text'}
18671 );
18672
18673 var myNewRecord = new TopicRecord({
18674     title: 'Do my job please',
18675     author: 'noobie',
18676     totalPosts: 1,
18677     lastPost: new Date(),
18678     lastPoster: 'Animal',
18679     excerpt: 'No way dude!'
18680 });
18681 myStore.add(myNewRecord);
18682 </code></pre>
18683  * @method create
18684  * @static
18685  */
18686 Roo.data.Record.create = function(o){
18687     var f = function(){
18688         f.superclass.constructor.apply(this, arguments);
18689     };
18690     Roo.extend(f, Roo.data.Record);
18691     var p = f.prototype;
18692     p.fields = new Roo.util.MixedCollection(false, function(field){
18693         return field.name;
18694     });
18695     for(var i = 0, len = o.length; i < len; i++){
18696         p.fields.add(new Roo.data.Field(o[i]));
18697     }
18698     f.getField = function(name){
18699         return p.fields.get(name);  
18700     };
18701     return f;
18702 };
18703
18704 Roo.data.Record.AUTO_ID = 1000;
18705 Roo.data.Record.EDIT = 'edit';
18706 Roo.data.Record.REJECT = 'reject';
18707 Roo.data.Record.COMMIT = 'commit';
18708
18709 Roo.data.Record.prototype = {
18710     /**
18711      * Readonly flag - true if this record has been modified.
18712      * @type Boolean
18713      */
18714     dirty : false,
18715     editing : false,
18716     error: null,
18717     modified: null,
18718
18719     // private
18720     join : function(store){
18721         this.store = store;
18722     },
18723
18724     /**
18725      * Set the named field to the specified value.
18726      * @param {String} name The name of the field to set.
18727      * @param {Object} value The value to set the field to.
18728      */
18729     set : function(name, value){
18730         if(this.data[name] == value){
18731             return;
18732         }
18733         this.dirty = true;
18734         if(!this.modified){
18735             this.modified = {};
18736         }
18737         if(typeof this.modified[name] == 'undefined'){
18738             this.modified[name] = this.data[name];
18739         }
18740         this.data[name] = value;
18741         if(!this.editing){
18742             this.store.afterEdit(this);
18743         }       
18744     },
18745
18746     /**
18747      * Get the value of the named field.
18748      * @param {String} name The name of the field to get the value of.
18749      * @return {Object} The value of the field.
18750      */
18751     get : function(name){
18752         return this.data[name]; 
18753     },
18754
18755     // private
18756     beginEdit : function(){
18757         this.editing = true;
18758         this.modified = {}; 
18759     },
18760
18761     // private
18762     cancelEdit : function(){
18763         this.editing = false;
18764         delete this.modified;
18765     },
18766
18767     // private
18768     endEdit : function(){
18769         this.editing = false;
18770         if(this.dirty && this.store){
18771             this.store.afterEdit(this);
18772         }
18773     },
18774
18775     /**
18776      * Usually called by the {@link Roo.data.Store} which owns the Record.
18777      * Rejects all changes made to the Record since either creation, or the last commit operation.
18778      * Modified fields are reverted to their original values.
18779      * <p>
18780      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18781      * of reject operations.
18782      */
18783     reject : function(){
18784         var m = this.modified;
18785         for(var n in m){
18786             if(typeof m[n] != "function"){
18787                 this.data[n] = m[n];
18788             }
18789         }
18790         this.dirty = false;
18791         delete this.modified;
18792         this.editing = false;
18793         if(this.store){
18794             this.store.afterReject(this);
18795         }
18796     },
18797
18798     /**
18799      * Usually called by the {@link Roo.data.Store} which owns the Record.
18800      * Commits all changes made to the Record since either creation, or the last commit operation.
18801      * <p>
18802      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18803      * of commit operations.
18804      */
18805     commit : function(){
18806         this.dirty = false;
18807         delete this.modified;
18808         this.editing = false;
18809         if(this.store){
18810             this.store.afterCommit(this);
18811         }
18812     },
18813
18814     // private
18815     hasError : function(){
18816         return this.error != null;
18817     },
18818
18819     // private
18820     clearError : function(){
18821         this.error = null;
18822     },
18823
18824     /**
18825      * Creates a copy of this record.
18826      * @param {String} id (optional) A new record id if you don't want to use this record's id
18827      * @return {Record}
18828      */
18829     copy : function(newId) {
18830         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18831     }
18832 };/*
18833  * Based on:
18834  * Ext JS Library 1.1.1
18835  * Copyright(c) 2006-2007, Ext JS, LLC.
18836  *
18837  * Originally Released Under LGPL - original licence link has changed is not relivant.
18838  *
18839  * Fork - LGPL
18840  * <script type="text/javascript">
18841  */
18842
18843
18844
18845 /**
18846  * @class Roo.data.Store
18847  * @extends Roo.util.Observable
18848  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18849  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18850  * <p>
18851  * 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
18852  * has no knowledge of the format of the data returned by the Proxy.<br>
18853  * <p>
18854  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18855  * instances from the data object. These records are cached and made available through accessor functions.
18856  * @constructor
18857  * Creates a new Store.
18858  * @param {Object} config A config object containing the objects needed for the Store to access data,
18859  * and read the data into Records.
18860  */
18861 Roo.data.Store = function(config){
18862     this.data = new Roo.util.MixedCollection(false);
18863     this.data.getKey = function(o){
18864         return o.id;
18865     };
18866     this.baseParams = {};
18867     // private
18868     this.paramNames = {
18869         "start" : "start",
18870         "limit" : "limit",
18871         "sort" : "sort",
18872         "dir" : "dir"
18873     };
18874
18875     if(config && config.data){
18876         this.inlineData = config.data;
18877         delete config.data;
18878     }
18879
18880     Roo.apply(this, config);
18881     
18882     if(this.reader){ // reader passed
18883         this.reader = Roo.factory(this.reader, Roo.data);
18884         this.reader.xmodule = this.xmodule || false;
18885         if(!this.recordType){
18886             this.recordType = this.reader.recordType;
18887         }
18888         if(this.reader.onMetaChange){
18889             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18890         }
18891     }
18892
18893     if(this.recordType){
18894         this.fields = this.recordType.prototype.fields;
18895     }
18896     this.modified = [];
18897
18898     this.addEvents({
18899         /**
18900          * @event datachanged
18901          * Fires when the data cache has changed, and a widget which is using this Store
18902          * as a Record cache should refresh its view.
18903          * @param {Store} this
18904          */
18905         datachanged : true,
18906         /**
18907          * @event metachange
18908          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
18909          * @param {Store} this
18910          * @param {Object} meta The JSON metadata
18911          */
18912         metachange : true,
18913         /**
18914          * @event add
18915          * Fires when Records have been added to the Store
18916          * @param {Store} this
18917          * @param {Roo.data.Record[]} records The array of Records added
18918          * @param {Number} index The index at which the record(s) were added
18919          */
18920         add : true,
18921         /**
18922          * @event remove
18923          * Fires when a Record has been removed from the Store
18924          * @param {Store} this
18925          * @param {Roo.data.Record} record The Record that was removed
18926          * @param {Number} index The index at which the record was removed
18927          */
18928         remove : true,
18929         /**
18930          * @event update
18931          * Fires when a Record has been updated
18932          * @param {Store} this
18933          * @param {Roo.data.Record} record The Record that was updated
18934          * @param {String} operation The update operation being performed.  Value may be one of:
18935          * <pre><code>
18936  Roo.data.Record.EDIT
18937  Roo.data.Record.REJECT
18938  Roo.data.Record.COMMIT
18939          * </code></pre>
18940          */
18941         update : true,
18942         /**
18943          * @event clear
18944          * Fires when the data cache has been cleared.
18945          * @param {Store} this
18946          */
18947         clear : true,
18948         /**
18949          * @event beforeload
18950          * Fires before a request is made for a new data object.  If the beforeload handler returns false
18951          * the load action will be canceled.
18952          * @param {Store} this
18953          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18954          */
18955         beforeload : true,
18956         /**
18957          * @event load
18958          * Fires after a new set of Records has been loaded.
18959          * @param {Store} this
18960          * @param {Roo.data.Record[]} records The Records that were loaded
18961          * @param {Object} options The loading options that were specified (see {@link #load} for details)
18962          */
18963         load : true,
18964         /**
18965          * @event loadexception
18966          * Fires if an exception occurs in the Proxy during loading.
18967          * Called with the signature of the Proxy's "loadexception" event.
18968          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
18969          * 
18970          * @param {Proxy} 
18971          * @param {Object} return from JsonData.reader() - success, totalRecords, records
18972          * @param {Object} load options 
18973          * @param {Object} jsonData from your request (normally this contains the Exception)
18974          */
18975         loadexception : true
18976     });
18977     
18978     if(this.proxy){
18979         this.proxy = Roo.factory(this.proxy, Roo.data);
18980         this.proxy.xmodule = this.xmodule || false;
18981         this.relayEvents(this.proxy,  ["loadexception"]);
18982     }
18983     this.sortToggle = {};
18984
18985     Roo.data.Store.superclass.constructor.call(this);
18986
18987     if(this.inlineData){
18988         this.loadData(this.inlineData);
18989         delete this.inlineData;
18990     }
18991 };
18992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
18993      /**
18994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
18995     * without a remote query - used by combo/forms at present.
18996     */
18997     
18998     /**
18999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19000     */
19001     /**
19002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19003     */
19004     /**
19005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19007     */
19008     /**
19009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19010     * on any HTTP request
19011     */
19012     /**
19013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19014     */
19015     /**
19016     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19017     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19018     */
19019     remoteSort : false,
19020
19021     /**
19022     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19023      * loaded or when a record is removed. (defaults to false).
19024     */
19025     pruneModifiedRecords : false,
19026
19027     // private
19028     lastOptions : null,
19029
19030     /**
19031      * Add Records to the Store and fires the add event.
19032      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19033      */
19034     add : function(records){
19035         records = [].concat(records);
19036         for(var i = 0, len = records.length; i < len; i++){
19037             records[i].join(this);
19038         }
19039         var index = this.data.length;
19040         this.data.addAll(records);
19041         this.fireEvent("add", this, records, index);
19042     },
19043
19044     /**
19045      * Remove a Record from the Store and fires the remove event.
19046      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19047      */
19048     remove : function(record){
19049         var index = this.data.indexOf(record);
19050         this.data.removeAt(index);
19051         if(this.pruneModifiedRecords){
19052             this.modified.remove(record);
19053         }
19054         this.fireEvent("remove", this, record, index);
19055     },
19056
19057     /**
19058      * Remove all Records from the Store and fires the clear event.
19059      */
19060     removeAll : function(){
19061         this.data.clear();
19062         if(this.pruneModifiedRecords){
19063             this.modified = [];
19064         }
19065         this.fireEvent("clear", this);
19066     },
19067
19068     /**
19069      * Inserts Records to the Store at the given index and fires the add event.
19070      * @param {Number} index The start index at which to insert the passed Records.
19071      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19072      */
19073     insert : function(index, records){
19074         records = [].concat(records);
19075         for(var i = 0, len = records.length; i < len; i++){
19076             this.data.insert(index, records[i]);
19077             records[i].join(this);
19078         }
19079         this.fireEvent("add", this, records, index);
19080     },
19081
19082     /**
19083      * Get the index within the cache of the passed Record.
19084      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19085      * @return {Number} The index of the passed Record. Returns -1 if not found.
19086      */
19087     indexOf : function(record){
19088         return this.data.indexOf(record);
19089     },
19090
19091     /**
19092      * Get the index within the cache of the Record with the passed id.
19093      * @param {String} id The id of the Record to find.
19094      * @return {Number} The index of the Record. Returns -1 if not found.
19095      */
19096     indexOfId : function(id){
19097         return this.data.indexOfKey(id);
19098     },
19099
19100     /**
19101      * Get the Record with the specified id.
19102      * @param {String} id The id of the Record to find.
19103      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19104      */
19105     getById : function(id){
19106         return this.data.key(id);
19107     },
19108
19109     /**
19110      * Get the Record at the specified index.
19111      * @param {Number} index The index of the Record to find.
19112      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19113      */
19114     getAt : function(index){
19115         return this.data.itemAt(index);
19116     },
19117
19118     /**
19119      * Returns a range of Records between specified indices.
19120      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19121      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19122      * @return {Roo.data.Record[]} An array of Records
19123      */
19124     getRange : function(start, end){
19125         return this.data.getRange(start, end);
19126     },
19127
19128     // private
19129     storeOptions : function(o){
19130         o = Roo.apply({}, o);
19131         delete o.callback;
19132         delete o.scope;
19133         this.lastOptions = o;
19134     },
19135
19136     /**
19137      * Loads the Record cache from the configured Proxy using the configured Reader.
19138      * <p>
19139      * If using remote paging, then the first load call must specify the <em>start</em>
19140      * and <em>limit</em> properties in the options.params property to establish the initial
19141      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19142      * <p>
19143      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19144      * and this call will return before the new data has been loaded. Perform any post-processing
19145      * in a callback function, or in a "load" event handler.</strong>
19146      * <p>
19147      * @param {Object} options An object containing properties which control loading options:<ul>
19148      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19149      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19150      * passed the following arguments:<ul>
19151      * <li>r : Roo.data.Record[]</li>
19152      * <li>options: Options object from the load call</li>
19153      * <li>success: Boolean success indicator</li></ul></li>
19154      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19155      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19156      * </ul>
19157      */
19158     load : function(options){
19159         options = options || {};
19160         if(this.fireEvent("beforeload", this, options) !== false){
19161             this.storeOptions(options);
19162             var p = Roo.apply(options.params || {}, this.baseParams);
19163             if(this.sortInfo && this.remoteSort){
19164                 var pn = this.paramNames;
19165                 p[pn["sort"]] = this.sortInfo.field;
19166                 p[pn["dir"]] = this.sortInfo.direction;
19167             }
19168             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19169         }
19170     },
19171
19172     /**
19173      * Reloads the Record cache from the configured Proxy using the configured Reader and
19174      * the options from the last load operation performed.
19175      * @param {Object} options (optional) An object containing properties which may override the options
19176      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19177      * the most recently used options are reused).
19178      */
19179     reload : function(options){
19180         this.load(Roo.applyIf(options||{}, this.lastOptions));
19181     },
19182
19183     // private
19184     // Called as a callback by the Reader during a load operation.
19185     loadRecords : function(o, options, success){
19186         if(!o || success === false){
19187             if(success !== false){
19188                 this.fireEvent("load", this, [], options);
19189             }
19190             if(options.callback){
19191                 options.callback.call(options.scope || this, [], options, false);
19192             }
19193             return;
19194         }
19195         // if data returned failure - throw an exception.
19196         if (o.success === false) {
19197             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19198             return;
19199         }
19200         var r = o.records, t = o.totalRecords || r.length;
19201         if(!options || options.add !== true){
19202             if(this.pruneModifiedRecords){
19203                 this.modified = [];
19204             }
19205             for(var i = 0, len = r.length; i < len; i++){
19206                 r[i].join(this);
19207             }
19208             if(this.snapshot){
19209                 this.data = this.snapshot;
19210                 delete this.snapshot;
19211             }
19212             this.data.clear();
19213             this.data.addAll(r);
19214             this.totalLength = t;
19215             this.applySort();
19216             this.fireEvent("datachanged", this);
19217         }else{
19218             this.totalLength = Math.max(t, this.data.length+r.length);
19219             this.add(r);
19220         }
19221         this.fireEvent("load", this, r, options);
19222         if(options.callback){
19223             options.callback.call(options.scope || this, r, options, true);
19224         }
19225     },
19226
19227     /**
19228      * Loads data from a passed data block. A Reader which understands the format of the data
19229      * must have been configured in the constructor.
19230      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19231      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19232      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19233      */
19234     loadData : function(o, append){
19235         var r = this.reader.readRecords(o);
19236         this.loadRecords(r, {add: append}, true);
19237     },
19238
19239     /**
19240      * Gets the number of cached records.
19241      * <p>
19242      * <em>If using paging, this may not be the total size of the dataset. If the data object
19243      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19244      * the data set size</em>
19245      */
19246     getCount : function(){
19247         return this.data.length || 0;
19248     },
19249
19250     /**
19251      * Gets the total number of records in the dataset as returned by the server.
19252      * <p>
19253      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19254      * the dataset size</em>
19255      */
19256     getTotalCount : function(){
19257         return this.totalLength || 0;
19258     },
19259
19260     /**
19261      * Returns the sort state of the Store as an object with two properties:
19262      * <pre><code>
19263  field {String} The name of the field by which the Records are sorted
19264  direction {String} The sort order, "ASC" or "DESC"
19265      * </code></pre>
19266      */
19267     getSortState : function(){
19268         return this.sortInfo;
19269     },
19270
19271     // private
19272     applySort : function(){
19273         if(this.sortInfo && !this.remoteSort){
19274             var s = this.sortInfo, f = s.field;
19275             var st = this.fields.get(f).sortType;
19276             var fn = function(r1, r2){
19277                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19278                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19279             };
19280             this.data.sort(s.direction, fn);
19281             if(this.snapshot && this.snapshot != this.data){
19282                 this.snapshot.sort(s.direction, fn);
19283             }
19284         }
19285     },
19286
19287     /**
19288      * Sets the default sort column and order to be used by the next load operation.
19289      * @param {String} fieldName The name of the field to sort by.
19290      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19291      */
19292     setDefaultSort : function(field, dir){
19293         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19294     },
19295
19296     /**
19297      * Sort the Records.
19298      * If remote sorting is used, the sort is performed on the server, and the cache is
19299      * reloaded. If local sorting is used, the cache is sorted internally.
19300      * @param {String} fieldName The name of the field to sort by.
19301      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19302      */
19303     sort : function(fieldName, dir){
19304         var f = this.fields.get(fieldName);
19305         if(!dir){
19306             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19307                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19308             }else{
19309                 dir = f.sortDir;
19310             }
19311         }
19312         this.sortToggle[f.name] = dir;
19313         this.sortInfo = {field: f.name, direction: dir};
19314         if(!this.remoteSort){
19315             this.applySort();
19316             this.fireEvent("datachanged", this);
19317         }else{
19318             this.load(this.lastOptions);
19319         }
19320     },
19321
19322     /**
19323      * Calls the specified function for each of the Records in the cache.
19324      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19325      * Returning <em>false</em> aborts and exits the iteration.
19326      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19327      */
19328     each : function(fn, scope){
19329         this.data.each(fn, scope);
19330     },
19331
19332     /**
19333      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19334      * (e.g., during paging).
19335      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19336      */
19337     getModifiedRecords : function(){
19338         return this.modified;
19339     },
19340
19341     // private
19342     createFilterFn : function(property, value, anyMatch){
19343         if(!value.exec){ // not a regex
19344             value = String(value);
19345             if(value.length == 0){
19346                 return false;
19347             }
19348             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19349         }
19350         return function(r){
19351             return value.test(r.data[property]);
19352         };
19353     },
19354
19355     /**
19356      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19357      * @param {String} property A field on your records
19358      * @param {Number} start The record index to start at (defaults to 0)
19359      * @param {Number} end The last record index to include (defaults to length - 1)
19360      * @return {Number} The sum
19361      */
19362     sum : function(property, start, end){
19363         var rs = this.data.items, v = 0;
19364         start = start || 0;
19365         end = (end || end === 0) ? end : rs.length-1;
19366
19367         for(var i = start; i <= end; i++){
19368             v += (rs[i].data[property] || 0);
19369         }
19370         return v;
19371     },
19372
19373     /**
19374      * Filter the records by a specified property.
19375      * @param {String} field A field on your records
19376      * @param {String/RegExp} value Either a string that the field
19377      * should start with or a RegExp to test against the field
19378      * @param {Boolean} anyMatch True to match any part not just the beginning
19379      */
19380     filter : function(property, value, anyMatch){
19381         var fn = this.createFilterFn(property, value, anyMatch);
19382         return fn ? this.filterBy(fn) : this.clearFilter();
19383     },
19384
19385     /**
19386      * Filter by a function. The specified function will be called with each
19387      * record in this data source. If the function returns true the record is included,
19388      * otherwise it is filtered.
19389      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19390      * @param {Object} scope (optional) The scope of the function (defaults to this)
19391      */
19392     filterBy : function(fn, scope){
19393         this.snapshot = this.snapshot || this.data;
19394         this.data = this.queryBy(fn, scope||this);
19395         this.fireEvent("datachanged", this);
19396     },
19397
19398     /**
19399      * Query the records by a specified property.
19400      * @param {String} field A field on your records
19401      * @param {String/RegExp} value Either a string that the field
19402      * should start with or a RegExp to test against the field
19403      * @param {Boolean} anyMatch True to match any part not just the beginning
19404      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19405      */
19406     query : function(property, value, anyMatch){
19407         var fn = this.createFilterFn(property, value, anyMatch);
19408         return fn ? this.queryBy(fn) : this.data.clone();
19409     },
19410
19411     /**
19412      * Query by a function. The specified function will be called with each
19413      * record in this data source. If the function returns true the record is included
19414      * in the results.
19415      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19416      * @param {Object} scope (optional) The scope of the function (defaults to this)
19417       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19418      **/
19419     queryBy : function(fn, scope){
19420         var data = this.snapshot || this.data;
19421         return data.filterBy(fn, scope||this);
19422     },
19423
19424     /**
19425      * Collects unique values for a particular dataIndex from this store.
19426      * @param {String} dataIndex The property to collect
19427      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19428      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19429      * @return {Array} An array of the unique values
19430      **/
19431     collect : function(dataIndex, allowNull, bypassFilter){
19432         var d = (bypassFilter === true && this.snapshot) ?
19433                 this.snapshot.items : this.data.items;
19434         var v, sv, r = [], l = {};
19435         for(var i = 0, len = d.length; i < len; i++){
19436             v = d[i].data[dataIndex];
19437             sv = String(v);
19438             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19439                 l[sv] = true;
19440                 r[r.length] = v;
19441             }
19442         }
19443         return r;
19444     },
19445
19446     /**
19447      * Revert to a view of the Record cache with no filtering applied.
19448      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19449      */
19450     clearFilter : function(suppressEvent){
19451         if(this.snapshot && this.snapshot != this.data){
19452             this.data = this.snapshot;
19453             delete this.snapshot;
19454             if(suppressEvent !== true){
19455                 this.fireEvent("datachanged", this);
19456             }
19457         }
19458     },
19459
19460     // private
19461     afterEdit : function(record){
19462         if(this.modified.indexOf(record) == -1){
19463             this.modified.push(record);
19464         }
19465         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19466     },
19467
19468     // private
19469     afterReject : function(record){
19470         this.modified.remove(record);
19471         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19472     },
19473
19474     // private
19475     afterCommit : function(record){
19476         this.modified.remove(record);
19477         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19478     },
19479
19480     /**
19481      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19482      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19483      */
19484     commitChanges : function(){
19485         var m = this.modified.slice(0);
19486         this.modified = [];
19487         for(var i = 0, len = m.length; i < len; i++){
19488             m[i].commit();
19489         }
19490     },
19491
19492     /**
19493      * Cancel outstanding changes on all changed records.
19494      */
19495     rejectChanges : function(){
19496         var m = this.modified.slice(0);
19497         this.modified = [];
19498         for(var i = 0, len = m.length; i < len; i++){
19499             m[i].reject();
19500         }
19501     },
19502
19503     onMetaChange : function(meta, rtype, o){
19504         this.recordType = rtype;
19505         this.fields = rtype.prototype.fields;
19506         delete this.snapshot;
19507         this.sortInfo = meta.sortInfo;
19508         this.modified = [];
19509         this.fireEvent('metachange', this, this.reader.meta);
19510     }
19511 });/*
19512  * Based on:
19513  * Ext JS Library 1.1.1
19514  * Copyright(c) 2006-2007, Ext JS, LLC.
19515  *
19516  * Originally Released Under LGPL - original licence link has changed is not relivant.
19517  *
19518  * Fork - LGPL
19519  * <script type="text/javascript">
19520  */
19521
19522 /**
19523  * @class Roo.data.SimpleStore
19524  * @extends Roo.data.Store
19525  * Small helper class to make creating Stores from Array data easier.
19526  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19527  * @cfg {Array} fields An array of field definition objects, or field name strings.
19528  * @cfg {Array} data The multi-dimensional array of data
19529  * @constructor
19530  * @param {Object} config
19531  */
19532 Roo.data.SimpleStore = function(config){
19533     Roo.data.SimpleStore.superclass.constructor.call(this, {
19534         isLocal : true,
19535         reader: new Roo.data.ArrayReader({
19536                 id: config.id
19537             },
19538             Roo.data.Record.create(config.fields)
19539         ),
19540         proxy : new Roo.data.MemoryProxy(config.data)
19541     });
19542     this.load();
19543 };
19544 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19545  * Based on:
19546  * Ext JS Library 1.1.1
19547  * Copyright(c) 2006-2007, Ext JS, LLC.
19548  *
19549  * Originally Released Under LGPL - original licence link has changed is not relivant.
19550  *
19551  * Fork - LGPL
19552  * <script type="text/javascript">
19553  */
19554
19555 /**
19556 /**
19557  * @extends Roo.data.Store
19558  * @class Roo.data.JsonStore
19559  * Small helper class to make creating Stores for JSON data easier. <br/>
19560 <pre><code>
19561 var store = new Roo.data.JsonStore({
19562     url: 'get-images.php',
19563     root: 'images',
19564     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19565 });
19566 </code></pre>
19567  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19568  * JsonReader and HttpProxy (unless inline data is provided).</b>
19569  * @cfg {Array} fields An array of field definition objects, or field name strings.
19570  * @constructor
19571  * @param {Object} config
19572  */
19573 Roo.data.JsonStore = function(c){
19574     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19575         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19576         reader: new Roo.data.JsonReader(c, c.fields)
19577     }));
19578 };
19579 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19580  * Based on:
19581  * Ext JS Library 1.1.1
19582  * Copyright(c) 2006-2007, Ext JS, LLC.
19583  *
19584  * Originally Released Under LGPL - original licence link has changed is not relivant.
19585  *
19586  * Fork - LGPL
19587  * <script type="text/javascript">
19588  */
19589
19590  
19591 Roo.data.Field = function(config){
19592     if(typeof config == "string"){
19593         config = {name: config};
19594     }
19595     Roo.apply(this, config);
19596     
19597     if(!this.type){
19598         this.type = "auto";
19599     }
19600     
19601     var st = Roo.data.SortTypes;
19602     // named sortTypes are supported, here we look them up
19603     if(typeof this.sortType == "string"){
19604         this.sortType = st[this.sortType];
19605     }
19606     
19607     // set default sortType for strings and dates
19608     if(!this.sortType){
19609         switch(this.type){
19610             case "string":
19611                 this.sortType = st.asUCString;
19612                 break;
19613             case "date":
19614                 this.sortType = st.asDate;
19615                 break;
19616             default:
19617                 this.sortType = st.none;
19618         }
19619     }
19620
19621     // define once
19622     var stripRe = /[\$,%]/g;
19623
19624     // prebuilt conversion function for this field, instead of
19625     // switching every time we're reading a value
19626     if(!this.convert){
19627         var cv, dateFormat = this.dateFormat;
19628         switch(this.type){
19629             case "":
19630             case "auto":
19631             case undefined:
19632                 cv = function(v){ return v; };
19633                 break;
19634             case "string":
19635                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19636                 break;
19637             case "int":
19638                 cv = function(v){
19639                     return v !== undefined && v !== null && v !== '' ?
19640                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19641                     };
19642                 break;
19643             case "float":
19644                 cv = function(v){
19645                     return v !== undefined && v !== null && v !== '' ?
19646                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19647                     };
19648                 break;
19649             case "bool":
19650             case "boolean":
19651                 cv = function(v){ return v === true || v === "true" || v == 1; };
19652                 break;
19653             case "date":
19654                 cv = function(v){
19655                     if(!v){
19656                         return '';
19657                     }
19658                     if(v instanceof Date){
19659                         return v;
19660                     }
19661                     if(dateFormat){
19662                         if(dateFormat == "timestamp"){
19663                             return new Date(v*1000);
19664                         }
19665                         return Date.parseDate(v, dateFormat);
19666                     }
19667                     var parsed = Date.parse(v);
19668                     return parsed ? new Date(parsed) : null;
19669                 };
19670              break;
19671             
19672         }
19673         this.convert = cv;
19674     }
19675 };
19676
19677 Roo.data.Field.prototype = {
19678     dateFormat: null,
19679     defaultValue: "",
19680     mapping: null,
19681     sortType : null,
19682     sortDir : "ASC"
19683 };/*
19684  * Based on:
19685  * Ext JS Library 1.1.1
19686  * Copyright(c) 2006-2007, Ext JS, LLC.
19687  *
19688  * Originally Released Under LGPL - original licence link has changed is not relivant.
19689  *
19690  * Fork - LGPL
19691  * <script type="text/javascript">
19692  */
19693  
19694 // Base class for reading structured data from a data source.  This class is intended to be
19695 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19696
19697 /**
19698  * @class Roo.data.DataReader
19699  * Base class for reading structured data from a data source.  This class is intended to be
19700  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19701  */
19702
19703 Roo.data.DataReader = function(meta, recordType){
19704     
19705     this.meta = meta;
19706     
19707     this.recordType = recordType instanceof Array ? 
19708         Roo.data.Record.create(recordType) : recordType;
19709 };
19710
19711 Roo.data.DataReader.prototype = {
19712      /**
19713      * Create an empty record
19714      * @param {Object} data (optional) - overlay some values
19715      * @return {Roo.data.Record} record created.
19716      */
19717     newRow :  function(d) {
19718         var da =  {};
19719         this.recordType.prototype.fields.each(function(c) {
19720             switch( c.type) {
19721                 case 'int' : da[c.name] = 0; break;
19722                 case 'date' : da[c.name] = new Date(); break;
19723                 case 'float' : da[c.name] = 0.0; break;
19724                 case 'boolean' : da[c.name] = false; break;
19725                 default : da[c.name] = ""; break;
19726             }
19727             
19728         });
19729         return new this.recordType(Roo.apply(da, d));
19730     }
19731     
19732 };/*
19733  * Based on:
19734  * Ext JS Library 1.1.1
19735  * Copyright(c) 2006-2007, Ext JS, LLC.
19736  *
19737  * Originally Released Under LGPL - original licence link has changed is not relivant.
19738  *
19739  * Fork - LGPL
19740  * <script type="text/javascript">
19741  */
19742
19743 /**
19744  * @class Roo.data.DataProxy
19745  * @extends Roo.data.Observable
19746  * This class is an abstract base class for implementations which provide retrieval of
19747  * unformatted data objects.<br>
19748  * <p>
19749  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19750  * (of the appropriate type which knows how to parse the data object) to provide a block of
19751  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19752  * <p>
19753  * Custom implementations must implement the load method as described in
19754  * {@link Roo.data.HttpProxy#load}.
19755  */
19756 Roo.data.DataProxy = function(){
19757     this.addEvents({
19758         /**
19759          * @event beforeload
19760          * Fires before a network request is made to retrieve a data object.
19761          * @param {Object} This DataProxy object.
19762          * @param {Object} params The params parameter to the load function.
19763          */
19764         beforeload : true,
19765         /**
19766          * @event load
19767          * Fires before the load method's callback is called.
19768          * @param {Object} This DataProxy object.
19769          * @param {Object} o The data object.
19770          * @param {Object} arg The callback argument object passed to the load function.
19771          */
19772         load : true,
19773         /**
19774          * @event loadexception
19775          * Fires if an Exception occurs during data retrieval.
19776          * @param {Object} This DataProxy object.
19777          * @param {Object} o The data object.
19778          * @param {Object} arg The callback argument object passed to the load function.
19779          * @param {Object} e The Exception.
19780          */
19781         loadexception : true
19782     });
19783     Roo.data.DataProxy.superclass.constructor.call(this);
19784 };
19785
19786 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19787
19788     /**
19789      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19790      */
19791 /*
19792  * Based on:
19793  * Ext JS Library 1.1.1
19794  * Copyright(c) 2006-2007, Ext JS, LLC.
19795  *
19796  * Originally Released Under LGPL - original licence link has changed is not relivant.
19797  *
19798  * Fork - LGPL
19799  * <script type="text/javascript">
19800  */
19801 /**
19802  * @class Roo.data.MemoryProxy
19803  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19804  * to the Reader when its load method is called.
19805  * @constructor
19806  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19807  */
19808 Roo.data.MemoryProxy = function(data){
19809     if (data.data) {
19810         data = data.data;
19811     }
19812     Roo.data.MemoryProxy.superclass.constructor.call(this);
19813     this.data = data;
19814 };
19815
19816 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19817     /**
19818      * Load data from the requested source (in this case an in-memory
19819      * data object passed to the constructor), read the data object into
19820      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19821      * process that block using the passed callback.
19822      * @param {Object} params This parameter is not used by the MemoryProxy class.
19823      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19824      * object into a block of Roo.data.Records.
19825      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19826      * The function must be passed <ul>
19827      * <li>The Record block object</li>
19828      * <li>The "arg" argument from the load function</li>
19829      * <li>A boolean success indicator</li>
19830      * </ul>
19831      * @param {Object} scope The scope in which to call the callback
19832      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19833      */
19834     load : function(params, reader, callback, scope, arg){
19835         params = params || {};
19836         var result;
19837         try {
19838             result = reader.readRecords(this.data);
19839         }catch(e){
19840             this.fireEvent("loadexception", this, arg, null, e);
19841             callback.call(scope, null, arg, false);
19842             return;
19843         }
19844         callback.call(scope, result, arg, true);
19845     },
19846     
19847     // private
19848     update : function(params, records){
19849         
19850     }
19851 });/*
19852  * Based on:
19853  * Ext JS Library 1.1.1
19854  * Copyright(c) 2006-2007, Ext JS, LLC.
19855  *
19856  * Originally Released Under LGPL - original licence link has changed is not relivant.
19857  *
19858  * Fork - LGPL
19859  * <script type="text/javascript">
19860  */
19861 /**
19862  * @class Roo.data.HttpProxy
19863  * @extends Roo.data.DataProxy
19864  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19865  * configured to reference a certain URL.<br><br>
19866  * <p>
19867  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19868  * from which the running page was served.<br><br>
19869  * <p>
19870  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19871  * <p>
19872  * Be aware that to enable the browser to parse an XML document, the server must set
19873  * the Content-Type header in the HTTP response to "text/xml".
19874  * @constructor
19875  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19876  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19877  * will be used to make the request.
19878  */
19879 Roo.data.HttpProxy = function(conn){
19880     Roo.data.HttpProxy.superclass.constructor.call(this);
19881     // is conn a conn config or a real conn?
19882     this.conn = conn;
19883     this.useAjax = !conn || !conn.events;
19884   
19885 };
19886
19887 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19888     // thse are take from connection...
19889     
19890     /**
19891      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19892      */
19893     /**
19894      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19895      * extra parameters to each request made by this object. (defaults to undefined)
19896      */
19897     /**
19898      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19899      *  to each request made by this object. (defaults to undefined)
19900      */
19901     /**
19902      * @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)
19903      */
19904     /**
19905      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
19906      */
19907      /**
19908      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
19909      * @type Boolean
19910      */
19911   
19912
19913     /**
19914      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
19915      * @type Boolean
19916      */
19917     /**
19918      * Return the {@link Roo.data.Connection} object being used by this Proxy.
19919      * @return {Connection} The Connection object. This object may be used to subscribe to events on
19920      * a finer-grained basis than the DataProxy events.
19921      */
19922     getConnection : function(){
19923         return this.useAjax ? Roo.Ajax : this.conn;
19924     },
19925
19926     /**
19927      * Load data from the configured {@link Roo.data.Connection}, read the data object into
19928      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
19929      * process that block using the passed callback.
19930      * @param {Object} params An object containing properties which are to be used as HTTP parameters
19931      * for the request to the remote server.
19932      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19933      * object into a block of Roo.data.Records.
19934      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
19935      * The function must be passed <ul>
19936      * <li>The Record block object</li>
19937      * <li>The "arg" argument from the load function</li>
19938      * <li>A boolean success indicator</li>
19939      * </ul>
19940      * @param {Object} scope The scope in which to call the callback
19941      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19942      */
19943     load : function(params, reader, callback, scope, arg){
19944         if(this.fireEvent("beforeload", this, params) !== false){
19945             var  o = {
19946                 params : params || {},
19947                 request: {
19948                     callback : callback,
19949                     scope : scope,
19950                     arg : arg
19951                 },
19952                 reader: reader,
19953                 callback : this.loadResponse,
19954                 scope: this
19955             };
19956             if(this.useAjax){
19957                 Roo.applyIf(o, this.conn);
19958                 if(this.activeRequest){
19959                     Roo.Ajax.abort(this.activeRequest);
19960                 }
19961                 this.activeRequest = Roo.Ajax.request(o);
19962             }else{
19963                 this.conn.request(o);
19964             }
19965         }else{
19966             callback.call(scope||this, null, arg, false);
19967         }
19968     },
19969
19970     // private
19971     loadResponse : function(o, success, response){
19972         delete this.activeRequest;
19973         if(!success){
19974             this.fireEvent("loadexception", this, o, response);
19975             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19976             return;
19977         }
19978         var result;
19979         try {
19980             result = o.reader.read(response);
19981         }catch(e){
19982             this.fireEvent("loadexception", this, o, response, e);
19983             o.request.callback.call(o.request.scope, null, o.request.arg, false);
19984             return;
19985         }
19986         
19987         this.fireEvent("load", this, o, o.request.arg);
19988         o.request.callback.call(o.request.scope, result, o.request.arg, true);
19989     },
19990
19991     // private
19992     update : function(dataSet){
19993
19994     },
19995
19996     // private
19997     updateResponse : function(dataSet){
19998
19999     }
20000 });/*
20001  * Based on:
20002  * Ext JS Library 1.1.1
20003  * Copyright(c) 2006-2007, Ext JS, LLC.
20004  *
20005  * Originally Released Under LGPL - original licence link has changed is not relivant.
20006  *
20007  * Fork - LGPL
20008  * <script type="text/javascript">
20009  */
20010
20011 /**
20012  * @class Roo.data.ScriptTagProxy
20013  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20014  * other than the originating domain of the running page.<br><br>
20015  * <p>
20016  * <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
20017  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20018  * <p>
20019  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20020  * source code that is used as the source inside a &lt;script> tag.<br><br>
20021  * <p>
20022  * In order for the browser to process the returned data, the server must wrap the data object
20023  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20024  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20025  * depending on whether the callback name was passed:
20026  * <p>
20027  * <pre><code>
20028 boolean scriptTag = false;
20029 String cb = request.getParameter("callback");
20030 if (cb != null) {
20031     scriptTag = true;
20032     response.setContentType("text/javascript");
20033 } else {
20034     response.setContentType("application/x-json");
20035 }
20036 Writer out = response.getWriter();
20037 if (scriptTag) {
20038     out.write(cb + "(");
20039 }
20040 out.print(dataBlock.toJsonString());
20041 if (scriptTag) {
20042     out.write(");");
20043 }
20044 </pre></code>
20045  *
20046  * @constructor
20047  * @param {Object} config A configuration object.
20048  */
20049 Roo.data.ScriptTagProxy = function(config){
20050     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20051     Roo.apply(this, config);
20052     this.head = document.getElementsByTagName("head")[0];
20053 };
20054
20055 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20056
20057 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20058     /**
20059      * @cfg {String} url The URL from which to request the data object.
20060      */
20061     /**
20062      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20063      */
20064     timeout : 30000,
20065     /**
20066      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20067      * the server the name of the callback function set up by the load call to process the returned data object.
20068      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20069      * javascript output which calls this named function passing the data object as its only parameter.
20070      */
20071     callbackParam : "callback",
20072     /**
20073      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20074      * name to the request.
20075      */
20076     nocache : true,
20077
20078     /**
20079      * Load data from the configured URL, read the data object into
20080      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20081      * process that block using the passed callback.
20082      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20083      * for the request to the remote server.
20084      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20085      * object into a block of Roo.data.Records.
20086      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20087      * The function must be passed <ul>
20088      * <li>The Record block object</li>
20089      * <li>The "arg" argument from the load function</li>
20090      * <li>A boolean success indicator</li>
20091      * </ul>
20092      * @param {Object} scope The scope in which to call the callback
20093      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20094      */
20095     load : function(params, reader, callback, scope, arg){
20096         if(this.fireEvent("beforeload", this, params) !== false){
20097
20098             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20099
20100             var url = this.url;
20101             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20102             if(this.nocache){
20103                 url += "&_dc=" + (new Date().getTime());
20104             }
20105             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20106             var trans = {
20107                 id : transId,
20108                 cb : "stcCallback"+transId,
20109                 scriptId : "stcScript"+transId,
20110                 params : params,
20111                 arg : arg,
20112                 url : url,
20113                 callback : callback,
20114                 scope : scope,
20115                 reader : reader
20116             };
20117             var conn = this;
20118
20119             window[trans.cb] = function(o){
20120                 conn.handleResponse(o, trans);
20121             };
20122
20123             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20124
20125             if(this.autoAbort !== false){
20126                 this.abort();
20127             }
20128
20129             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20130
20131             var script = document.createElement("script");
20132             script.setAttribute("src", url);
20133             script.setAttribute("type", "text/javascript");
20134             script.setAttribute("id", trans.scriptId);
20135             this.head.appendChild(script);
20136
20137             this.trans = trans;
20138         }else{
20139             callback.call(scope||this, null, arg, false);
20140         }
20141     },
20142
20143     // private
20144     isLoading : function(){
20145         return this.trans ? true : false;
20146     },
20147
20148     /**
20149      * Abort the current server request.
20150      */
20151     abort : function(){
20152         if(this.isLoading()){
20153             this.destroyTrans(this.trans);
20154         }
20155     },
20156
20157     // private
20158     destroyTrans : function(trans, isLoaded){
20159         this.head.removeChild(document.getElementById(trans.scriptId));
20160         clearTimeout(trans.timeoutId);
20161         if(isLoaded){
20162             window[trans.cb] = undefined;
20163             try{
20164                 delete window[trans.cb];
20165             }catch(e){}
20166         }else{
20167             // if hasn't been loaded, wait for load to remove it to prevent script error
20168             window[trans.cb] = function(){
20169                 window[trans.cb] = undefined;
20170                 try{
20171                     delete window[trans.cb];
20172                 }catch(e){}
20173             };
20174         }
20175     },
20176
20177     // private
20178     handleResponse : function(o, trans){
20179         this.trans = false;
20180         this.destroyTrans(trans, true);
20181         var result;
20182         try {
20183             result = trans.reader.readRecords(o);
20184         }catch(e){
20185             this.fireEvent("loadexception", this, o, trans.arg, e);
20186             trans.callback.call(trans.scope||window, null, trans.arg, false);
20187             return;
20188         }
20189         this.fireEvent("load", this, o, trans.arg);
20190         trans.callback.call(trans.scope||window, result, trans.arg, true);
20191     },
20192
20193     // private
20194     handleFailure : function(trans){
20195         this.trans = false;
20196         this.destroyTrans(trans, false);
20197         this.fireEvent("loadexception", this, null, trans.arg);
20198         trans.callback.call(trans.scope||window, null, trans.arg, false);
20199     }
20200 });/*
20201  * Based on:
20202  * Ext JS Library 1.1.1
20203  * Copyright(c) 2006-2007, Ext JS, LLC.
20204  *
20205  * Originally Released Under LGPL - original licence link has changed is not relivant.
20206  *
20207  * Fork - LGPL
20208  * <script type="text/javascript">
20209  */
20210
20211 /**
20212  * @class Roo.data.JsonReader
20213  * @extends Roo.data.DataReader
20214  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20215  * based on mappings in a provided Roo.data.Record constructor.
20216  * <p>
20217  * Example code:
20218  * <pre><code>
20219 var RecordDef = Roo.data.Record.create([
20220     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20221     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20222 ]);
20223 var myReader = new Roo.data.JsonReader({
20224     totalProperty: "results",    // The property which contains the total dataset size (optional)
20225     root: "rows",                // The property which contains an Array of row objects
20226     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20227 }, RecordDef);
20228 </code></pre>
20229  * <p>
20230  * This would consume a JSON file like this:
20231  * <pre><code>
20232 { 'results': 2, 'rows': [
20233     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20234     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20235 }
20236 </code></pre>
20237  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20238  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20239  * paged from the remote server.
20240  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20241  * @cfg {String} root name of the property which contains the Array of row objects.
20242  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20243  * @constructor
20244  * Create a new JsonReader
20245  * @param {Object} meta Metadata configuration options
20246  * @param {Object} recordType Either an Array of field definition objects,
20247  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20248  */
20249 Roo.data.JsonReader = function(meta, recordType){
20250     
20251     meta = meta || {};
20252     // set some defaults:
20253     Roo.applyIf(meta, {
20254         totalProperty: 'total',
20255         successProperty : 'success',
20256         root : 'data',
20257         id : 'id'
20258     });
20259     
20260     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20261 };
20262 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20263     /**
20264      * This method is only used by a DataProxy which has retrieved data from a remote server.
20265      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20266      * @return {Object} data A data block which is used by an Roo.data.Store object as
20267      * a cache of Roo.data.Records.
20268      */
20269     read : function(response){
20270         var json = response.responseText;
20271         /* eval:var:o */
20272         var o = eval("("+json+")");
20273         if(!o) {
20274             throw {message: "JsonReader.read: Json object not found"};
20275         }
20276         
20277         if(o.metaData){
20278             delete this.ef;
20279             this.meta = o.metaData;
20280             this.recordType = Roo.data.Record.create(o.metaData.fields);
20281             this.onMetaChange(this.meta, this.recordType, o);
20282         }
20283         return this.readRecords(o);
20284     },
20285
20286     // private function a store will implement
20287     onMetaChange : function(meta, recordType, o){
20288
20289     },
20290
20291     /**
20292          * @ignore
20293          */
20294     simpleAccess: function(obj, subsc) {
20295         return obj[subsc];
20296     },
20297
20298         /**
20299          * @ignore
20300          */
20301     getJsonAccessor: function(){
20302         var re = /[\[\.]/;
20303         return function(expr) {
20304             try {
20305                 return(re.test(expr))
20306                     ? new Function("obj", "return obj." + expr)
20307                     : function(obj){
20308                         return obj[expr];
20309                     };
20310             } catch(e){}
20311             return Roo.emptyFn;
20312         };
20313     }(),
20314
20315     /**
20316      * Create a data block containing Roo.data.Records from an XML document.
20317      * @param {Object} o An object which contains an Array of row objects in the property specified
20318      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20319      * which contains the total size of the dataset.
20320      * @return {Object} data A data block which is used by an Roo.data.Store object as
20321      * a cache of Roo.data.Records.
20322      */
20323     readRecords : function(o){
20324         /**
20325          * After any data loads, the raw JSON data is available for further custom processing.
20326          * @type Object
20327          */
20328         this.jsonData = o;
20329         var s = this.meta, Record = this.recordType,
20330             f = Record.prototype.fields, fi = f.items, fl = f.length;
20331
20332 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20333         if (!this.ef) {
20334             if(s.totalProperty) {
20335                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20336                 }
20337                 if(s.successProperty) {
20338                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20339                 }
20340                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20341                 if (s.id) {
20342                         var g = this.getJsonAccessor(s.id);
20343                         this.getId = function(rec) {
20344                                 var r = g(rec);
20345                                 return (r === undefined || r === "") ? null : r;
20346                         };
20347                 } else {
20348                         this.getId = function(){return null;};
20349                 }
20350             this.ef = [];
20351             for(var i = 0; i < fl; i++){
20352                 f = fi[i];
20353                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20354                 this.ef[i] = this.getJsonAccessor(map);
20355             }
20356         }
20357
20358         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20359         if(s.totalProperty){
20360             var v = parseInt(this.getTotal(o), 10);
20361             if(!isNaN(v)){
20362                 totalRecords = v;
20363             }
20364         }
20365         if(s.successProperty){
20366             var v = this.getSuccess(o);
20367             if(v === false || v === 'false'){
20368                 success = false;
20369             }
20370         }
20371         var records = [];
20372             for(var i = 0; i < c; i++){
20373                     var n = root[i];
20374                 var values = {};
20375                 var id = this.getId(n);
20376                 for(var j = 0; j < fl; j++){
20377                     f = fi[j];
20378                 var v = this.ef[j](n);
20379                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20380                 }
20381                 var record = new Record(values, id);
20382                 record.json = n;
20383                 records[i] = record;
20384             }
20385             return {
20386                 success : success,
20387                 records : records,
20388                 totalRecords : totalRecords
20389             };
20390     }
20391 });/*
20392  * Based on:
20393  * Ext JS Library 1.1.1
20394  * Copyright(c) 2006-2007, Ext JS, LLC.
20395  *
20396  * Originally Released Under LGPL - original licence link has changed is not relivant.
20397  *
20398  * Fork - LGPL
20399  * <script type="text/javascript">
20400  */
20401
20402 /**
20403  * @class Roo.data.XmlReader
20404  * @extends Roo.data.DataReader
20405  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20406  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20407  * <p>
20408  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20409  * header in the HTTP response must be set to "text/xml".</em>
20410  * <p>
20411  * Example code:
20412  * <pre><code>
20413 var RecordDef = Roo.data.Record.create([
20414    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20415    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20416 ]);
20417 var myReader = new Roo.data.XmlReader({
20418    totalRecords: "results", // The element which contains the total dataset size (optional)
20419    record: "row",           // The repeated element which contains row information
20420    id: "id"                 // The element within the row that provides an ID for the record (optional)
20421 }, RecordDef);
20422 </code></pre>
20423  * <p>
20424  * This would consume an XML file like this:
20425  * <pre><code>
20426 &lt;?xml?>
20427 &lt;dataset>
20428  &lt;results>2&lt;/results>
20429  &lt;row>
20430    &lt;id>1&lt;/id>
20431    &lt;name>Bill&lt;/name>
20432    &lt;occupation>Gardener&lt;/occupation>
20433  &lt;/row>
20434  &lt;row>
20435    &lt;id>2&lt;/id>
20436    &lt;name>Ben&lt;/name>
20437    &lt;occupation>Horticulturalist&lt;/occupation>
20438  &lt;/row>
20439 &lt;/dataset>
20440 </code></pre>
20441  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20442  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20443  * paged from the remote server.
20444  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20445  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20446  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20447  * a record identifier value.
20448  * @constructor
20449  * Create a new XmlReader
20450  * @param {Object} meta Metadata configuration options
20451  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20452  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20453  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20454  */
20455 Roo.data.XmlReader = function(meta, recordType){
20456     meta = meta || {};
20457     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20458 };
20459 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20460     /**
20461      * This method is only used by a DataProxy which has retrieved data from a remote server.
20462          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20463          * to contain a method called 'responseXML' that returns an XML document object.
20464      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20465      * a cache of Roo.data.Records.
20466      */
20467     read : function(response){
20468         var doc = response.responseXML;
20469         if(!doc) {
20470             throw {message: "XmlReader.read: XML Document not available"};
20471         }
20472         return this.readRecords(doc);
20473     },
20474
20475     /**
20476      * Create a data block containing Roo.data.Records from an XML document.
20477          * @param {Object} doc A parsed XML document.
20478      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20479      * a cache of Roo.data.Records.
20480      */
20481     readRecords : function(doc){
20482         /**
20483          * After any data loads/reads, the raw XML Document is available for further custom processing.
20484          * @type XMLDocument
20485          */
20486         this.xmlData = doc;
20487         var root = doc.documentElement || doc;
20488         var q = Roo.DomQuery;
20489         var recordType = this.recordType, fields = recordType.prototype.fields;
20490         var sid = this.meta.id;
20491         var totalRecords = 0, success = true;
20492         if(this.meta.totalRecords){
20493             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20494         }
20495         
20496         if(this.meta.success){
20497             var sv = q.selectValue(this.meta.success, root, true);
20498             success = sv !== false && sv !== 'false';
20499         }
20500         var records = [];
20501         var ns = q.select(this.meta.record, root);
20502         for(var i = 0, len = ns.length; i < len; i++) {
20503                 var n = ns[i];
20504                 var values = {};
20505                 var id = sid ? q.selectValue(sid, n) : undefined;
20506                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20507                     var f = fields.items[j];
20508                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20509                     v = f.convert(v);
20510                     values[f.name] = v;
20511                 }
20512                 var record = new recordType(values, id);
20513                 record.node = n;
20514                 records[records.length] = record;
20515             }
20516
20517             return {
20518                 success : success,
20519                 records : records,
20520                 totalRecords : totalRecords || records.length
20521             };
20522     }
20523 });/*
20524  * Based on:
20525  * Ext JS Library 1.1.1
20526  * Copyright(c) 2006-2007, Ext JS, LLC.
20527  *
20528  * Originally Released Under LGPL - original licence link has changed is not relivant.
20529  *
20530  * Fork - LGPL
20531  * <script type="text/javascript">
20532  */
20533
20534 /**
20535  * @class Roo.data.ArrayReader
20536  * @extends Roo.data.DataReader
20537  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20538  * Each element of that Array represents a row of data fields. The
20539  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20540  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20541  * <p>
20542  * Example code:.
20543  * <pre><code>
20544 var RecordDef = Roo.data.Record.create([
20545     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20546     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20547 ]);
20548 var myReader = new Roo.data.ArrayReader({
20549     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20550 }, RecordDef);
20551 </code></pre>
20552  * <p>
20553  * This would consume an Array like this:
20554  * <pre><code>
20555 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20556   </code></pre>
20557  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20558  * @constructor
20559  * Create a new JsonReader
20560  * @param {Object} meta Metadata configuration options.
20561  * @param {Object} recordType Either an Array of field definition objects
20562  * as specified to {@link Roo.data.Record#create},
20563  * or an {@link Roo.data.Record} object
20564  * created using {@link Roo.data.Record#create}.
20565  */
20566 Roo.data.ArrayReader = function(meta, recordType){
20567     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20568 };
20569
20570 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20571     /**
20572      * Create a data block containing Roo.data.Records from an XML document.
20573      * @param {Object} o An Array of row objects which represents the dataset.
20574      * @return {Object} data A data block which is used by an Roo.data.Store object as
20575      * a cache of Roo.data.Records.
20576      */
20577     readRecords : function(o){
20578         var sid = this.meta ? this.meta.id : null;
20579         var recordType = this.recordType, fields = recordType.prototype.fields;
20580         var records = [];
20581         var root = o;
20582             for(var i = 0; i < root.length; i++){
20583                     var n = root[i];
20584                 var values = {};
20585                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20586                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20587                 var f = fields.items[j];
20588                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20589                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20590                 v = f.convert(v);
20591                 values[f.name] = v;
20592             }
20593                 var record = new recordType(values, id);
20594                 record.json = n;
20595                 records[records.length] = record;
20596             }
20597             return {
20598                 records : records,
20599                 totalRecords : records.length
20600             };
20601     }
20602 });/*
20603  * Based on:
20604  * Ext JS Library 1.1.1
20605  * Copyright(c) 2006-2007, Ext JS, LLC.
20606  *
20607  * Originally Released Under LGPL - original licence link has changed is not relivant.
20608  *
20609  * Fork - LGPL
20610  * <script type="text/javascript">
20611  */
20612
20613
20614 /**
20615  * @class Roo.data.Tree
20616  * @extends Roo.util.Observable
20617  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20618  * in the tree have most standard DOM functionality.
20619  * @constructor
20620  * @param {Node} root (optional) The root node
20621  */
20622 Roo.data.Tree = function(root){
20623    this.nodeHash = {};
20624    /**
20625     * The root node for this tree
20626     * @type Node
20627     */
20628    this.root = null;
20629    if(root){
20630        this.setRootNode(root);
20631    }
20632    this.addEvents({
20633        /**
20634         * @event append
20635         * Fires when a new child node is appended to a node in this tree.
20636         * @param {Tree} tree The owner tree
20637         * @param {Node} parent The parent node
20638         * @param {Node} node The newly appended node
20639         * @param {Number} index The index of the newly appended node
20640         */
20641        "append" : true,
20642        /**
20643         * @event remove
20644         * Fires when a child node is removed from a node in this tree.
20645         * @param {Tree} tree The owner tree
20646         * @param {Node} parent The parent node
20647         * @param {Node} node The child node removed
20648         */
20649        "remove" : true,
20650        /**
20651         * @event move
20652         * Fires when a node is moved to a new location in the tree
20653         * @param {Tree} tree The owner tree
20654         * @param {Node} node The node moved
20655         * @param {Node} oldParent The old parent of this node
20656         * @param {Node} newParent The new parent of this node
20657         * @param {Number} index The index it was moved to
20658         */
20659        "move" : true,
20660        /**
20661         * @event insert
20662         * Fires when a new child node is inserted in a node in this tree.
20663         * @param {Tree} tree The owner tree
20664         * @param {Node} parent The parent node
20665         * @param {Node} node The child node inserted
20666         * @param {Node} refNode The child node the node was inserted before
20667         */
20668        "insert" : true,
20669        /**
20670         * @event beforeappend
20671         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20672         * @param {Tree} tree The owner tree
20673         * @param {Node} parent The parent node
20674         * @param {Node} node The child node to be appended
20675         */
20676        "beforeappend" : true,
20677        /**
20678         * @event beforeremove
20679         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20680         * @param {Tree} tree The owner tree
20681         * @param {Node} parent The parent node
20682         * @param {Node} node The child node to be removed
20683         */
20684        "beforeremove" : true,
20685        /**
20686         * @event beforemove
20687         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20688         * @param {Tree} tree The owner tree
20689         * @param {Node} node The node being moved
20690         * @param {Node} oldParent The parent of the node
20691         * @param {Node} newParent The new parent the node is moving to
20692         * @param {Number} index The index it is being moved to
20693         */
20694        "beforemove" : true,
20695        /**
20696         * @event beforeinsert
20697         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20698         * @param {Tree} tree The owner tree
20699         * @param {Node} parent The parent node
20700         * @param {Node} node The child node to be inserted
20701         * @param {Node} refNode The child node the node is being inserted before
20702         */
20703        "beforeinsert" : true
20704    });
20705
20706     Roo.data.Tree.superclass.constructor.call(this);
20707 };
20708
20709 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20710     pathSeparator: "/",
20711
20712     proxyNodeEvent : function(){
20713         return this.fireEvent.apply(this, arguments);
20714     },
20715
20716     /**
20717      * Returns the root node for this tree.
20718      * @return {Node}
20719      */
20720     getRootNode : function(){
20721         return this.root;
20722     },
20723
20724     /**
20725      * Sets the root node for this tree.
20726      * @param {Node} node
20727      * @return {Node}
20728      */
20729     setRootNode : function(node){
20730         this.root = node;
20731         node.ownerTree = this;
20732         node.isRoot = true;
20733         this.registerNode(node);
20734         return node;
20735     },
20736
20737     /**
20738      * Gets a node in this tree by its id.
20739      * @param {String} id
20740      * @return {Node}
20741      */
20742     getNodeById : function(id){
20743         return this.nodeHash[id];
20744     },
20745
20746     registerNode : function(node){
20747         this.nodeHash[node.id] = node;
20748     },
20749
20750     unregisterNode : function(node){
20751         delete this.nodeHash[node.id];
20752     },
20753
20754     toString : function(){
20755         return "[Tree"+(this.id?" "+this.id:"")+"]";
20756     }
20757 });
20758
20759 /**
20760  * @class Roo.data.Node
20761  * @extends Roo.util.Observable
20762  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20763  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20764  * @constructor
20765  * @param {Object} attributes The attributes/config for the node
20766  */
20767 Roo.data.Node = function(attributes){
20768     /**
20769      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20770      * @type {Object}
20771      */
20772     this.attributes = attributes || {};
20773     this.leaf = this.attributes.leaf;
20774     /**
20775      * The node id. @type String
20776      */
20777     this.id = this.attributes.id;
20778     if(!this.id){
20779         this.id = Roo.id(null, "ynode-");
20780         this.attributes.id = this.id;
20781     }
20782     /**
20783      * All child nodes of this node. @type Array
20784      */
20785     this.childNodes = [];
20786     if(!this.childNodes.indexOf){ // indexOf is a must
20787         this.childNodes.indexOf = function(o){
20788             for(var i = 0, len = this.length; i < len; i++){
20789                 if(this[i] == o) return i;
20790             }
20791             return -1;
20792         };
20793     }
20794     /**
20795      * The parent node for this node. @type Node
20796      */
20797     this.parentNode = null;
20798     /**
20799      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20800      */
20801     this.firstChild = null;
20802     /**
20803      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20804      */
20805     this.lastChild = null;
20806     /**
20807      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20808      */
20809     this.previousSibling = null;
20810     /**
20811      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20812      */
20813     this.nextSibling = null;
20814
20815     this.addEvents({
20816        /**
20817         * @event append
20818         * Fires when a new child node is appended
20819         * @param {Tree} tree The owner tree
20820         * @param {Node} this This node
20821         * @param {Node} node The newly appended node
20822         * @param {Number} index The index of the newly appended node
20823         */
20824        "append" : true,
20825        /**
20826         * @event remove
20827         * Fires when a child node is removed
20828         * @param {Tree} tree The owner tree
20829         * @param {Node} this This node
20830         * @param {Node} node The removed node
20831         */
20832        "remove" : true,
20833        /**
20834         * @event move
20835         * Fires when this node is moved to a new location in the tree
20836         * @param {Tree} tree The owner tree
20837         * @param {Node} this This node
20838         * @param {Node} oldParent The old parent of this node
20839         * @param {Node} newParent The new parent of this node
20840         * @param {Number} index The index it was moved to
20841         */
20842        "move" : true,
20843        /**
20844         * @event insert
20845         * Fires when a new child node is inserted.
20846         * @param {Tree} tree The owner tree
20847         * @param {Node} this This node
20848         * @param {Node} node The child node inserted
20849         * @param {Node} refNode The child node the node was inserted before
20850         */
20851        "insert" : true,
20852        /**
20853         * @event beforeappend
20854         * Fires before a new child is appended, return false to cancel the append.
20855         * @param {Tree} tree The owner tree
20856         * @param {Node} this This node
20857         * @param {Node} node The child node to be appended
20858         */
20859        "beforeappend" : true,
20860        /**
20861         * @event beforeremove
20862         * Fires before a child is removed, return false to cancel the remove.
20863         * @param {Tree} tree The owner tree
20864         * @param {Node} this This node
20865         * @param {Node} node The child node to be removed
20866         */
20867        "beforeremove" : true,
20868        /**
20869         * @event beforemove
20870         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20871         * @param {Tree} tree The owner tree
20872         * @param {Node} this This node
20873         * @param {Node} oldParent The parent of this node
20874         * @param {Node} newParent The new parent this node is moving to
20875         * @param {Number} index The index it is being moved to
20876         */
20877        "beforemove" : true,
20878        /**
20879         * @event beforeinsert
20880         * Fires before a new child is inserted, return false to cancel the insert.
20881         * @param {Tree} tree The owner tree
20882         * @param {Node} this This node
20883         * @param {Node} node The child node to be inserted
20884         * @param {Node} refNode The child node the node is being inserted before
20885         */
20886        "beforeinsert" : true
20887    });
20888     this.listeners = this.attributes.listeners;
20889     Roo.data.Node.superclass.constructor.call(this);
20890 };
20891
20892 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20893     fireEvent : function(evtName){
20894         // first do standard event for this node
20895         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20896             return false;
20897         }
20898         // then bubble it up to the tree if the event wasn't cancelled
20899         var ot = this.getOwnerTree();
20900         if(ot){
20901             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20902                 return false;
20903             }
20904         }
20905         return true;
20906     },
20907
20908     /**
20909      * Returns true if this node is a leaf
20910      * @return {Boolean}
20911      */
20912     isLeaf : function(){
20913         return this.leaf === true;
20914     },
20915
20916     // private
20917     setFirstChild : function(node){
20918         this.firstChild = node;
20919     },
20920
20921     //private
20922     setLastChild : function(node){
20923         this.lastChild = node;
20924     },
20925
20926
20927     /**
20928      * Returns true if this node is the last child of its parent
20929      * @return {Boolean}
20930      */
20931     isLast : function(){
20932        return (!this.parentNode ? true : this.parentNode.lastChild == this);
20933     },
20934
20935     /**
20936      * Returns true if this node is the first child of its parent
20937      * @return {Boolean}
20938      */
20939     isFirst : function(){
20940        return (!this.parentNode ? true : this.parentNode.firstChild == this);
20941     },
20942
20943     hasChildNodes : function(){
20944         return !this.isLeaf() && this.childNodes.length > 0;
20945     },
20946
20947     /**
20948      * Insert node(s) as the last child node of this node.
20949      * @param {Node/Array} node The node or Array of nodes to append
20950      * @return {Node} The appended node if single append, or null if an array was passed
20951      */
20952     appendChild : function(node){
20953         var multi = false;
20954         if(node instanceof Array){
20955             multi = node;
20956         }else if(arguments.length > 1){
20957             multi = arguments;
20958         }
20959         // if passed an array or multiple args do them one by one
20960         if(multi){
20961             for(var i = 0, len = multi.length; i < len; i++) {
20962                 this.appendChild(multi[i]);
20963             }
20964         }else{
20965             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
20966                 return false;
20967             }
20968             var index = this.childNodes.length;
20969             var oldParent = node.parentNode;
20970             // it's a move, make sure we move it cleanly
20971             if(oldParent){
20972                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
20973                     return false;
20974                 }
20975                 oldParent.removeChild(node);
20976             }
20977             index = this.childNodes.length;
20978             if(index == 0){
20979                 this.setFirstChild(node);
20980             }
20981             this.childNodes.push(node);
20982             node.parentNode = this;
20983             var ps = this.childNodes[index-1];
20984             if(ps){
20985                 node.previousSibling = ps;
20986                 ps.nextSibling = node;
20987             }else{
20988                 node.previousSibling = null;
20989             }
20990             node.nextSibling = null;
20991             this.setLastChild(node);
20992             node.setOwnerTree(this.getOwnerTree());
20993             this.fireEvent("append", this.ownerTree, this, node, index);
20994             if(oldParent){
20995                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
20996             }
20997             return node;
20998         }
20999     },
21000
21001     /**
21002      * Removes a child node from this node.
21003      * @param {Node} node The node to remove
21004      * @return {Node} The removed node
21005      */
21006     removeChild : function(node){
21007         var index = this.childNodes.indexOf(node);
21008         if(index == -1){
21009             return false;
21010         }
21011         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21012             return false;
21013         }
21014
21015         // remove it from childNodes collection
21016         this.childNodes.splice(index, 1);
21017
21018         // update siblings
21019         if(node.previousSibling){
21020             node.previousSibling.nextSibling = node.nextSibling;
21021         }
21022         if(node.nextSibling){
21023             node.nextSibling.previousSibling = node.previousSibling;
21024         }
21025
21026         // update child refs
21027         if(this.firstChild == node){
21028             this.setFirstChild(node.nextSibling);
21029         }
21030         if(this.lastChild == node){
21031             this.setLastChild(node.previousSibling);
21032         }
21033
21034         node.setOwnerTree(null);
21035         // clear any references from the node
21036         node.parentNode = null;
21037         node.previousSibling = null;
21038         node.nextSibling = null;
21039         this.fireEvent("remove", this.ownerTree, this, node);
21040         return node;
21041     },
21042
21043     /**
21044      * Inserts the first node before the second node in this nodes childNodes collection.
21045      * @param {Node} node The node to insert
21046      * @param {Node} refNode The node to insert before (if null the node is appended)
21047      * @return {Node} The inserted node
21048      */
21049     insertBefore : function(node, refNode){
21050         if(!refNode){ // like standard Dom, refNode can be null for append
21051             return this.appendChild(node);
21052         }
21053         // nothing to do
21054         if(node == refNode){
21055             return false;
21056         }
21057
21058         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21059             return false;
21060         }
21061         var index = this.childNodes.indexOf(refNode);
21062         var oldParent = node.parentNode;
21063         var refIndex = index;
21064
21065         // when moving internally, indexes will change after remove
21066         if(oldParent == this && this.childNodes.indexOf(node) < index){
21067             refIndex--;
21068         }
21069
21070         // it's a move, make sure we move it cleanly
21071         if(oldParent){
21072             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21073                 return false;
21074             }
21075             oldParent.removeChild(node);
21076         }
21077         if(refIndex == 0){
21078             this.setFirstChild(node);
21079         }
21080         this.childNodes.splice(refIndex, 0, node);
21081         node.parentNode = this;
21082         var ps = this.childNodes[refIndex-1];
21083         if(ps){
21084             node.previousSibling = ps;
21085             ps.nextSibling = node;
21086         }else{
21087             node.previousSibling = null;
21088         }
21089         node.nextSibling = refNode;
21090         refNode.previousSibling = node;
21091         node.setOwnerTree(this.getOwnerTree());
21092         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21093         if(oldParent){
21094             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21095         }
21096         return node;
21097     },
21098
21099     /**
21100      * Returns the child node at the specified index.
21101      * @param {Number} index
21102      * @return {Node}
21103      */
21104     item : function(index){
21105         return this.childNodes[index];
21106     },
21107
21108     /**
21109      * Replaces one child node in this node with another.
21110      * @param {Node} newChild The replacement node
21111      * @param {Node} oldChild The node to replace
21112      * @return {Node} The replaced node
21113      */
21114     replaceChild : function(newChild, oldChild){
21115         this.insertBefore(newChild, oldChild);
21116         this.removeChild(oldChild);
21117         return oldChild;
21118     },
21119
21120     /**
21121      * Returns the index of a child node
21122      * @param {Node} node
21123      * @return {Number} The index of the node or -1 if it was not found
21124      */
21125     indexOf : function(child){
21126         return this.childNodes.indexOf(child);
21127     },
21128
21129     /**
21130      * Returns the tree this node is in.
21131      * @return {Tree}
21132      */
21133     getOwnerTree : function(){
21134         // if it doesn't have one, look for one
21135         if(!this.ownerTree){
21136             var p = this;
21137             while(p){
21138                 if(p.ownerTree){
21139                     this.ownerTree = p.ownerTree;
21140                     break;
21141                 }
21142                 p = p.parentNode;
21143             }
21144         }
21145         return this.ownerTree;
21146     },
21147
21148     /**
21149      * Returns depth of this node (the root node has a depth of 0)
21150      * @return {Number}
21151      */
21152     getDepth : function(){
21153         var depth = 0;
21154         var p = this;
21155         while(p.parentNode){
21156             ++depth;
21157             p = p.parentNode;
21158         }
21159         return depth;
21160     },
21161
21162     // private
21163     setOwnerTree : function(tree){
21164         // if it's move, we need to update everyone
21165         if(tree != this.ownerTree){
21166             if(this.ownerTree){
21167                 this.ownerTree.unregisterNode(this);
21168             }
21169             this.ownerTree = tree;
21170             var cs = this.childNodes;
21171             for(var i = 0, len = cs.length; i < len; i++) {
21172                 cs[i].setOwnerTree(tree);
21173             }
21174             if(tree){
21175                 tree.registerNode(this);
21176             }
21177         }
21178     },
21179
21180     /**
21181      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21182      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21183      * @return {String} The path
21184      */
21185     getPath : function(attr){
21186         attr = attr || "id";
21187         var p = this.parentNode;
21188         var b = [this.attributes[attr]];
21189         while(p){
21190             b.unshift(p.attributes[attr]);
21191             p = p.parentNode;
21192         }
21193         var sep = this.getOwnerTree().pathSeparator;
21194         return sep + b.join(sep);
21195     },
21196
21197     /**
21198      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21199      * function call will be the scope provided or the current node. The arguments to the function
21200      * will be the args provided or the current node. If the function returns false at any point,
21201      * the bubble is stopped.
21202      * @param {Function} fn The function to call
21203      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21204      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21205      */
21206     bubble : function(fn, scope, args){
21207         var p = this;
21208         while(p){
21209             if(fn.call(scope || p, args || p) === false){
21210                 break;
21211             }
21212             p = p.parentNode;
21213         }
21214     },
21215
21216     /**
21217      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21218      * function call will be the scope provided or the current node. The arguments to the function
21219      * will be the args provided or the current node. If the function returns false at any point,
21220      * the cascade is stopped on that branch.
21221      * @param {Function} fn The function to call
21222      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21223      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21224      */
21225     cascade : function(fn, scope, args){
21226         if(fn.call(scope || this, args || this) !== false){
21227             var cs = this.childNodes;
21228             for(var i = 0, len = cs.length; i < len; i++) {
21229                 cs[i].cascade(fn, scope, args);
21230             }
21231         }
21232     },
21233
21234     /**
21235      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21236      * function call will be the scope provided or the current node. The arguments to the function
21237      * will be the args provided or the current node. If the function returns false at any point,
21238      * the iteration stops.
21239      * @param {Function} fn The function to call
21240      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21241      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21242      */
21243     eachChild : function(fn, scope, args){
21244         var cs = this.childNodes;
21245         for(var i = 0, len = cs.length; i < len; i++) {
21246                 if(fn.call(scope || this, args || cs[i]) === false){
21247                     break;
21248                 }
21249         }
21250     },
21251
21252     /**
21253      * Finds the first child that has the attribute with the specified value.
21254      * @param {String} attribute The attribute name
21255      * @param {Mixed} value The value to search for
21256      * @return {Node} The found child or null if none was found
21257      */
21258     findChild : function(attribute, value){
21259         var cs = this.childNodes;
21260         for(var i = 0, len = cs.length; i < len; i++) {
21261                 if(cs[i].attributes[attribute] == value){
21262                     return cs[i];
21263                 }
21264         }
21265         return null;
21266     },
21267
21268     /**
21269      * Finds the first child by a custom function. The child matches if the function passed
21270      * returns true.
21271      * @param {Function} fn
21272      * @param {Object} scope (optional)
21273      * @return {Node} The found child or null if none was found
21274      */
21275     findChildBy : function(fn, scope){
21276         var cs = this.childNodes;
21277         for(var i = 0, len = cs.length; i < len; i++) {
21278                 if(fn.call(scope||cs[i], cs[i]) === true){
21279                     return cs[i];
21280                 }
21281         }
21282         return null;
21283     },
21284
21285     /**
21286      * Sorts this nodes children using the supplied sort function
21287      * @param {Function} fn
21288      * @param {Object} scope (optional)
21289      */
21290     sort : function(fn, scope){
21291         var cs = this.childNodes;
21292         var len = cs.length;
21293         if(len > 0){
21294             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21295             cs.sort(sortFn);
21296             for(var i = 0; i < len; i++){
21297                 var n = cs[i];
21298                 n.previousSibling = cs[i-1];
21299                 n.nextSibling = cs[i+1];
21300                 if(i == 0){
21301                     this.setFirstChild(n);
21302                 }
21303                 if(i == len-1){
21304                     this.setLastChild(n);
21305                 }
21306             }
21307         }
21308     },
21309
21310     /**
21311      * Returns true if this node is an ancestor (at any point) of the passed node.
21312      * @param {Node} node
21313      * @return {Boolean}
21314      */
21315     contains : function(node){
21316         return node.isAncestor(this);
21317     },
21318
21319     /**
21320      * Returns true if the passed node is an ancestor (at any point) of this node.
21321      * @param {Node} node
21322      * @return {Boolean}
21323      */
21324     isAncestor : function(node){
21325         var p = this.parentNode;
21326         while(p){
21327             if(p == node){
21328                 return true;
21329             }
21330             p = p.parentNode;
21331         }
21332         return false;
21333     },
21334
21335     toString : function(){
21336         return "[Node"+(this.id?" "+this.id:"")+"]";
21337     }
21338 });/*
21339  * Based on:
21340  * Ext JS Library 1.1.1
21341  * Copyright(c) 2006-2007, Ext JS, LLC.
21342  *
21343  * Originally Released Under LGPL - original licence link has changed is not relivant.
21344  *
21345  * Fork - LGPL
21346  * <script type="text/javascript">
21347  */
21348  
21349
21350 /**
21351  * @class Roo.ComponentMgr
21352  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21353  * @singleton
21354  */
21355 Roo.ComponentMgr = function(){
21356     var all = new Roo.util.MixedCollection();
21357
21358     return {
21359         /**
21360          * Registers a component.
21361          * @param {Roo.Component} c The component
21362          */
21363         register : function(c){
21364             all.add(c);
21365         },
21366
21367         /**
21368          * Unregisters a component.
21369          * @param {Roo.Component} c The component
21370          */
21371         unregister : function(c){
21372             all.remove(c);
21373         },
21374
21375         /**
21376          * Returns a component by id
21377          * @param {String} id The component id
21378          */
21379         get : function(id){
21380             return all.get(id);
21381         },
21382
21383         /**
21384          * Registers a function that will be called when a specified component is added to ComponentMgr
21385          * @param {String} id The component id
21386          * @param {Funtction} fn The callback function
21387          * @param {Object} scope The scope of the callback
21388          */
21389         onAvailable : function(id, fn, scope){
21390             all.on("add", function(index, o){
21391                 if(o.id == id){
21392                     fn.call(scope || o, o);
21393                     all.un("add", fn, scope);
21394                 }
21395             });
21396         }
21397     };
21398 }();/*
21399  * Based on:
21400  * Ext JS Library 1.1.1
21401  * Copyright(c) 2006-2007, Ext JS, LLC.
21402  *
21403  * Originally Released Under LGPL - original licence link has changed is not relivant.
21404  *
21405  * Fork - LGPL
21406  * <script type="text/javascript">
21407  */
21408  
21409 /**
21410  * @class Roo.Component
21411  * @extends Roo.util.Observable
21412  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21413  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21414  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21415  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21416  * All visual components (widgets) that require rendering into a layout should subclass Component.
21417  * @constructor
21418  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21419  * 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
21420  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21421  */
21422 Roo.Component = function(config){
21423     config = config || {};
21424     if(config.tagName || config.dom || typeof config == "string"){ // element object
21425         config = {el: config, id: config.id || config};
21426     }
21427     this.initialConfig = config;
21428
21429     Roo.apply(this, config);
21430     this.addEvents({
21431         /**
21432          * @event disable
21433          * Fires after the component is disabled.
21434              * @param {Roo.Component} this
21435              */
21436         disable : true,
21437         /**
21438          * @event enable
21439          * Fires after the component is enabled.
21440              * @param {Roo.Component} this
21441              */
21442         enable : true,
21443         /**
21444          * @event beforeshow
21445          * Fires before the component is shown.  Return false to stop the show.
21446              * @param {Roo.Component} this
21447              */
21448         beforeshow : true,
21449         /**
21450          * @event show
21451          * Fires after the component is shown.
21452              * @param {Roo.Component} this
21453              */
21454         show : true,
21455         /**
21456          * @event beforehide
21457          * Fires before the component is hidden. Return false to stop the hide.
21458              * @param {Roo.Component} this
21459              */
21460         beforehide : true,
21461         /**
21462          * @event hide
21463          * Fires after the component is hidden.
21464              * @param {Roo.Component} this
21465              */
21466         hide : true,
21467         /**
21468          * @event beforerender
21469          * Fires before the component is rendered. Return false to stop the render.
21470              * @param {Roo.Component} this
21471              */
21472         beforerender : true,
21473         /**
21474          * @event render
21475          * Fires after the component is rendered.
21476              * @param {Roo.Component} this
21477              */
21478         render : true,
21479         /**
21480          * @event beforedestroy
21481          * Fires before the component is destroyed. Return false to stop the destroy.
21482              * @param {Roo.Component} this
21483              */
21484         beforedestroy : true,
21485         /**
21486          * @event destroy
21487          * Fires after the component is destroyed.
21488              * @param {Roo.Component} this
21489              */
21490         destroy : true
21491     });
21492     if(!this.id){
21493         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21494     }
21495     Roo.ComponentMgr.register(this);
21496     Roo.Component.superclass.constructor.call(this);
21497     this.initComponent();
21498     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21499         this.render(this.renderTo);
21500         delete this.renderTo;
21501     }
21502 };
21503
21504 // private
21505 Roo.Component.AUTO_ID = 1000;
21506
21507 Roo.extend(Roo.Component, Roo.util.Observable, {
21508     /**
21509      * @property {Boolean} hidden
21510      * true if this component is hidden. Read-only.
21511      */
21512     hidden : false,
21513     /**
21514      * true if this component is disabled. Read-only.
21515      */
21516     disabled : false,
21517     /**
21518      * true if this component has been rendered. Read-only.
21519      */
21520     rendered : false,
21521     
21522     /** @cfg {String} disableClass
21523      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21524      */
21525     disabledClass : "x-item-disabled",
21526         /** @cfg {Boolean} allowDomMove
21527          * Whether the component can move the Dom node when rendering (defaults to true).
21528          */
21529     allowDomMove : true,
21530     /** @cfg {String} hideMode
21531      * How this component should hidden. Supported values are
21532      * "visibility" (css visibility), "offsets" (negative offset position) and
21533      * "display" (css display) - defaults to "display".
21534      */
21535     hideMode: 'display',
21536
21537     // private
21538     ctype : "Roo.Component",
21539
21540     /** @cfg {String} actionMode 
21541      * which property holds the element that used for  hide() / show() / disable() / enable()
21542      * default is 'el' 
21543      */
21544     actionMode : "el",
21545
21546     // private
21547     getActionEl : function(){
21548         return this[this.actionMode];
21549     },
21550
21551     initComponent : Roo.emptyFn,
21552     /**
21553      * If this is a lazy rendering component, render it to its container element.
21554      * @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.
21555      */
21556     render : function(container, position){
21557         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21558             if(!container && this.el){
21559                 this.el = Roo.get(this.el);
21560                 container = this.el.dom.parentNode;
21561                 this.allowDomMove = false;
21562             }
21563             this.container = Roo.get(container);
21564             this.rendered = true;
21565             if(position !== undefined){
21566                 if(typeof position == 'number'){
21567                     position = this.container.dom.childNodes[position];
21568                 }else{
21569                     position = Roo.getDom(position);
21570                 }
21571             }
21572             this.onRender(this.container, position || null);
21573             if(this.cls){
21574                 this.el.addClass(this.cls);
21575                 delete this.cls;
21576             }
21577             if(this.style){
21578                 this.el.applyStyles(this.style);
21579                 delete this.style;
21580             }
21581             this.fireEvent("render", this);
21582             this.afterRender(this.container);
21583             if(this.hidden){
21584                 this.hide();
21585             }
21586             if(this.disabled){
21587                 this.disable();
21588             }
21589         }
21590         return this;
21591     },
21592
21593     // private
21594     // default function is not really useful
21595     onRender : function(ct, position){
21596         if(this.el){
21597             this.el = Roo.get(this.el);
21598             if(this.allowDomMove !== false){
21599                 ct.dom.insertBefore(this.el.dom, position);
21600             }
21601         }
21602     },
21603
21604     // private
21605     getAutoCreate : function(){
21606         var cfg = typeof this.autoCreate == "object" ?
21607                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21608         if(this.id && !cfg.id){
21609             cfg.id = this.id;
21610         }
21611         return cfg;
21612     },
21613
21614     // private
21615     afterRender : Roo.emptyFn,
21616
21617     /**
21618      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21619      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21620      */
21621     destroy : function(){
21622         if(this.fireEvent("beforedestroy", this) !== false){
21623             this.purgeListeners();
21624             this.beforeDestroy();
21625             if(this.rendered){
21626                 this.el.removeAllListeners();
21627                 this.el.remove();
21628                 if(this.actionMode == "container"){
21629                     this.container.remove();
21630                 }
21631             }
21632             this.onDestroy();
21633             Roo.ComponentMgr.unregister(this);
21634             this.fireEvent("destroy", this);
21635         }
21636     },
21637
21638         // private
21639     beforeDestroy : function(){
21640
21641     },
21642
21643         // private
21644         onDestroy : function(){
21645
21646     },
21647
21648     /**
21649      * Returns the underlying {@link Roo.Element}.
21650      * @return {Roo.Element} The element
21651      */
21652     getEl : function(){
21653         return this.el;
21654     },
21655
21656     /**
21657      * Returns the id of this component.
21658      * @return {String}
21659      */
21660     getId : function(){
21661         return this.id;
21662     },
21663
21664     /**
21665      * Try to focus this component.
21666      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21667      * @return {Roo.Component} this
21668      */
21669     focus : function(selectText){
21670         if(this.rendered){
21671             this.el.focus();
21672             if(selectText === true){
21673                 this.el.dom.select();
21674             }
21675         }
21676         return this;
21677     },
21678
21679     // private
21680     blur : function(){
21681         if(this.rendered){
21682             this.el.blur();
21683         }
21684         return this;
21685     },
21686
21687     /**
21688      * Disable this component.
21689      * @return {Roo.Component} this
21690      */
21691     disable : function(){
21692         if(this.rendered){
21693             this.onDisable();
21694         }
21695         this.disabled = true;
21696         this.fireEvent("disable", this);
21697         return this;
21698     },
21699
21700         // private
21701     onDisable : function(){
21702         this.getActionEl().addClass(this.disabledClass);
21703         this.el.dom.disabled = true;
21704     },
21705
21706     /**
21707      * Enable this component.
21708      * @return {Roo.Component} this
21709      */
21710     enable : function(){
21711         if(this.rendered){
21712             this.onEnable();
21713         }
21714         this.disabled = false;
21715         this.fireEvent("enable", this);
21716         return this;
21717     },
21718
21719         // private
21720     onEnable : function(){
21721         this.getActionEl().removeClass(this.disabledClass);
21722         this.el.dom.disabled = false;
21723     },
21724
21725     /**
21726      * Convenience function for setting disabled/enabled by boolean.
21727      * @param {Boolean} disabled
21728      */
21729     setDisabled : function(disabled){
21730         this[disabled ? "disable" : "enable"]();
21731     },
21732
21733     /**
21734      * Show this component.
21735      * @return {Roo.Component} this
21736      */
21737     show: function(){
21738         if(this.fireEvent("beforeshow", this) !== false){
21739             this.hidden = false;
21740             if(this.rendered){
21741                 this.onShow();
21742             }
21743             this.fireEvent("show", this);
21744         }
21745         return this;
21746     },
21747
21748     // private
21749     onShow : function(){
21750         var ae = this.getActionEl();
21751         if(this.hideMode == 'visibility'){
21752             ae.dom.style.visibility = "visible";
21753         }else if(this.hideMode == 'offsets'){
21754             ae.removeClass('x-hidden');
21755         }else{
21756             ae.dom.style.display = "";
21757         }
21758     },
21759
21760     /**
21761      * Hide this component.
21762      * @return {Roo.Component} this
21763      */
21764     hide: function(){
21765         if(this.fireEvent("beforehide", this) !== false){
21766             this.hidden = true;
21767             if(this.rendered){
21768                 this.onHide();
21769             }
21770             this.fireEvent("hide", this);
21771         }
21772         return this;
21773     },
21774
21775     // private
21776     onHide : function(){
21777         var ae = this.getActionEl();
21778         if(this.hideMode == 'visibility'){
21779             ae.dom.style.visibility = "hidden";
21780         }else if(this.hideMode == 'offsets'){
21781             ae.addClass('x-hidden');
21782         }else{
21783             ae.dom.style.display = "none";
21784         }
21785     },
21786
21787     /**
21788      * Convenience function to hide or show this component by boolean.
21789      * @param {Boolean} visible True to show, false to hide
21790      * @return {Roo.Component} this
21791      */
21792     setVisible: function(visible){
21793         if(visible) {
21794             this.show();
21795         }else{
21796             this.hide();
21797         }
21798         return this;
21799     },
21800
21801     /**
21802      * Returns true if this component is visible.
21803      */
21804     isVisible : function(){
21805         return this.getActionEl().isVisible();
21806     },
21807
21808     cloneConfig : function(overrides){
21809         overrides = overrides || {};
21810         var id = overrides.id || Roo.id();
21811         var cfg = Roo.applyIf(overrides, this.initialConfig);
21812         cfg.id = id; // prevent dup id
21813         return new this.constructor(cfg);
21814     }
21815 });/*
21816  * Based on:
21817  * Ext JS Library 1.1.1
21818  * Copyright(c) 2006-2007, Ext JS, LLC.
21819  *
21820  * Originally Released Under LGPL - original licence link has changed is not relivant.
21821  *
21822  * Fork - LGPL
21823  * <script type="text/javascript">
21824  */
21825  (function(){ 
21826 /**
21827  * @class Roo.Layer
21828  * @extends Roo.Element
21829  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21830  * automatic maintaining of shadow/shim positions.
21831  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21832  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21833  * you can pass a string with a CSS class name. False turns off the shadow.
21834  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21835  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21836  * @cfg {String} cls CSS class to add to the element
21837  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21838  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21839  * @constructor
21840  * @param {Object} config An object with config options.
21841  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21842  */
21843
21844 Roo.Layer = function(config, existingEl){
21845     config = config || {};
21846     var dh = Roo.DomHelper;
21847     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21848     if(existingEl){
21849         this.dom = Roo.getDom(existingEl);
21850     }
21851     if(!this.dom){
21852         var o = config.dh || {tag: "div", cls: "x-layer"};
21853         this.dom = dh.append(pel, o);
21854     }
21855     if(config.cls){
21856         this.addClass(config.cls);
21857     }
21858     this.constrain = config.constrain !== false;
21859     this.visibilityMode = Roo.Element.VISIBILITY;
21860     if(config.id){
21861         this.id = this.dom.id = config.id;
21862     }else{
21863         this.id = Roo.id(this.dom);
21864     }
21865     this.zindex = config.zindex || this.getZIndex();
21866     this.position("absolute", this.zindex);
21867     if(config.shadow){
21868         this.shadowOffset = config.shadowOffset || 4;
21869         this.shadow = new Roo.Shadow({
21870             offset : this.shadowOffset,
21871             mode : config.shadow
21872         });
21873     }else{
21874         this.shadowOffset = 0;
21875     }
21876     this.useShim = config.shim !== false && Roo.useShims;
21877     this.useDisplay = config.useDisplay;
21878     this.hide();
21879 };
21880
21881 var supr = Roo.Element.prototype;
21882
21883 // shims are shared among layer to keep from having 100 iframes
21884 var shims = [];
21885
21886 Roo.extend(Roo.Layer, Roo.Element, {
21887
21888     getZIndex : function(){
21889         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21890     },
21891
21892     getShim : function(){
21893         if(!this.useShim){
21894             return null;
21895         }
21896         if(this.shim){
21897             return this.shim;
21898         }
21899         var shim = shims.shift();
21900         if(!shim){
21901             shim = this.createShim();
21902             shim.enableDisplayMode('block');
21903             shim.dom.style.display = 'none';
21904             shim.dom.style.visibility = 'visible';
21905         }
21906         var pn = this.dom.parentNode;
21907         if(shim.dom.parentNode != pn){
21908             pn.insertBefore(shim.dom, this.dom);
21909         }
21910         shim.setStyle('z-index', this.getZIndex()-2);
21911         this.shim = shim;
21912         return shim;
21913     },
21914
21915     hideShim : function(){
21916         if(this.shim){
21917             this.shim.setDisplayed(false);
21918             shims.push(this.shim);
21919             delete this.shim;
21920         }
21921     },
21922
21923     disableShadow : function(){
21924         if(this.shadow){
21925             this.shadowDisabled = true;
21926             this.shadow.hide();
21927             this.lastShadowOffset = this.shadowOffset;
21928             this.shadowOffset = 0;
21929         }
21930     },
21931
21932     enableShadow : function(show){
21933         if(this.shadow){
21934             this.shadowDisabled = false;
21935             this.shadowOffset = this.lastShadowOffset;
21936             delete this.lastShadowOffset;
21937             if(show){
21938                 this.sync(true);
21939             }
21940         }
21941     },
21942
21943     // private
21944     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
21945     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
21946     sync : function(doShow){
21947         var sw = this.shadow;
21948         if(!this.updating && this.isVisible() && (sw || this.useShim)){
21949             var sh = this.getShim();
21950
21951             var w = this.getWidth(),
21952                 h = this.getHeight();
21953
21954             var l = this.getLeft(true),
21955                 t = this.getTop(true);
21956
21957             if(sw && !this.shadowDisabled){
21958                 if(doShow && !sw.isVisible()){
21959                     sw.show(this);
21960                 }else{
21961                     sw.realign(l, t, w, h);
21962                 }
21963                 if(sh){
21964                     if(doShow){
21965                        sh.show();
21966                     }
21967                     // fit the shim behind the shadow, so it is shimmed too
21968                     var a = sw.adjusts, s = sh.dom.style;
21969                     s.left = (Math.min(l, l+a.l))+"px";
21970                     s.top = (Math.min(t, t+a.t))+"px";
21971                     s.width = (w+a.w)+"px";
21972                     s.height = (h+a.h)+"px";
21973                 }
21974             }else if(sh){
21975                 if(doShow){
21976                    sh.show();
21977                 }
21978                 sh.setSize(w, h);
21979                 sh.setLeftTop(l, t);
21980             }
21981             
21982         }
21983     },
21984
21985     // private
21986     destroy : function(){
21987         this.hideShim();
21988         if(this.shadow){
21989             this.shadow.hide();
21990         }
21991         this.removeAllListeners();
21992         var pn = this.dom.parentNode;
21993         if(pn){
21994             pn.removeChild(this.dom);
21995         }
21996         Roo.Element.uncache(this.id);
21997     },
21998
21999     remove : function(){
22000         this.destroy();
22001     },
22002
22003     // private
22004     beginUpdate : function(){
22005         this.updating = true;
22006     },
22007
22008     // private
22009     endUpdate : function(){
22010         this.updating = false;
22011         this.sync(true);
22012     },
22013
22014     // private
22015     hideUnders : function(negOffset){
22016         if(this.shadow){
22017             this.shadow.hide();
22018         }
22019         this.hideShim();
22020     },
22021
22022     // private
22023     constrainXY : function(){
22024         if(this.constrain){
22025             var vw = Roo.lib.Dom.getViewWidth(),
22026                 vh = Roo.lib.Dom.getViewHeight();
22027             var s = Roo.get(document).getScroll();
22028
22029             var xy = this.getXY();
22030             var x = xy[0], y = xy[1];   
22031             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22032             // only move it if it needs it
22033             var moved = false;
22034             // first validate right/bottom
22035             if((x + w) > vw+s.left){
22036                 x = vw - w - this.shadowOffset;
22037                 moved = true;
22038             }
22039             if((y + h) > vh+s.top){
22040                 y = vh - h - this.shadowOffset;
22041                 moved = true;
22042             }
22043             // then make sure top/left isn't negative
22044             if(x < s.left){
22045                 x = s.left;
22046                 moved = true;
22047             }
22048             if(y < s.top){
22049                 y = s.top;
22050                 moved = true;
22051             }
22052             if(moved){
22053                 if(this.avoidY){
22054                     var ay = this.avoidY;
22055                     if(y <= ay && (y+h) >= ay){
22056                         y = ay-h-5;   
22057                     }
22058                 }
22059                 xy = [x, y];
22060                 this.storeXY(xy);
22061                 supr.setXY.call(this, xy);
22062                 this.sync();
22063             }
22064         }
22065     },
22066
22067     isVisible : function(){
22068         return this.visible;    
22069     },
22070
22071     // private
22072     showAction : function(){
22073         this.visible = true; // track visibility to prevent getStyle calls
22074         if(this.useDisplay === true){
22075             this.setDisplayed("");
22076         }else if(this.lastXY){
22077             supr.setXY.call(this, this.lastXY);
22078         }else if(this.lastLT){
22079             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22080         }
22081     },
22082
22083     // private
22084     hideAction : function(){
22085         this.visible = false;
22086         if(this.useDisplay === true){
22087             this.setDisplayed(false);
22088         }else{
22089             this.setLeftTop(-10000,-10000);
22090         }
22091     },
22092
22093     // overridden Element method
22094     setVisible : function(v, a, d, c, e){
22095         if(v){
22096             this.showAction();
22097         }
22098         if(a && v){
22099             var cb = function(){
22100                 this.sync(true);
22101                 if(c){
22102                     c();
22103                 }
22104             }.createDelegate(this);
22105             supr.setVisible.call(this, true, true, d, cb, e);
22106         }else{
22107             if(!v){
22108                 this.hideUnders(true);
22109             }
22110             var cb = c;
22111             if(a){
22112                 cb = function(){
22113                     this.hideAction();
22114                     if(c){
22115                         c();
22116                     }
22117                 }.createDelegate(this);
22118             }
22119             supr.setVisible.call(this, v, a, d, cb, e);
22120             if(v){
22121                 this.sync(true);
22122             }else if(!a){
22123                 this.hideAction();
22124             }
22125         }
22126     },
22127
22128     storeXY : function(xy){
22129         delete this.lastLT;
22130         this.lastXY = xy;
22131     },
22132
22133     storeLeftTop : function(left, top){
22134         delete this.lastXY;
22135         this.lastLT = [left, top];
22136     },
22137
22138     // private
22139     beforeFx : function(){
22140         this.beforeAction();
22141         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22142     },
22143
22144     // private
22145     afterFx : function(){
22146         Roo.Layer.superclass.afterFx.apply(this, arguments);
22147         this.sync(this.isVisible());
22148     },
22149
22150     // private
22151     beforeAction : function(){
22152         if(!this.updating && this.shadow){
22153             this.shadow.hide();
22154         }
22155     },
22156
22157     // overridden Element method
22158     setLeft : function(left){
22159         this.storeLeftTop(left, this.getTop(true));
22160         supr.setLeft.apply(this, arguments);
22161         this.sync();
22162     },
22163
22164     setTop : function(top){
22165         this.storeLeftTop(this.getLeft(true), top);
22166         supr.setTop.apply(this, arguments);
22167         this.sync();
22168     },
22169
22170     setLeftTop : function(left, top){
22171         this.storeLeftTop(left, top);
22172         supr.setLeftTop.apply(this, arguments);
22173         this.sync();
22174     },
22175
22176     setXY : function(xy, a, d, c, e){
22177         this.fixDisplay();
22178         this.beforeAction();
22179         this.storeXY(xy);
22180         var cb = this.createCB(c);
22181         supr.setXY.call(this, xy, a, d, cb, e);
22182         if(!a){
22183             cb();
22184         }
22185     },
22186
22187     // private
22188     createCB : function(c){
22189         var el = this;
22190         return function(){
22191             el.constrainXY();
22192             el.sync(true);
22193             if(c){
22194                 c();
22195             }
22196         };
22197     },
22198
22199     // overridden Element method
22200     setX : function(x, a, d, c, e){
22201         this.setXY([x, this.getY()], a, d, c, e);
22202     },
22203
22204     // overridden Element method
22205     setY : function(y, a, d, c, e){
22206         this.setXY([this.getX(), y], a, d, c, e);
22207     },
22208
22209     // overridden Element method
22210     setSize : function(w, h, a, d, c, e){
22211         this.beforeAction();
22212         var cb = this.createCB(c);
22213         supr.setSize.call(this, w, h, a, d, cb, e);
22214         if(!a){
22215             cb();
22216         }
22217     },
22218
22219     // overridden Element method
22220     setWidth : function(w, a, d, c, e){
22221         this.beforeAction();
22222         var cb = this.createCB(c);
22223         supr.setWidth.call(this, w, a, d, cb, e);
22224         if(!a){
22225             cb();
22226         }
22227     },
22228
22229     // overridden Element method
22230     setHeight : function(h, a, d, c, e){
22231         this.beforeAction();
22232         var cb = this.createCB(c);
22233         supr.setHeight.call(this, h, a, d, cb, e);
22234         if(!a){
22235             cb();
22236         }
22237     },
22238
22239     // overridden Element method
22240     setBounds : function(x, y, w, h, a, d, c, e){
22241         this.beforeAction();
22242         var cb = this.createCB(c);
22243         if(!a){
22244             this.storeXY([x, y]);
22245             supr.setXY.call(this, [x, y]);
22246             supr.setSize.call(this, w, h, a, d, cb, e);
22247             cb();
22248         }else{
22249             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22250         }
22251         return this;
22252     },
22253     
22254     /**
22255      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22256      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22257      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22258      * @param {Number} zindex The new z-index to set
22259      * @return {this} The Layer
22260      */
22261     setZIndex : function(zindex){
22262         this.zindex = zindex;
22263         this.setStyle("z-index", zindex + 2);
22264         if(this.shadow){
22265             this.shadow.setZIndex(zindex + 1);
22266         }
22267         if(this.shim){
22268             this.shim.setStyle("z-index", zindex);
22269         }
22270     }
22271 });
22272 })();/*
22273  * Based on:
22274  * Ext JS Library 1.1.1
22275  * Copyright(c) 2006-2007, Ext JS, LLC.
22276  *
22277  * Originally Released Under LGPL - original licence link has changed is not relivant.
22278  *
22279  * Fork - LGPL
22280  * <script type="text/javascript">
22281  */
22282
22283
22284 /**
22285  * @class Roo.Shadow
22286  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22287  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22288  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22289  * @constructor
22290  * Create a new Shadow
22291  * @param {Object} config The config object
22292  */
22293 Roo.Shadow = function(config){
22294     Roo.apply(this, config);
22295     if(typeof this.mode != "string"){
22296         this.mode = this.defaultMode;
22297     }
22298     var o = this.offset, a = {h: 0};
22299     var rad = Math.floor(this.offset/2);
22300     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22301         case "drop":
22302             a.w = 0;
22303             a.l = a.t = o;
22304             a.t -= 1;
22305             if(Roo.isIE){
22306                 a.l -= this.offset + rad;
22307                 a.t -= this.offset + rad;
22308                 a.w -= rad;
22309                 a.h -= rad;
22310                 a.t += 1;
22311             }
22312         break;
22313         case "sides":
22314             a.w = (o*2);
22315             a.l = -o;
22316             a.t = o-1;
22317             if(Roo.isIE){
22318                 a.l -= (this.offset - rad);
22319                 a.t -= this.offset + rad;
22320                 a.l += 1;
22321                 a.w -= (this.offset - rad)*2;
22322                 a.w -= rad + 1;
22323                 a.h -= 1;
22324             }
22325         break;
22326         case "frame":
22327             a.w = a.h = (o*2);
22328             a.l = a.t = -o;
22329             a.t += 1;
22330             a.h -= 2;
22331             if(Roo.isIE){
22332                 a.l -= (this.offset - rad);
22333                 a.t -= (this.offset - rad);
22334                 a.l += 1;
22335                 a.w -= (this.offset + rad + 1);
22336                 a.h -= (this.offset + rad);
22337                 a.h += 1;
22338             }
22339         break;
22340     };
22341
22342     this.adjusts = a;
22343 };
22344
22345 Roo.Shadow.prototype = {
22346     /**
22347      * @cfg {String} mode
22348      * The shadow display mode.  Supports the following options:<br />
22349      * sides: Shadow displays on both sides and bottom only<br />
22350      * frame: Shadow displays equally on all four sides<br />
22351      * drop: Traditional bottom-right drop shadow (default)
22352      */
22353     /**
22354      * @cfg {String} offset
22355      * The number of pixels to offset the shadow from the element (defaults to 4)
22356      */
22357     offset: 4,
22358
22359     // private
22360     defaultMode: "drop",
22361
22362     /**
22363      * Displays the shadow under the target element
22364      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22365      */
22366     show : function(target){
22367         target = Roo.get(target);
22368         if(!this.el){
22369             this.el = Roo.Shadow.Pool.pull();
22370             if(this.el.dom.nextSibling != target.dom){
22371                 this.el.insertBefore(target);
22372             }
22373         }
22374         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22375         if(Roo.isIE){
22376             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22377         }
22378         this.realign(
22379             target.getLeft(true),
22380             target.getTop(true),
22381             target.getWidth(),
22382             target.getHeight()
22383         );
22384         this.el.dom.style.display = "block";
22385     },
22386
22387     /**
22388      * Returns true if the shadow is visible, else false
22389      */
22390     isVisible : function(){
22391         return this.el ? true : false;  
22392     },
22393
22394     /**
22395      * Direct alignment when values are already available. Show must be called at least once before
22396      * calling this method to ensure it is initialized.
22397      * @param {Number} left The target element left position
22398      * @param {Number} top The target element top position
22399      * @param {Number} width The target element width
22400      * @param {Number} height The target element height
22401      */
22402     realign : function(l, t, w, h){
22403         if(!this.el){
22404             return;
22405         }
22406         var a = this.adjusts, d = this.el.dom, s = d.style;
22407         var iea = 0;
22408         s.left = (l+a.l)+"px";
22409         s.top = (t+a.t)+"px";
22410         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22411         if(s.width != sws || s.height != shs){
22412             s.width = sws;
22413             s.height = shs;
22414             if(!Roo.isIE){
22415                 var cn = d.childNodes;
22416                 var sww = Math.max(0, (sw-12))+"px";
22417                 cn[0].childNodes[1].style.width = sww;
22418                 cn[1].childNodes[1].style.width = sww;
22419                 cn[2].childNodes[1].style.width = sww;
22420                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22421             }
22422         }
22423     },
22424
22425     /**
22426      * Hides this shadow
22427      */
22428     hide : function(){
22429         if(this.el){
22430             this.el.dom.style.display = "none";
22431             Roo.Shadow.Pool.push(this.el);
22432             delete this.el;
22433         }
22434     },
22435
22436     /**
22437      * Adjust the z-index of this shadow
22438      * @param {Number} zindex The new z-index
22439      */
22440     setZIndex : function(z){
22441         this.zIndex = z;
22442         if(this.el){
22443             this.el.setStyle("z-index", z);
22444         }
22445     }
22446 };
22447
22448 // Private utility class that manages the internal Shadow cache
22449 Roo.Shadow.Pool = function(){
22450     var p = [];
22451     var markup = Roo.isIE ?
22452                  '<div class="x-ie-shadow"></div>' :
22453                  '<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>';
22454     return {
22455         pull : function(){
22456             var sh = p.shift();
22457             if(!sh){
22458                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22459                 sh.autoBoxAdjust = false;
22460             }
22461             return sh;
22462         },
22463
22464         push : function(sh){
22465             p.push(sh);
22466         }
22467     };
22468 }();/*
22469  * Based on:
22470  * Ext JS Library 1.1.1
22471  * Copyright(c) 2006-2007, Ext JS, LLC.
22472  *
22473  * Originally Released Under LGPL - original licence link has changed is not relivant.
22474  *
22475  * Fork - LGPL
22476  * <script type="text/javascript">
22477  */
22478
22479 /**
22480  * @class Roo.BoxComponent
22481  * @extends Roo.Component
22482  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22483  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22484  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22485  * layout containers.
22486  * @constructor
22487  * @param {Roo.Element/String/Object} config The configuration options.
22488  */
22489 Roo.BoxComponent = function(config){
22490     Roo.Component.call(this, config);
22491     this.addEvents({
22492         /**
22493          * @event resize
22494          * Fires after the component is resized.
22495              * @param {Roo.Component} this
22496              * @param {Number} adjWidth The box-adjusted width that was set
22497              * @param {Number} adjHeight The box-adjusted height that was set
22498              * @param {Number} rawWidth The width that was originally specified
22499              * @param {Number} rawHeight The height that was originally specified
22500              */
22501         resize : true,
22502         /**
22503          * @event move
22504          * Fires after the component is moved.
22505              * @param {Roo.Component} this
22506              * @param {Number} x The new x position
22507              * @param {Number} y The new y position
22508              */
22509         move : true
22510     });
22511 };
22512
22513 Roo.extend(Roo.BoxComponent, Roo.Component, {
22514     // private, set in afterRender to signify that the component has been rendered
22515     boxReady : false,
22516     // private, used to defer height settings to subclasses
22517     deferHeight: false,
22518     /** @cfg {Number} width
22519      * width (optional) size of component
22520      */
22521      /** @cfg {Number} height
22522      * height (optional) size of component
22523      */
22524      
22525     /**
22526      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22527      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22528      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22529      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22530      * @return {Roo.BoxComponent} this
22531      */
22532     setSize : function(w, h){
22533         // support for standard size objects
22534         if(typeof w == 'object'){
22535             h = w.height;
22536             w = w.width;
22537         }
22538         // not rendered
22539         if(!this.boxReady){
22540             this.width = w;
22541             this.height = h;
22542             return this;
22543         }
22544
22545         // prevent recalcs when not needed
22546         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22547             return this;
22548         }
22549         this.lastSize = {width: w, height: h};
22550
22551         var adj = this.adjustSize(w, h);
22552         var aw = adj.width, ah = adj.height;
22553         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22554             var rz = this.getResizeEl();
22555             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22556                 rz.setSize(aw, ah);
22557             }else if(!this.deferHeight && ah !== undefined){
22558                 rz.setHeight(ah);
22559             }else if(aw !== undefined){
22560                 rz.setWidth(aw);
22561             }
22562             this.onResize(aw, ah, w, h);
22563             this.fireEvent('resize', this, aw, ah, w, h);
22564         }
22565         return this;
22566     },
22567
22568     /**
22569      * Gets the current size of the component's underlying element.
22570      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22571      */
22572     getSize : function(){
22573         return this.el.getSize();
22574     },
22575
22576     /**
22577      * Gets the current XY position of the component's underlying element.
22578      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22579      * @return {Array} The XY position of the element (e.g., [100, 200])
22580      */
22581     getPosition : function(local){
22582         if(local === true){
22583             return [this.el.getLeft(true), this.el.getTop(true)];
22584         }
22585         return this.xy || this.el.getXY();
22586     },
22587
22588     /**
22589      * Gets the current box measurements of the component's underlying element.
22590      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22591      * @returns {Object} box An object in the format {x, y, width, height}
22592      */
22593     getBox : function(local){
22594         var s = this.el.getSize();
22595         if(local){
22596             s.x = this.el.getLeft(true);
22597             s.y = this.el.getTop(true);
22598         }else{
22599             var xy = this.xy || this.el.getXY();
22600             s.x = xy[0];
22601             s.y = xy[1];
22602         }
22603         return s;
22604     },
22605
22606     /**
22607      * Sets the current box measurements of the component's underlying element.
22608      * @param {Object} box An object in the format {x, y, width, height}
22609      * @returns {Roo.BoxComponent} this
22610      */
22611     updateBox : function(box){
22612         this.setSize(box.width, box.height);
22613         this.setPagePosition(box.x, box.y);
22614         return this;
22615     },
22616
22617     // protected
22618     getResizeEl : function(){
22619         return this.resizeEl || this.el;
22620     },
22621
22622     // protected
22623     getPositionEl : function(){
22624         return this.positionEl || this.el;
22625     },
22626
22627     /**
22628      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22629      * This method fires the move event.
22630      * @param {Number} left The new left
22631      * @param {Number} top The new top
22632      * @returns {Roo.BoxComponent} this
22633      */
22634     setPosition : function(x, y){
22635         this.x = x;
22636         this.y = y;
22637         if(!this.boxReady){
22638             return this;
22639         }
22640         var adj = this.adjustPosition(x, y);
22641         var ax = adj.x, ay = adj.y;
22642
22643         var el = this.getPositionEl();
22644         if(ax !== undefined || ay !== undefined){
22645             if(ax !== undefined && ay !== undefined){
22646                 el.setLeftTop(ax, ay);
22647             }else if(ax !== undefined){
22648                 el.setLeft(ax);
22649             }else if(ay !== undefined){
22650                 el.setTop(ay);
22651             }
22652             this.onPosition(ax, ay);
22653             this.fireEvent('move', this, ax, ay);
22654         }
22655         return this;
22656     },
22657
22658     /**
22659      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22660      * This method fires the move event.
22661      * @param {Number} x The new x position
22662      * @param {Number} y The new y position
22663      * @returns {Roo.BoxComponent} this
22664      */
22665     setPagePosition : function(x, y){
22666         this.pageX = x;
22667         this.pageY = y;
22668         if(!this.boxReady){
22669             return;
22670         }
22671         if(x === undefined || y === undefined){ // cannot translate undefined points
22672             return;
22673         }
22674         var p = this.el.translatePoints(x, y);
22675         this.setPosition(p.left, p.top);
22676         return this;
22677     },
22678
22679     // private
22680     onRender : function(ct, position){
22681         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22682         if(this.resizeEl){
22683             this.resizeEl = Roo.get(this.resizeEl);
22684         }
22685         if(this.positionEl){
22686             this.positionEl = Roo.get(this.positionEl);
22687         }
22688     },
22689
22690     // private
22691     afterRender : function(){
22692         Roo.BoxComponent.superclass.afterRender.call(this);
22693         this.boxReady = true;
22694         this.setSize(this.width, this.height);
22695         if(this.x || this.y){
22696             this.setPosition(this.x, this.y);
22697         }
22698         if(this.pageX || this.pageY){
22699             this.setPagePosition(this.pageX, this.pageY);
22700         }
22701     },
22702
22703     /**
22704      * Force the component's size to recalculate based on the underlying element's current height and width.
22705      * @returns {Roo.BoxComponent} this
22706      */
22707     syncSize : function(){
22708         delete this.lastSize;
22709         this.setSize(this.el.getWidth(), this.el.getHeight());
22710         return this;
22711     },
22712
22713     /**
22714      * Called after the component is resized, this method is empty by default but can be implemented by any
22715      * subclass that needs to perform custom logic after a resize occurs.
22716      * @param {Number} adjWidth The box-adjusted width that was set
22717      * @param {Number} adjHeight The box-adjusted height that was set
22718      * @param {Number} rawWidth The width that was originally specified
22719      * @param {Number} rawHeight The height that was originally specified
22720      */
22721     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22722
22723     },
22724
22725     /**
22726      * Called after the component is moved, this method is empty by default but can be implemented by any
22727      * subclass that needs to perform custom logic after a move occurs.
22728      * @param {Number} x The new x position
22729      * @param {Number} y The new y position
22730      */
22731     onPosition : function(x, y){
22732
22733     },
22734
22735     // private
22736     adjustSize : function(w, h){
22737         if(this.autoWidth){
22738             w = 'auto';
22739         }
22740         if(this.autoHeight){
22741             h = 'auto';
22742         }
22743         return {width : w, height: h};
22744     },
22745
22746     // private
22747     adjustPosition : function(x, y){
22748         return {x : x, y: y};
22749     }
22750 });/*
22751  * Based on:
22752  * Ext JS Library 1.1.1
22753  * Copyright(c) 2006-2007, Ext JS, LLC.
22754  *
22755  * Originally Released Under LGPL - original licence link has changed is not relivant.
22756  *
22757  * Fork - LGPL
22758  * <script type="text/javascript">
22759  */
22760
22761
22762 /**
22763  * @class Roo.SplitBar
22764  * @extends Roo.util.Observable
22765  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22766  * <br><br>
22767  * Usage:
22768  * <pre><code>
22769 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22770                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22771 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22772 split.minSize = 100;
22773 split.maxSize = 600;
22774 split.animate = true;
22775 split.on('moved', splitterMoved);
22776 </code></pre>
22777  * @constructor
22778  * Create a new SplitBar
22779  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22780  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22781  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22782  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22783                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22784                         position of the SplitBar).
22785  */
22786 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22787     
22788     /** @private */
22789     this.el = Roo.get(dragElement, true);
22790     this.el.dom.unselectable = "on";
22791     /** @private */
22792     this.resizingEl = Roo.get(resizingElement, true);
22793
22794     /**
22795      * @private
22796      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22797      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22798      * @type Number
22799      */
22800     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22801     
22802     /**
22803      * The minimum size of the resizing element. (Defaults to 0)
22804      * @type Number
22805      */
22806     this.minSize = 0;
22807     
22808     /**
22809      * The maximum size of the resizing element. (Defaults to 2000)
22810      * @type Number
22811      */
22812     this.maxSize = 2000;
22813     
22814     /**
22815      * Whether to animate the transition to the new size
22816      * @type Boolean
22817      */
22818     this.animate = false;
22819     
22820     /**
22821      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22822      * @type Boolean
22823      */
22824     this.useShim = false;
22825     
22826     /** @private */
22827     this.shim = null;
22828     
22829     if(!existingProxy){
22830         /** @private */
22831         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22832     }else{
22833         this.proxy = Roo.get(existingProxy).dom;
22834     }
22835     /** @private */
22836     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22837     
22838     /** @private */
22839     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22840     
22841     /** @private */
22842     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22843     
22844     /** @private */
22845     this.dragSpecs = {};
22846     
22847     /**
22848      * @private The adapter to use to positon and resize elements
22849      */
22850     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22851     this.adapter.init(this);
22852     
22853     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22854         /** @private */
22855         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22856         this.el.addClass("x-splitbar-h");
22857     }else{
22858         /** @private */
22859         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22860         this.el.addClass("x-splitbar-v");
22861     }
22862     
22863     this.addEvents({
22864         /**
22865          * @event resize
22866          * Fires when the splitter is moved (alias for {@link #event-moved})
22867          * @param {Roo.SplitBar} this
22868          * @param {Number} newSize the new width or height
22869          */
22870         "resize" : true,
22871         /**
22872          * @event moved
22873          * Fires when the splitter is moved
22874          * @param {Roo.SplitBar} this
22875          * @param {Number} newSize the new width or height
22876          */
22877         "moved" : true,
22878         /**
22879          * @event beforeresize
22880          * Fires before the splitter is dragged
22881          * @param {Roo.SplitBar} this
22882          */
22883         "beforeresize" : true,
22884
22885         "beforeapply" : true
22886     });
22887
22888     Roo.util.Observable.call(this);
22889 };
22890
22891 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22892     onStartProxyDrag : function(x, y){
22893         this.fireEvent("beforeresize", this);
22894         if(!this.overlay){
22895             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22896             o.unselectable();
22897             o.enableDisplayMode("block");
22898             // all splitbars share the same overlay
22899             Roo.SplitBar.prototype.overlay = o;
22900         }
22901         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22902         this.overlay.show();
22903         Roo.get(this.proxy).setDisplayed("block");
22904         var size = this.adapter.getElementSize(this);
22905         this.activeMinSize = this.getMinimumSize();;
22906         this.activeMaxSize = this.getMaximumSize();;
22907         var c1 = size - this.activeMinSize;
22908         var c2 = Math.max(this.activeMaxSize - size, 0);
22909         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22910             this.dd.resetConstraints();
22911             this.dd.setXConstraint(
22912                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
22913                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
22914             );
22915             this.dd.setYConstraint(0, 0);
22916         }else{
22917             this.dd.resetConstraints();
22918             this.dd.setXConstraint(0, 0);
22919             this.dd.setYConstraint(
22920                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
22921                 this.placement == Roo.SplitBar.TOP ? c2 : c1
22922             );
22923          }
22924         this.dragSpecs.startSize = size;
22925         this.dragSpecs.startPoint = [x, y];
22926         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
22927     },
22928     
22929     /** 
22930      * @private Called after the drag operation by the DDProxy
22931      */
22932     onEndProxyDrag : function(e){
22933         Roo.get(this.proxy).setDisplayed(false);
22934         var endPoint = Roo.lib.Event.getXY(e);
22935         if(this.overlay){
22936             this.overlay.hide();
22937         }
22938         var newSize;
22939         if(this.orientation == Roo.SplitBar.HORIZONTAL){
22940             newSize = this.dragSpecs.startSize + 
22941                 (this.placement == Roo.SplitBar.LEFT ?
22942                     endPoint[0] - this.dragSpecs.startPoint[0] :
22943                     this.dragSpecs.startPoint[0] - endPoint[0]
22944                 );
22945         }else{
22946             newSize = this.dragSpecs.startSize + 
22947                 (this.placement == Roo.SplitBar.TOP ?
22948                     endPoint[1] - this.dragSpecs.startPoint[1] :
22949                     this.dragSpecs.startPoint[1] - endPoint[1]
22950                 );
22951         }
22952         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
22953         if(newSize != this.dragSpecs.startSize){
22954             if(this.fireEvent('beforeapply', this, newSize) !== false){
22955                 this.adapter.setElementSize(this, newSize);
22956                 this.fireEvent("moved", this, newSize);
22957                 this.fireEvent("resize", this, newSize);
22958             }
22959         }
22960     },
22961     
22962     /**
22963      * Get the adapter this SplitBar uses
22964      * @return The adapter object
22965      */
22966     getAdapter : function(){
22967         return this.adapter;
22968     },
22969     
22970     /**
22971      * Set the adapter this SplitBar uses
22972      * @param {Object} adapter A SplitBar adapter object
22973      */
22974     setAdapter : function(adapter){
22975         this.adapter = adapter;
22976         this.adapter.init(this);
22977     },
22978     
22979     /**
22980      * Gets the minimum size for the resizing element
22981      * @return {Number} The minimum size
22982      */
22983     getMinimumSize : function(){
22984         return this.minSize;
22985     },
22986     
22987     /**
22988      * Sets the minimum size for the resizing element
22989      * @param {Number} minSize The minimum size
22990      */
22991     setMinimumSize : function(minSize){
22992         this.minSize = minSize;
22993     },
22994     
22995     /**
22996      * Gets the maximum size for the resizing element
22997      * @return {Number} The maximum size
22998      */
22999     getMaximumSize : function(){
23000         return this.maxSize;
23001     },
23002     
23003     /**
23004      * Sets the maximum size for the resizing element
23005      * @param {Number} maxSize The maximum size
23006      */
23007     setMaximumSize : function(maxSize){
23008         this.maxSize = maxSize;
23009     },
23010     
23011     /**
23012      * Sets the initialize size for the resizing element
23013      * @param {Number} size The initial size
23014      */
23015     setCurrentSize : function(size){
23016         var oldAnimate = this.animate;
23017         this.animate = false;
23018         this.adapter.setElementSize(this, size);
23019         this.animate = oldAnimate;
23020     },
23021     
23022     /**
23023      * Destroy this splitbar. 
23024      * @param {Boolean} removeEl True to remove the element
23025      */
23026     destroy : function(removeEl){
23027         if(this.shim){
23028             this.shim.remove();
23029         }
23030         this.dd.unreg();
23031         this.proxy.parentNode.removeChild(this.proxy);
23032         if(removeEl){
23033             this.el.remove();
23034         }
23035     }
23036 });
23037
23038 /**
23039  * @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.
23040  */
23041 Roo.SplitBar.createProxy = function(dir){
23042     var proxy = new Roo.Element(document.createElement("div"));
23043     proxy.unselectable();
23044     var cls = 'x-splitbar-proxy';
23045     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23046     document.body.appendChild(proxy.dom);
23047     return proxy.dom;
23048 };
23049
23050 /** 
23051  * @class Roo.SplitBar.BasicLayoutAdapter
23052  * Default Adapter. It assumes the splitter and resizing element are not positioned
23053  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23054  */
23055 Roo.SplitBar.BasicLayoutAdapter = function(){
23056 };
23057
23058 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23059     // do nothing for now
23060     init : function(s){
23061     
23062     },
23063     /**
23064      * Called before drag operations to get the current size of the resizing element. 
23065      * @param {Roo.SplitBar} s The SplitBar using this adapter
23066      */
23067      getElementSize : function(s){
23068         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23069             return s.resizingEl.getWidth();
23070         }else{
23071             return s.resizingEl.getHeight();
23072         }
23073     },
23074     
23075     /**
23076      * Called after drag operations to set the size of the resizing element.
23077      * @param {Roo.SplitBar} s The SplitBar using this adapter
23078      * @param {Number} newSize The new size to set
23079      * @param {Function} onComplete A function to be invoked when resizing is complete
23080      */
23081     setElementSize : function(s, newSize, onComplete){
23082         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23083             if(!s.animate){
23084                 s.resizingEl.setWidth(newSize);
23085                 if(onComplete){
23086                     onComplete(s, newSize);
23087                 }
23088             }else{
23089                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23090             }
23091         }else{
23092             
23093             if(!s.animate){
23094                 s.resizingEl.setHeight(newSize);
23095                 if(onComplete){
23096                     onComplete(s, newSize);
23097                 }
23098             }else{
23099                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23100             }
23101         }
23102     }
23103 };
23104
23105 /** 
23106  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23107  * @extends Roo.SplitBar.BasicLayoutAdapter
23108  * Adapter that  moves the splitter element to align with the resized sizing element. 
23109  * Used with an absolute positioned SplitBar.
23110  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23111  * document.body, make sure you assign an id to the body element.
23112  */
23113 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23114     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23115     this.container = Roo.get(container);
23116 };
23117
23118 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23119     init : function(s){
23120         this.basic.init(s);
23121     },
23122     
23123     getElementSize : function(s){
23124         return this.basic.getElementSize(s);
23125     },
23126     
23127     setElementSize : function(s, newSize, onComplete){
23128         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23129     },
23130     
23131     moveSplitter : function(s){
23132         var yes = Roo.SplitBar;
23133         switch(s.placement){
23134             case yes.LEFT:
23135                 s.el.setX(s.resizingEl.getRight());
23136                 break;
23137             case yes.RIGHT:
23138                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23139                 break;
23140             case yes.TOP:
23141                 s.el.setY(s.resizingEl.getBottom());
23142                 break;
23143             case yes.BOTTOM:
23144                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23145                 break;
23146         }
23147     }
23148 };
23149
23150 /**
23151  * Orientation constant - Create a vertical SplitBar
23152  * @static
23153  * @type Number
23154  */
23155 Roo.SplitBar.VERTICAL = 1;
23156
23157 /**
23158  * Orientation constant - Create a horizontal SplitBar
23159  * @static
23160  * @type Number
23161  */
23162 Roo.SplitBar.HORIZONTAL = 2;
23163
23164 /**
23165  * Placement constant - The resizing element is to the left of the splitter element
23166  * @static
23167  * @type Number
23168  */
23169 Roo.SplitBar.LEFT = 1;
23170
23171 /**
23172  * Placement constant - The resizing element is to the right of the splitter element
23173  * @static
23174  * @type Number
23175  */
23176 Roo.SplitBar.RIGHT = 2;
23177
23178 /**
23179  * Placement constant - The resizing element is positioned above the splitter element
23180  * @static
23181  * @type Number
23182  */
23183 Roo.SplitBar.TOP = 3;
23184
23185 /**
23186  * Placement constant - The resizing element is positioned under splitter element
23187  * @static
23188  * @type Number
23189  */
23190 Roo.SplitBar.BOTTOM = 4;
23191 /*
23192  * Based on:
23193  * Ext JS Library 1.1.1
23194  * Copyright(c) 2006-2007, Ext JS, LLC.
23195  *
23196  * Originally Released Under LGPL - original licence link has changed is not relivant.
23197  *
23198  * Fork - LGPL
23199  * <script type="text/javascript">
23200  */
23201
23202 /**
23203  * @class Roo.View
23204  * @extends Roo.util.Observable
23205  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23206  * This class also supports single and multi selection modes. <br>
23207  * Create a data model bound view:
23208  <pre><code>
23209  var store = new Roo.data.Store(...);
23210
23211  var view = new Roo.View("my-element",
23212  '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23213  {
23214  singleSelect: true,
23215  selectedClass: "ydataview-selected",
23216  store: store
23217  });
23218
23219  // listen for node click?
23220  view.on("click", function(vw, index, node, e){
23221  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23222  });
23223
23224  // load XML data
23225  dataModel.load("foobar.xml");
23226  </code></pre>
23227  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23228  * <br><br>
23229  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23230  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23231  * @constructor
23232  * Create a new View
23233  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23234  * @param {String/DomHelper.Template} tpl The rendering template or a string to create a template with
23235  * @param {Object} config The config object
23236  */
23237 Roo.View = function(container, tpl, config){
23238     this.el = Roo.get(container);
23239     if(typeof tpl == "string"){
23240         tpl = new Roo.Template(tpl);
23241     }
23242     tpl.compile();
23243     /**
23244      * The template used by this View
23245      * @type {Roo.DomHelper.Template}
23246      */
23247     this.tpl = tpl;
23248
23249     Roo.apply(this, config);
23250
23251     /** @private */
23252     this.addEvents({
23253     /**
23254      * @event beforeclick
23255      * Fires before a click is processed. Returns false to cancel the default action.
23256      * @param {Roo.View} this
23257      * @param {Number} index The index of the target node
23258      * @param {HTMLElement} node The target node
23259      * @param {Roo.EventObject} e The raw event object
23260      */
23261         "beforeclick" : true,
23262     /**
23263      * @event click
23264      * Fires when a template node is clicked.
23265      * @param {Roo.View} this
23266      * @param {Number} index The index of the target node
23267      * @param {HTMLElement} node The target node
23268      * @param {Roo.EventObject} e The raw event object
23269      */
23270         "click" : true,
23271     /**
23272      * @event dblclick
23273      * Fires when a template node is double clicked.
23274      * @param {Roo.View} this
23275      * @param {Number} index The index of the target node
23276      * @param {HTMLElement} node The target node
23277      * @param {Roo.EventObject} e The raw event object
23278      */
23279         "dblclick" : true,
23280     /**
23281      * @event contextmenu
23282      * Fires when a template node is right clicked.
23283      * @param {Roo.View} this
23284      * @param {Number} index The index of the target node
23285      * @param {HTMLElement} node The target node
23286      * @param {Roo.EventObject} e The raw event object
23287      */
23288         "contextmenu" : true,
23289     /**
23290      * @event selectionchange
23291      * Fires when the selected nodes change.
23292      * @param {Roo.View} this
23293      * @param {Array} selections Array of the selected nodes
23294      */
23295         "selectionchange" : true,
23296
23297     /**
23298      * @event beforeselect
23299      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23300      * @param {Roo.View} this
23301      * @param {HTMLElement} node The node to be selected
23302      * @param {Array} selections Array of currently selected nodes
23303      */
23304         "beforeselect" : true
23305     });
23306
23307     this.el.on({
23308         "click": this.onClick,
23309         "dblclick": this.onDblClick,
23310         "contextmenu": this.onContextMenu,
23311         scope:this
23312     });
23313
23314     this.selections = [];
23315     this.nodes = [];
23316     this.cmp = new Roo.CompositeElementLite([]);
23317     if(this.store){
23318         this.store = Roo.factory(this.store, Roo.data);
23319         this.setStore(this.store, true);
23320     }
23321     Roo.View.superclass.constructor.call(this);
23322 };
23323
23324 Roo.extend(Roo.View, Roo.util.Observable, {
23325     /**
23326      * The css class to add to selected nodes
23327      * @type {Roo.DomHelper.Template}
23328      */
23329     selectedClass : "x-view-selected",
23330     
23331     emptyText : "",
23332     /**
23333      * Returns the element this view is bound to.
23334      * @return {Roo.Element}
23335      */
23336     getEl : function(){
23337         return this.el;
23338     },
23339
23340     /**
23341      * Refreshes the view.
23342      */
23343     refresh : function(){
23344         var t = this.tpl;
23345         this.clearSelections();
23346         this.el.update("");
23347         var html = [];
23348         var records = this.store.getRange();
23349         if(records.length < 1){
23350             this.el.update(this.emptyText);
23351             return;
23352         }
23353         for(var i = 0, len = records.length; i < len; i++){
23354             var data = this.prepareData(records[i].data, i, records[i]);
23355             html[html.length] = t.apply(data);
23356         }
23357         this.el.update(html.join(""));
23358         this.nodes = this.el.dom.childNodes;
23359         this.updateIndexes(0);
23360     },
23361
23362     /**
23363      * Function to override to reformat the data that is sent to
23364      * the template for each node.
23365      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23366      * a JSON object for an UpdateManager bound view).
23367      */
23368     prepareData : function(data){
23369         return data;
23370     },
23371
23372     onUpdate : function(ds, record){
23373         this.clearSelections();
23374         var index = this.store.indexOf(record);
23375         var n = this.nodes[index];
23376         this.tpl.insertBefore(n, this.prepareData(record.data));
23377         n.parentNode.removeChild(n);
23378         this.updateIndexes(index, index);
23379     },
23380
23381     onAdd : function(ds, records, index){
23382         this.clearSelections();
23383         if(this.nodes.length == 0){
23384             this.refresh();
23385             return;
23386         }
23387         var n = this.nodes[index];
23388         for(var i = 0, len = records.length; i < len; i++){
23389             var d = this.prepareData(records[i].data);
23390             if(n){
23391                 this.tpl.insertBefore(n, d);
23392             }else{
23393                 this.tpl.append(this.el, d);
23394             }
23395         }
23396         this.updateIndexes(index);
23397     },
23398
23399     onRemove : function(ds, record, index){
23400         this.clearSelections();
23401         this.el.dom.removeChild(this.nodes[index]);
23402         this.updateIndexes(index);
23403     },
23404
23405     /**
23406      * Refresh an individual node.
23407      * @param {Number} index
23408      */
23409     refreshNode : function(index){
23410         this.onUpdate(this.store, this.store.getAt(index));
23411     },
23412
23413     updateIndexes : function(startIndex, endIndex){
23414         var ns = this.nodes;
23415         startIndex = startIndex || 0;
23416         endIndex = endIndex || ns.length - 1;
23417         for(var i = startIndex; i <= endIndex; i++){
23418             ns[i].nodeIndex = i;
23419         }
23420     },
23421
23422     /**
23423      * Changes the data store this view uses and refresh the view.
23424      * @param {Store} store
23425      */
23426     setStore : function(store, initial){
23427         if(!initial && this.store){
23428             this.store.un("datachanged", this.refresh);
23429             this.store.un("add", this.onAdd);
23430             this.store.un("remove", this.onRemove);
23431             this.store.un("update", this.onUpdate);
23432             this.store.un("clear", this.refresh);
23433         }
23434         if(store){
23435           
23436             store.on("datachanged", this.refresh, this);
23437             store.on("add", this.onAdd, this);
23438             store.on("remove", this.onRemove, this);
23439             store.on("update", this.onUpdate, this);
23440             store.on("clear", this.refresh, this);
23441         }
23442         
23443         if(store){
23444             this.refresh();
23445         }
23446     },
23447
23448     /**
23449      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23450      * @param {HTMLElement} node
23451      * @return {HTMLElement} The template node
23452      */
23453     findItemFromChild : function(node){
23454         var el = this.el.dom;
23455         if(!node || node.parentNode == el){
23456                     return node;
23457             }
23458             var p = node.parentNode;
23459             while(p && p != el){
23460             if(p.parentNode == el){
23461                 return p;
23462             }
23463             p = p.parentNode;
23464         }
23465             return null;
23466     },
23467
23468     /** @ignore */
23469     onClick : function(e){
23470         var item = this.findItemFromChild(e.getTarget());
23471         if(item){
23472             var index = this.indexOf(item);
23473             if(this.onItemClick(item, index, e) !== false){
23474                 this.fireEvent("click", this, index, item, e);
23475             }
23476         }else{
23477             this.clearSelections();
23478         }
23479     },
23480
23481     /** @ignore */
23482     onContextMenu : function(e){
23483         var item = this.findItemFromChild(e.getTarget());
23484         if(item){
23485             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23486         }
23487     },
23488
23489     /** @ignore */
23490     onDblClick : function(e){
23491         var item = this.findItemFromChild(e.getTarget());
23492         if(item){
23493             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23494         }
23495     },
23496
23497     onItemClick : function(item, index, e){
23498         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23499             return false;
23500         }
23501         if(this.multiSelect || this.singleSelect){
23502             if(this.multiSelect && e.shiftKey && this.lastSelection){
23503                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23504             }else{
23505                 this.select(item, this.multiSelect && e.ctrlKey);
23506                 this.lastSelection = item;
23507             }
23508             e.preventDefault();
23509         }
23510         return true;
23511     },
23512
23513     /**
23514      * Get the number of selected nodes.
23515      * @return {Number}
23516      */
23517     getSelectionCount : function(){
23518         return this.selections.length;
23519     },
23520
23521     /**
23522      * Get the currently selected nodes.
23523      * @return {Array} An array of HTMLElements
23524      */
23525     getSelectedNodes : function(){
23526         return this.selections;
23527     },
23528
23529     /**
23530      * Get the indexes of the selected nodes.
23531      * @return {Array}
23532      */
23533     getSelectedIndexes : function(){
23534         var indexes = [], s = this.selections;
23535         for(var i = 0, len = s.length; i < len; i++){
23536             indexes.push(s[i].nodeIndex);
23537         }
23538         return indexes;
23539     },
23540
23541     /**
23542      * Clear all selections
23543      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23544      */
23545     clearSelections : function(suppressEvent){
23546         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23547             this.cmp.elements = this.selections;
23548             this.cmp.removeClass(this.selectedClass);
23549             this.selections = [];
23550             if(!suppressEvent){
23551                 this.fireEvent("selectionchange", this, this.selections);
23552             }
23553         }
23554     },
23555
23556     /**
23557      * Returns true if the passed node is selected
23558      * @param {HTMLElement/Number} node The node or node index
23559      * @return {Boolean}
23560      */
23561     isSelected : function(node){
23562         var s = this.selections;
23563         if(s.length < 1){
23564             return false;
23565         }
23566         node = this.getNode(node);
23567         return s.indexOf(node) !== -1;
23568     },
23569
23570     /**
23571      * Selects nodes.
23572      * @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
23573      * @param {Boolean} keepExisting (optional) true to keep existing selections
23574      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23575      */
23576     select : function(nodeInfo, keepExisting, suppressEvent){
23577         if(nodeInfo instanceof Array){
23578             if(!keepExisting){
23579                 this.clearSelections(true);
23580             }
23581             for(var i = 0, len = nodeInfo.length; i < len; i++){
23582                 this.select(nodeInfo[i], true, true);
23583             }
23584         } else{
23585             var node = this.getNode(nodeInfo);
23586             if(node && !this.isSelected(node)){
23587                 if(!keepExisting){
23588                     this.clearSelections(true);
23589                 }
23590                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23591                     Roo.fly(node).addClass(this.selectedClass);
23592                     this.selections.push(node);
23593                     if(!suppressEvent){
23594                         this.fireEvent("selectionchange", this, this.selections);
23595                     }
23596                 }
23597             }
23598         }
23599     },
23600
23601     /**
23602      * Gets a template node.
23603      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23604      * @return {HTMLElement} The node or null if it wasn't found
23605      */
23606     getNode : function(nodeInfo){
23607         if(typeof nodeInfo == "string"){
23608             return document.getElementById(nodeInfo);
23609         }else if(typeof nodeInfo == "number"){
23610             return this.nodes[nodeInfo];
23611         }
23612         return nodeInfo;
23613     },
23614
23615     /**
23616      * Gets a range template nodes.
23617      * @param {Number} startIndex
23618      * @param {Number} endIndex
23619      * @return {Array} An array of nodes
23620      */
23621     getNodes : function(start, end){
23622         var ns = this.nodes;
23623         start = start || 0;
23624         end = typeof end == "undefined" ? ns.length - 1 : end;
23625         var nodes = [];
23626         if(start <= end){
23627             for(var i = start; i <= end; i++){
23628                 nodes.push(ns[i]);
23629             }
23630         } else{
23631             for(var i = start; i >= end; i--){
23632                 nodes.push(ns[i]);
23633             }
23634         }
23635         return nodes;
23636     },
23637
23638     /**
23639      * Finds the index of the passed node
23640      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23641      * @return {Number} The index of the node or -1
23642      */
23643     indexOf : function(node){
23644         node = this.getNode(node);
23645         if(typeof node.nodeIndex == "number"){
23646             return node.nodeIndex;
23647         }
23648         var ns = this.nodes;
23649         for(var i = 0, len = ns.length; i < len; i++){
23650             if(ns[i] == node){
23651                 return i;
23652             }
23653         }
23654         return -1;
23655     }
23656 });
23657 /*
23658  * Based on:
23659  * Ext JS Library 1.1.1
23660  * Copyright(c) 2006-2007, Ext JS, LLC.
23661  *
23662  * Originally Released Under LGPL - original licence link has changed is not relivant.
23663  *
23664  * Fork - LGPL
23665  * <script type="text/javascript">
23666  */
23667
23668 /**
23669  * @class Roo.JsonView
23670  * @extends Roo.View
23671  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23672 <pre><code>
23673 var view = new Roo.JsonView("my-element",
23674     '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23675     { multiSelect: true, jsonRoot: "data" }
23676 );
23677
23678 // listen for node click?
23679 view.on("click", function(vw, index, node, e){
23680     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23681 });
23682
23683 // direct load of JSON data
23684 view.load("foobar.php");
23685
23686 // Example from my blog list
23687 var tpl = new Roo.Template(
23688     '&lt;div class="entry"&gt;' +
23689     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23690     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23691     "&lt;/div&gt;&lt;hr /&gt;"
23692 );
23693
23694 var moreView = new Roo.JsonView("entry-list", tpl, {
23695     jsonRoot: "posts"
23696 });
23697 moreView.on("beforerender", this.sortEntries, this);
23698 moreView.load({
23699     url: "/blog/get-posts.php",
23700     params: "allposts=true",
23701     text: "Loading Blog Entries..."
23702 });
23703 </code></pre>
23704  * @constructor
23705  * Create a new JsonView
23706  * @param {String/HTMLElement/Element} container The container element where the view is to be rendered.
23707  * @param {Template} tpl The rendering template
23708  * @param {Object} config The config object
23709  */
23710 Roo.JsonView = function(container, tpl, config){
23711     Roo.JsonView.superclass.constructor.call(this, container, tpl, config);
23712
23713     var um = this.el.getUpdateManager();
23714     um.setRenderer(this);
23715     um.on("update", this.onLoad, this);
23716     um.on("failure", this.onLoadException, this);
23717
23718     /**
23719      * @event beforerender
23720      * Fires before rendering of the downloaded JSON data.
23721      * @param {Roo.JsonView} this
23722      * @param {Object} data The JSON data loaded
23723      */
23724     /**
23725      * @event load
23726      * Fires when data is loaded.
23727      * @param {Roo.JsonView} this
23728      * @param {Object} data The JSON data loaded
23729      * @param {Object} response The raw Connect response object
23730      */
23731     /**
23732      * @event loadexception
23733      * Fires when loading fails.
23734      * @param {Roo.JsonView} this
23735      * @param {Object} response The raw Connect response object
23736      */
23737     this.addEvents({
23738         'beforerender' : true,
23739         'load' : true,
23740         'loadexception' : true
23741     });
23742 };
23743 Roo.extend(Roo.JsonView, Roo.View, {
23744     /**
23745      * The root property in the loaded JSON object that contains the data
23746      * @type {String}
23747      */
23748     jsonRoot : "",
23749
23750     /**
23751      * Refreshes the view.
23752      */
23753     refresh : function(){
23754         this.clearSelections();
23755         this.el.update("");
23756         var html = [];
23757         var o = this.jsonData;
23758         if(o && o.length > 0){
23759             for(var i = 0, len = o.length; i < len; i++){
23760                 var data = this.prepareData(o[i], i, o);
23761                 html[html.length] = this.tpl.apply(data);
23762             }
23763         }else{
23764             html.push(this.emptyText);
23765         }
23766         this.el.update(html.join(""));
23767         this.nodes = this.el.dom.childNodes;
23768         this.updateIndexes(0);
23769     },
23770
23771     /**
23772      * 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.
23773      * @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:
23774      <pre><code>
23775      view.load({
23776          url: "your-url.php",
23777          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23778          callback: yourFunction,
23779          scope: yourObject, //(optional scope)
23780          discardUrl: false,
23781          nocache: false,
23782          text: "Loading...",
23783          timeout: 30,
23784          scripts: false
23785      });
23786      </code></pre>
23787      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23788      * 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.
23789      * @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}
23790      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23791      * @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.
23792      */
23793     load : function(){
23794         var um = this.el.getUpdateManager();
23795         um.update.apply(um, arguments);
23796     },
23797
23798     render : function(el, response){
23799         this.clearSelections();
23800         this.el.update("");
23801         var o;
23802         try{
23803             o = Roo.util.JSON.decode(response.responseText);
23804             if(this.jsonRoot){
23805                 
23806                 o = /** eval:var:o */ eval("o." + this.jsonRoot);
23807             }
23808         } catch(e){
23809         }
23810         /**
23811          * The current JSON data or null
23812          */
23813         this.jsonData = o;
23814         this.beforeRender();
23815         this.refresh();
23816     },
23817
23818 /**
23819  * Get the number of records in the current JSON dataset
23820  * @return {Number}
23821  */
23822     getCount : function(){
23823         return this.jsonData ? this.jsonData.length : 0;
23824     },
23825
23826 /**
23827  * Returns the JSON object for the specified node(s)
23828  * @param {HTMLElement/Array} node The node or an array of nodes
23829  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23830  * you get the JSON object for the node
23831  */
23832     getNodeData : function(node){
23833         if(node instanceof Array){
23834             var data = [];
23835             for(var i = 0, len = node.length; i < len; i++){
23836                 data.push(this.getNodeData(node[i]));
23837             }
23838             return data;
23839         }
23840         return this.jsonData[this.indexOf(node)] || null;
23841     },
23842
23843     beforeRender : function(){
23844         this.snapshot = this.jsonData;
23845         if(this.sortInfo){
23846             this.sort.apply(this, this.sortInfo);
23847         }
23848         this.fireEvent("beforerender", this, this.jsonData);
23849     },
23850
23851     onLoad : function(el, o){
23852         this.fireEvent("load", this, this.jsonData, o);
23853     },
23854
23855     onLoadException : function(el, o){
23856         this.fireEvent("loadexception", this, o);
23857     },
23858
23859 /**
23860  * Filter the data by a specific property.
23861  * @param {String} property A property on your JSON objects
23862  * @param {String/RegExp} value Either string that the property values
23863  * should start with, or a RegExp to test against the property
23864  */
23865     filter : function(property, value){
23866         if(this.jsonData){
23867             var data = [];
23868             var ss = this.snapshot;
23869             if(typeof value == "string"){
23870                 var vlen = value.length;
23871                 if(vlen == 0){
23872                     this.clearFilter();
23873                     return;
23874                 }
23875                 value = value.toLowerCase();
23876                 for(var i = 0, len = ss.length; i < len; i++){
23877                     var o = ss[i];
23878                     if(o[property].substr(0, vlen).toLowerCase() == value){
23879                         data.push(o);
23880                     }
23881                 }
23882             } else if(value.exec){ // regex?
23883                 for(var i = 0, len = ss.length; i < len; i++){
23884                     var o = ss[i];
23885                     if(value.test(o[property])){
23886                         data.push(o);
23887                     }
23888                 }
23889             } else{
23890                 return;
23891             }
23892             this.jsonData = data;
23893             this.refresh();
23894         }
23895     },
23896
23897 /**
23898  * Filter by a function. The passed function will be called with each
23899  * object in the current dataset. If the function returns true the value is kept,
23900  * otherwise it is filtered.
23901  * @param {Function} fn
23902  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
23903  */
23904     filterBy : function(fn, scope){
23905         if(this.jsonData){
23906             var data = [];
23907             var ss = this.snapshot;
23908             for(var i = 0, len = ss.length; i < len; i++){
23909                 var o = ss[i];
23910                 if(fn.call(scope || this, o)){
23911                     data.push(o);
23912                 }
23913             }
23914             this.jsonData = data;
23915             this.refresh();
23916         }
23917     },
23918
23919 /**
23920  * Clears the current filter.
23921  */
23922     clearFilter : function(){
23923         if(this.snapshot && this.jsonData != this.snapshot){
23924             this.jsonData = this.snapshot;
23925             this.refresh();
23926         }
23927     },
23928
23929
23930 /**
23931  * Sorts the data for this view and refreshes it.
23932  * @param {String} property A property on your JSON objects to sort on
23933  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
23934  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
23935  */
23936     sort : function(property, dir, sortType){
23937         this.sortInfo = Array.prototype.slice.call(arguments, 0);
23938         if(this.jsonData){
23939             var p = property;
23940             var dsc = dir && dir.toLowerCase() == "desc";
23941             var f = function(o1, o2){
23942                 var v1 = sortType ? sortType(o1[p]) : o1[p];
23943                 var v2 = sortType ? sortType(o2[p]) : o2[p];
23944                 ;
23945                 if(v1 < v2){
23946                     return dsc ? +1 : -1;
23947                 } else if(v1 > v2){
23948                     return dsc ? -1 : +1;
23949                 } else{
23950                     return 0;
23951                 }
23952             };
23953             this.jsonData.sort(f);
23954             this.refresh();
23955             if(this.jsonData != this.snapshot){
23956                 this.snapshot.sort(f);
23957             }
23958         }
23959     }
23960 });/*
23961  * Based on:
23962  * Ext JS Library 1.1.1
23963  * Copyright(c) 2006-2007, Ext JS, LLC.
23964  *
23965  * Originally Released Under LGPL - original licence link has changed is not relivant.
23966  *
23967  * Fork - LGPL
23968  * <script type="text/javascript">
23969  */
23970  
23971
23972 /**
23973  * @class Roo.ColorPalette
23974  * @extends Roo.Component
23975  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
23976  * Here's an example of typical usage:
23977  * <pre><code>
23978 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
23979 cp.render('my-div');
23980
23981 cp.on('select', function(palette, selColor){
23982     // do something with selColor
23983 });
23984 </code></pre>
23985  * @constructor
23986  * Create a new ColorPalette
23987  * @param {Object} config The config object
23988  */
23989 Roo.ColorPalette = function(config){
23990     Roo.ColorPalette.superclass.constructor.call(this, config);
23991     this.addEvents({
23992         /**
23993              * @event select
23994              * Fires when a color is selected
23995              * @param {ColorPalette} this
23996              * @param {String} color The 6-digit color hex code (without the # symbol)
23997              */
23998         select: true
23999     });
24000
24001     if(this.handler){
24002         this.on("select", this.handler, this.scope, true);
24003     }
24004 };
24005 Roo.extend(Roo.ColorPalette, Roo.Component, {
24006     /**
24007      * @cfg {String} itemCls
24008      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24009      */
24010     itemCls : "x-color-palette",
24011     /**
24012      * @cfg {String} value
24013      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24014      * the hex codes are case-sensitive.
24015      */
24016     value : null,
24017     clickEvent:'click',
24018     // private
24019     ctype: "Roo.ColorPalette",
24020
24021     /**
24022      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24023      */
24024     allowReselect : false,
24025
24026     /**
24027      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24028      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24029      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24030      * of colors with the width setting until the box is symmetrical.</p>
24031      * <p>You can override individual colors if needed:</p>
24032      * <pre><code>
24033 var cp = new Roo.ColorPalette();
24034 cp.colors[0] = "FF0000";  // change the first box to red
24035 </code></pre>
24036
24037 Or you can provide a custom array of your own for complete control:
24038 <pre><code>
24039 var cp = new Roo.ColorPalette();
24040 cp.colors = ["000000", "993300", "333300"];
24041 </code></pre>
24042      * @type Array
24043      */
24044     colors : [
24045         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24046         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24047         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24048         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24049         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24050     ],
24051
24052     // private
24053     onRender : function(container, position){
24054         var t = new Roo.MasterTemplate(
24055             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24056         );
24057         var c = this.colors;
24058         for(var i = 0, len = c.length; i < len; i++){
24059             t.add([c[i]]);
24060         }
24061         var el = document.createElement("div");
24062         el.className = this.itemCls;
24063         t.overwrite(el);
24064         container.dom.insertBefore(el, position);
24065         this.el = Roo.get(el);
24066         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24067         if(this.clickEvent != 'click'){
24068             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24069         }
24070     },
24071
24072     // private
24073     afterRender : function(){
24074         Roo.ColorPalette.superclass.afterRender.call(this);
24075         if(this.value){
24076             var s = this.value;
24077             this.value = null;
24078             this.select(s);
24079         }
24080     },
24081
24082     // private
24083     handleClick : function(e, t){
24084         e.preventDefault();
24085         if(!this.disabled){
24086             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24087             this.select(c.toUpperCase());
24088         }
24089     },
24090
24091     /**
24092      * Selects the specified color in the palette (fires the select event)
24093      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24094      */
24095     select : function(color){
24096         color = color.replace("#", "");
24097         if(color != this.value || this.allowReselect){
24098             var el = this.el;
24099             if(this.value){
24100                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24101             }
24102             el.child("a.color-"+color).addClass("x-color-palette-sel");
24103             this.value = color;
24104             this.fireEvent("select", this, color);
24105         }
24106     }
24107 });/*
24108  * Based on:
24109  * Ext JS Library 1.1.1
24110  * Copyright(c) 2006-2007, Ext JS, LLC.
24111  *
24112  * Originally Released Under LGPL - original licence link has changed is not relivant.
24113  *
24114  * Fork - LGPL
24115  * <script type="text/javascript">
24116  */
24117  
24118 /**
24119  * @class Roo.DatePicker
24120  * @extends Roo.Component
24121  * Simple date picker class.
24122  * @constructor
24123  * Create a new DatePicker
24124  * @param {Object} config The config object
24125  */
24126 Roo.DatePicker = function(config){
24127     Roo.DatePicker.superclass.constructor.call(this, config);
24128
24129     this.value = config && config.value ?
24130                  config.value.clearTime() : new Date().clearTime();
24131
24132     this.addEvents({
24133         /**
24134              * @event select
24135              * Fires when a date is selected
24136              * @param {DatePicker} this
24137              * @param {Date} date The selected date
24138              */
24139         select: true
24140     });
24141
24142     if(this.handler){
24143         this.on("select", this.handler,  this.scope || this);
24144     }
24145     // build the disabledDatesRE
24146     if(!this.disabledDatesRE && this.disabledDates){
24147         var dd = this.disabledDates;
24148         var re = "(?:";
24149         for(var i = 0; i < dd.length; i++){
24150             re += dd[i];
24151             if(i != dd.length-1) re += "|";
24152         }
24153         this.disabledDatesRE = new RegExp(re + ")");
24154     }
24155 };
24156
24157 Roo.extend(Roo.DatePicker, Roo.Component, {
24158     /**
24159      * @cfg {String} todayText
24160      * The text to display on the button that selects the current date (defaults to "Today")
24161      */
24162     todayText : "Today",
24163     /**
24164      * @cfg {String} okText
24165      * The text to display on the ok button
24166      */
24167     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24168     /**
24169      * @cfg {String} cancelText
24170      * The text to display on the cancel button
24171      */
24172     cancelText : "Cancel",
24173     /**
24174      * @cfg {String} todayTip
24175      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24176      */
24177     todayTip : "{0} (Spacebar)",
24178     /**
24179      * @cfg {Date} minDate
24180      * Minimum allowable date (JavaScript date object, defaults to null)
24181      */
24182     minDate : null,
24183     /**
24184      * @cfg {Date} maxDate
24185      * Maximum allowable date (JavaScript date object, defaults to null)
24186      */
24187     maxDate : null,
24188     /**
24189      * @cfg {String} minText
24190      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24191      */
24192     minText : "This date is before the minimum date",
24193     /**
24194      * @cfg {String} maxText
24195      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24196      */
24197     maxText : "This date is after the maximum date",
24198     /**
24199      * @cfg {String} format
24200      * The default date format string which can be overriden for localization support.  The format must be
24201      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24202      */
24203     format : "m/d/y",
24204     /**
24205      * @cfg {Array} disabledDays
24206      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24207      */
24208     disabledDays : null,
24209     /**
24210      * @cfg {String} disabledDaysText
24211      * The tooltip to display when the date falls on a disabled day (defaults to "")
24212      */
24213     disabledDaysText : "",
24214     /**
24215      * @cfg {RegExp} disabledDatesRE
24216      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24217      */
24218     disabledDatesRE : null,
24219     /**
24220      * @cfg {String} disabledDatesText
24221      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24222      */
24223     disabledDatesText : "",
24224     /**
24225      * @cfg {Boolean} constrainToViewport
24226      * True to constrain the date picker to the viewport (defaults to true)
24227      */
24228     constrainToViewport : true,
24229     /**
24230      * @cfg {Array} monthNames
24231      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24232      */
24233     monthNames : Date.monthNames,
24234     /**
24235      * @cfg {Array} dayNames
24236      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24237      */
24238     dayNames : Date.dayNames,
24239     /**
24240      * @cfg {String} nextText
24241      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24242      */
24243     nextText: 'Next Month (Control+Right)',
24244     /**
24245      * @cfg {String} prevText
24246      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24247      */
24248     prevText: 'Previous Month (Control+Left)',
24249     /**
24250      * @cfg {String} monthYearText
24251      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24252      */
24253     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24254     /**
24255      * @cfg {Number} startDay
24256      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24257      */
24258     startDay : 0,
24259     /**
24260      * @cfg {Bool} showClear
24261      * Show a clear button (usefull for date form elements that can be blank.)
24262      */
24263     
24264     showClear: false,
24265     
24266     /**
24267      * Sets the value of the date field
24268      * @param {Date} value The date to set
24269      */
24270     setValue : function(value){
24271         var old = this.value;
24272         this.value = value.clearTime(true);
24273         if(this.el){
24274             this.update(this.value);
24275         }
24276     },
24277
24278     /**
24279      * Gets the current selected value of the date field
24280      * @return {Date} The selected date
24281      */
24282     getValue : function(){
24283         return this.value;
24284     },
24285
24286     // private
24287     focus : function(){
24288         if(this.el){
24289             this.update(this.activeDate);
24290         }
24291     },
24292
24293     // private
24294     onRender : function(container, position){
24295         var m = [
24296              '<table cellspacing="0">',
24297                 '<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>',
24298                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24299         var dn = this.dayNames;
24300         for(var i = 0; i < 7; i++){
24301             var d = this.startDay+i;
24302             if(d > 6){
24303                 d = d-7;
24304             }
24305             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24306         }
24307         m[m.length] = "</tr></thead><tbody><tr>";
24308         for(var i = 0; i < 42; i++) {
24309             if(i % 7 == 0 && i != 0){
24310                 m[m.length] = "</tr><tr>";
24311             }
24312             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24313         }
24314         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24315             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24316
24317         var el = document.createElement("div");
24318         el.className = "x-date-picker";
24319         el.innerHTML = m.join("");
24320
24321         container.dom.insertBefore(el, position);
24322
24323         this.el = Roo.get(el);
24324         this.eventEl = Roo.get(el.firstChild);
24325
24326         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24327             handler: this.showPrevMonth,
24328             scope: this,
24329             preventDefault:true,
24330             stopDefault:true
24331         });
24332
24333         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24334             handler: this.showNextMonth,
24335             scope: this,
24336             preventDefault:true,
24337             stopDefault:true
24338         });
24339
24340         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24341
24342         this.monthPicker = this.el.down('div.x-date-mp');
24343         this.monthPicker.enableDisplayMode('block');
24344         
24345         var kn = new Roo.KeyNav(this.eventEl, {
24346             "left" : function(e){
24347                 e.ctrlKey ?
24348                     this.showPrevMonth() :
24349                     this.update(this.activeDate.add("d", -1));
24350             },
24351
24352             "right" : function(e){
24353                 e.ctrlKey ?
24354                     this.showNextMonth() :
24355                     this.update(this.activeDate.add("d", 1));
24356             },
24357
24358             "up" : function(e){
24359                 e.ctrlKey ?
24360                     this.showNextYear() :
24361                     this.update(this.activeDate.add("d", -7));
24362             },
24363
24364             "down" : function(e){
24365                 e.ctrlKey ?
24366                     this.showPrevYear() :
24367                     this.update(this.activeDate.add("d", 7));
24368             },
24369
24370             "pageUp" : function(e){
24371                 this.showNextMonth();
24372             },
24373
24374             "pageDown" : function(e){
24375                 this.showPrevMonth();
24376             },
24377
24378             "enter" : function(e){
24379                 e.stopPropagation();
24380                 return true;
24381             },
24382
24383             scope : this
24384         });
24385
24386         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24387
24388         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24389
24390         this.el.unselectable();
24391         
24392         this.cells = this.el.select("table.x-date-inner tbody td");
24393         this.textNodes = this.el.query("table.x-date-inner tbody span");
24394
24395         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24396             text: "&#160;",
24397             tooltip: this.monthYearText
24398         });
24399
24400         this.mbtn.on('click', this.showMonthPicker, this);
24401         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24402
24403
24404         var today = (new Date()).dateFormat(this.format);
24405         
24406         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24407         baseTb.add({
24408             text: String.format(this.todayText, today),
24409             tooltip: String.format(this.todayTip, today),
24410             handler: this.selectToday,
24411             scope: this
24412         });
24413         
24414         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24415             
24416         //});
24417         if (this.showClear) {
24418             
24419             baseTb.add( new Roo.Toolbar.Fill());
24420             baseTb.add({
24421                 text: '&#160;',
24422                 cls: 'x-btn-icon x-btn-clear',
24423                 handler: function() {
24424                     //this.value = '';
24425                     this.fireEvent("select", this, '');
24426                 },
24427                 scope: this
24428             });
24429         }
24430         
24431         
24432         if(Roo.isIE){
24433             this.el.repaint();
24434         }
24435         this.update(this.value);
24436     },
24437
24438     createMonthPicker : function(){
24439         if(!this.monthPicker.dom.firstChild){
24440             var buf = ['<table border="0" cellspacing="0">'];
24441             for(var i = 0; i < 6; i++){
24442                 buf.push(
24443                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24444                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24445                     i == 0 ?
24446                     '<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>' :
24447                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24448                 );
24449             }
24450             buf.push(
24451                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24452                     this.okText,
24453                     '</button><button type="button" class="x-date-mp-cancel">',
24454                     this.cancelText,
24455                     '</button></td></tr>',
24456                 '</table>'
24457             );
24458             this.monthPicker.update(buf.join(''));
24459             this.monthPicker.on('click', this.onMonthClick, this);
24460             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24461
24462             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24463             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24464
24465             this.mpMonths.each(function(m, a, i){
24466                 i += 1;
24467                 if((i%2) == 0){
24468                     m.dom.xmonth = 5 + Math.round(i * .5);
24469                 }else{
24470                     m.dom.xmonth = Math.round((i-1) * .5);
24471                 }
24472             });
24473         }
24474     },
24475
24476     showMonthPicker : function(){
24477         this.createMonthPicker();
24478         var size = this.el.getSize();
24479         this.monthPicker.setSize(size);
24480         this.monthPicker.child('table').setSize(size);
24481
24482         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24483         this.updateMPMonth(this.mpSelMonth);
24484         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24485         this.updateMPYear(this.mpSelYear);
24486
24487         this.monthPicker.slideIn('t', {duration:.2});
24488     },
24489
24490     updateMPYear : function(y){
24491         this.mpyear = y;
24492         var ys = this.mpYears.elements;
24493         for(var i = 1; i <= 10; i++){
24494             var td = ys[i-1], y2;
24495             if((i%2) == 0){
24496                 y2 = y + Math.round(i * .5);
24497                 td.firstChild.innerHTML = y2;
24498                 td.xyear = y2;
24499             }else{
24500                 y2 = y - (5-Math.round(i * .5));
24501                 td.firstChild.innerHTML = y2;
24502                 td.xyear = y2;
24503             }
24504             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24505         }
24506     },
24507
24508     updateMPMonth : function(sm){
24509         this.mpMonths.each(function(m, a, i){
24510             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24511         });
24512     },
24513
24514     selectMPMonth: function(m){
24515         
24516     },
24517
24518     onMonthClick : function(e, t){
24519         e.stopEvent();
24520         var el = new Roo.Element(t), pn;
24521         if(el.is('button.x-date-mp-cancel')){
24522             this.hideMonthPicker();
24523         }
24524         else if(el.is('button.x-date-mp-ok')){
24525             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24526             this.hideMonthPicker();
24527         }
24528         else if(pn = el.up('td.x-date-mp-month', 2)){
24529             this.mpMonths.removeClass('x-date-mp-sel');
24530             pn.addClass('x-date-mp-sel');
24531             this.mpSelMonth = pn.dom.xmonth;
24532         }
24533         else if(pn = el.up('td.x-date-mp-year', 2)){
24534             this.mpYears.removeClass('x-date-mp-sel');
24535             pn.addClass('x-date-mp-sel');
24536             this.mpSelYear = pn.dom.xyear;
24537         }
24538         else if(el.is('a.x-date-mp-prev')){
24539             this.updateMPYear(this.mpyear-10);
24540         }
24541         else if(el.is('a.x-date-mp-next')){
24542             this.updateMPYear(this.mpyear+10);
24543         }
24544     },
24545
24546     onMonthDblClick : function(e, t){
24547         e.stopEvent();
24548         var el = new Roo.Element(t), pn;
24549         if(pn = el.up('td.x-date-mp-month', 2)){
24550             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24551             this.hideMonthPicker();
24552         }
24553         else if(pn = el.up('td.x-date-mp-year', 2)){
24554             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24555             this.hideMonthPicker();
24556         }
24557     },
24558
24559     hideMonthPicker : function(disableAnim){
24560         if(this.monthPicker){
24561             if(disableAnim === true){
24562                 this.monthPicker.hide();
24563             }else{
24564                 this.monthPicker.slideOut('t', {duration:.2});
24565             }
24566         }
24567     },
24568
24569     // private
24570     showPrevMonth : function(e){
24571         this.update(this.activeDate.add("mo", -1));
24572     },
24573
24574     // private
24575     showNextMonth : function(e){
24576         this.update(this.activeDate.add("mo", 1));
24577     },
24578
24579     // private
24580     showPrevYear : function(){
24581         this.update(this.activeDate.add("y", -1));
24582     },
24583
24584     // private
24585     showNextYear : function(){
24586         this.update(this.activeDate.add("y", 1));
24587     },
24588
24589     // private
24590     handleMouseWheel : function(e){
24591         var delta = e.getWheelDelta();
24592         if(delta > 0){
24593             this.showPrevMonth();
24594             e.stopEvent();
24595         } else if(delta < 0){
24596             this.showNextMonth();
24597             e.stopEvent();
24598         }
24599     },
24600
24601     // private
24602     handleDateClick : function(e, t){
24603         e.stopEvent();
24604         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24605             this.setValue(new Date(t.dateValue));
24606             this.fireEvent("select", this, this.value);
24607         }
24608     },
24609
24610     // private
24611     selectToday : function(){
24612         this.setValue(new Date().clearTime());
24613         this.fireEvent("select", this, this.value);
24614     },
24615
24616     // private
24617     update : function(date){
24618         var vd = this.activeDate;
24619         this.activeDate = date;
24620         if(vd && this.el){
24621             var t = date.getTime();
24622             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24623                 this.cells.removeClass("x-date-selected");
24624                 this.cells.each(function(c){
24625                    if(c.dom.firstChild.dateValue == t){
24626                        c.addClass("x-date-selected");
24627                        setTimeout(function(){
24628                             try{c.dom.firstChild.focus();}catch(e){}
24629                        }, 50);
24630                        return false;
24631                    }
24632                 });
24633                 return;
24634             }
24635         }
24636         var days = date.getDaysInMonth();
24637         var firstOfMonth = date.getFirstDateOfMonth();
24638         var startingPos = firstOfMonth.getDay()-this.startDay;
24639
24640         if(startingPos <= this.startDay){
24641             startingPos += 7;
24642         }
24643
24644         var pm = date.add("mo", -1);
24645         var prevStart = pm.getDaysInMonth()-startingPos;
24646
24647         var cells = this.cells.elements;
24648         var textEls = this.textNodes;
24649         days += startingPos;
24650
24651         // convert everything to numbers so it's fast
24652         var day = 86400000;
24653         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24654         var today = new Date().clearTime().getTime();
24655         var sel = date.clearTime().getTime();
24656         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24657         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24658         var ddMatch = this.disabledDatesRE;
24659         var ddText = this.disabledDatesText;
24660         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24661         var ddaysText = this.disabledDaysText;
24662         var format = this.format;
24663
24664         var setCellClass = function(cal, cell){
24665             cell.title = "";
24666             var t = d.getTime();
24667             cell.firstChild.dateValue = t;
24668             if(t == today){
24669                 cell.className += " x-date-today";
24670                 cell.title = cal.todayText;
24671             }
24672             if(t == sel){
24673                 cell.className += " x-date-selected";
24674                 setTimeout(function(){
24675                     try{cell.firstChild.focus();}catch(e){}
24676                 }, 50);
24677             }
24678             // disabling
24679             if(t < min) {
24680                 cell.className = " x-date-disabled";
24681                 cell.title = cal.minText;
24682                 return;
24683             }
24684             if(t > max) {
24685                 cell.className = " x-date-disabled";
24686                 cell.title = cal.maxText;
24687                 return;
24688             }
24689             if(ddays){
24690                 if(ddays.indexOf(d.getDay()) != -1){
24691                     cell.title = ddaysText;
24692                     cell.className = " x-date-disabled";
24693                 }
24694             }
24695             if(ddMatch && format){
24696                 var fvalue = d.dateFormat(format);
24697                 if(ddMatch.test(fvalue)){
24698                     cell.title = ddText.replace("%0", fvalue);
24699                     cell.className = " x-date-disabled";
24700                 }
24701             }
24702         };
24703
24704         var i = 0;
24705         for(; i < startingPos; i++) {
24706             textEls[i].innerHTML = (++prevStart);
24707             d.setDate(d.getDate()+1);
24708             cells[i].className = "x-date-prevday";
24709             setCellClass(this, cells[i]);
24710         }
24711         for(; i < days; i++){
24712             intDay = i - startingPos + 1;
24713             textEls[i].innerHTML = (intDay);
24714             d.setDate(d.getDate()+1);
24715             cells[i].className = "x-date-active";
24716             setCellClass(this, cells[i]);
24717         }
24718         var extraDays = 0;
24719         for(; i < 42; i++) {
24720              textEls[i].innerHTML = (++extraDays);
24721              d.setDate(d.getDate()+1);
24722              cells[i].className = "x-date-nextday";
24723              setCellClass(this, cells[i]);
24724         }
24725
24726         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24727
24728         if(!this.internalRender){
24729             var main = this.el.dom.firstChild;
24730             var w = main.offsetWidth;
24731             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24732             Roo.fly(main).setWidth(w);
24733             this.internalRender = true;
24734             // opera does not respect the auto grow header center column
24735             // then, after it gets a width opera refuses to recalculate
24736             // without a second pass
24737             if(Roo.isOpera && !this.secondPass){
24738                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24739                 this.secondPass = true;
24740                 this.update.defer(10, this, [date]);
24741             }
24742         }
24743     }
24744 });/*
24745  * Based on:
24746  * Ext JS Library 1.1.1
24747  * Copyright(c) 2006-2007, Ext JS, LLC.
24748  *
24749  * Originally Released Under LGPL - original licence link has changed is not relivant.
24750  *
24751  * Fork - LGPL
24752  * <script type="text/javascript">
24753  */
24754 /**
24755  * @class Roo.TabPanel
24756  * @extends Roo.util.Observable
24757  * A lightweight tab container.
24758  * <br><br>
24759  * Usage:
24760  * <pre><code>
24761 // basic tabs 1, built from existing content
24762 var tabs = new Roo.TabPanel("tabs1");
24763 tabs.addTab("script", "View Script");
24764 tabs.addTab("markup", "View Markup");
24765 tabs.activate("script");
24766
24767 // more advanced tabs, built from javascript
24768 var jtabs = new Roo.TabPanel("jtabs");
24769 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24770
24771 // set up the UpdateManager
24772 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24773 var updater = tab2.getUpdateManager();
24774 updater.setDefaultUrl("ajax1.htm");
24775 tab2.on('activate', updater.refresh, updater, true);
24776
24777 // Use setUrl for Ajax loading
24778 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24779 tab3.setUrl("ajax2.htm", null, true);
24780
24781 // Disabled tab
24782 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24783 tab4.disable();
24784
24785 jtabs.activate("jtabs-1");
24786  * </code></pre>
24787  * @constructor
24788  * Create a new TabPanel.
24789  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24790  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24791  */
24792 Roo.TabPanel = function(container, config){
24793     /**
24794     * The container element for this TabPanel.
24795     * @type Roo.Element
24796     */
24797     this.el = Roo.get(container, true);
24798     if(config){
24799         if(typeof config == "boolean"){
24800             this.tabPosition = config ? "bottom" : "top";
24801         }else{
24802             Roo.apply(this, config);
24803         }
24804     }
24805     if(this.tabPosition == "bottom"){
24806         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24807         this.el.addClass("x-tabs-bottom");
24808     }
24809     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24810     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24811     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24812     if(Roo.isIE){
24813         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24814     }
24815     if(this.tabPosition != "bottom"){
24816     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24817      * @type Roo.Element
24818      */
24819       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24820       this.el.addClass("x-tabs-top");
24821     }
24822     this.items = [];
24823
24824     this.bodyEl.setStyle("position", "relative");
24825
24826     this.active = null;
24827     this.activateDelegate = this.activate.createDelegate(this);
24828
24829     this.addEvents({
24830         /**
24831          * @event tabchange
24832          * Fires when the active tab changes
24833          * @param {Roo.TabPanel} this
24834          * @param {Roo.TabPanelItem} activePanel The new active tab
24835          */
24836         "tabchange": true,
24837         /**
24838          * @event beforetabchange
24839          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24840          * @param {Roo.TabPanel} this
24841          * @param {Object} e Set cancel to true on this object to cancel the tab change
24842          * @param {Roo.TabPanelItem} tab The tab being changed to
24843          */
24844         "beforetabchange" : true
24845     });
24846
24847     Roo.EventManager.onWindowResize(this.onResize, this);
24848     this.cpad = this.el.getPadding("lr");
24849     this.hiddenCount = 0;
24850
24851     Roo.TabPanel.superclass.constructor.call(this);
24852 };
24853
24854 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
24855         /*
24856          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
24857          */
24858     tabPosition : "top",
24859         /*
24860          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
24861          */
24862     currentTabWidth : 0,
24863         /*
24864          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
24865          */
24866     minTabWidth : 40,
24867         /*
24868          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
24869          */
24870     maxTabWidth : 250,
24871         /*
24872          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
24873          */
24874     preferredTabWidth : 175,
24875         /*
24876          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
24877          */
24878     resizeTabs : false,
24879         /*
24880          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
24881          */
24882     monitorResize : true,
24883
24884     /**
24885      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
24886      * @param {String} id The id of the div to use <b>or create</b>
24887      * @param {String} text The text for the tab
24888      * @param {String} content (optional) Content to put in the TabPanelItem body
24889      * @param {Boolean} closable (optional) True to create a close icon on the tab
24890      * @return {Roo.TabPanelItem} The created TabPanelItem
24891      */
24892     addTab : function(id, text, content, closable){
24893         var item = new Roo.TabPanelItem(this, id, text, closable);
24894         this.addTabItem(item);
24895         if(content){
24896             item.setContent(content);
24897         }
24898         return item;
24899     },
24900
24901     /**
24902      * Returns the {@link Roo.TabPanelItem} with the specified id/index
24903      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
24904      * @return {Roo.TabPanelItem}
24905      */
24906     getTab : function(id){
24907         return this.items[id];
24908     },
24909
24910     /**
24911      * Hides the {@link Roo.TabPanelItem} with the specified id/index
24912      * @param {String/Number} id The id or index of the TabPanelItem to hide.
24913      */
24914     hideTab : function(id){
24915         var t = this.items[id];
24916         if(!t.isHidden()){
24917            t.setHidden(true);
24918            this.hiddenCount++;
24919            this.autoSizeTabs();
24920         }
24921     },
24922
24923     /**
24924      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
24925      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
24926      */
24927     unhideTab : function(id){
24928         var t = this.items[id];
24929         if(t.isHidden()){
24930            t.setHidden(false);
24931            this.hiddenCount--;
24932            this.autoSizeTabs();
24933         }
24934     },
24935
24936     /**
24937      * Adds an existing {@link Roo.TabPanelItem}.
24938      * @param {Roo.TabPanelItem} item The TabPanelItem to add
24939      */
24940     addTabItem : function(item){
24941         this.items[item.id] = item;
24942         this.items.push(item);
24943         if(this.resizeTabs){
24944            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
24945            this.autoSizeTabs();
24946         }else{
24947             item.autoSize();
24948         }
24949     },
24950
24951     /**
24952      * Removes a {@link Roo.TabPanelItem}.
24953      * @param {String/Number} id The id or index of the TabPanelItem to remove.
24954      */
24955     removeTab : function(id){
24956         var items = this.items;
24957         var tab = items[id];
24958         if(!tab) return;
24959         var index = items.indexOf(tab);
24960         if(this.active == tab && items.length > 1){
24961             var newTab = this.getNextAvailable(index);
24962             if(newTab)newTab.activate();
24963         }
24964         this.stripEl.dom.removeChild(tab.pnode.dom);
24965         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
24966             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
24967         }
24968         items.splice(index, 1);
24969         delete this.items[tab.id];
24970         tab.fireEvent("close", tab);
24971         tab.purgeListeners();
24972         this.autoSizeTabs();
24973     },
24974
24975     getNextAvailable : function(start){
24976         var items = this.items;
24977         var index = start;
24978         // look for a next tab that will slide over to
24979         // replace the one being removed
24980         while(index < items.length){
24981             var item = items[++index];
24982             if(item && !item.isHidden()){
24983                 return item;
24984             }
24985         }
24986         // if one isn't found select the previous tab (on the left)
24987         index = start;
24988         while(index >= 0){
24989             var item = items[--index];
24990             if(item && !item.isHidden()){
24991                 return item;
24992             }
24993         }
24994         return null;
24995     },
24996
24997     /**
24998      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
24999      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25000      */
25001     disableTab : function(id){
25002         var tab = this.items[id];
25003         if(tab && this.active != tab){
25004             tab.disable();
25005         }
25006     },
25007
25008     /**
25009      * Enables a {@link Roo.TabPanelItem} that is disabled.
25010      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25011      */
25012     enableTab : function(id){
25013         var tab = this.items[id];
25014         tab.enable();
25015     },
25016
25017     /**
25018      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25019      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25020      * @return {Roo.TabPanelItem} The TabPanelItem.
25021      */
25022     activate : function(id){
25023         var tab = this.items[id];
25024         if(!tab){
25025             return null;
25026         }
25027         if(tab == this.active || tab.disabled){
25028             return tab;
25029         }
25030         var e = {};
25031         this.fireEvent("beforetabchange", this, e, tab);
25032         if(e.cancel !== true && !tab.disabled){
25033             if(this.active){
25034                 this.active.hide();
25035             }
25036             this.active = this.items[id];
25037             this.active.show();
25038             this.fireEvent("tabchange", this, this.active);
25039         }
25040         return tab;
25041     },
25042
25043     /**
25044      * Gets the active {@link Roo.TabPanelItem}.
25045      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25046      */
25047     getActiveTab : function(){
25048         return this.active;
25049     },
25050
25051     /**
25052      * Updates the tab body element to fit the height of the container element
25053      * for overflow scrolling
25054      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25055      */
25056     syncHeight : function(targetHeight){
25057         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25058         var bm = this.bodyEl.getMargins();
25059         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25060         this.bodyEl.setHeight(newHeight);
25061         return newHeight;
25062     },
25063
25064     onResize : function(){
25065         if(this.monitorResize){
25066             this.autoSizeTabs();
25067         }
25068     },
25069
25070     /**
25071      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25072      */
25073     beginUpdate : function(){
25074         this.updating = true;
25075     },
25076
25077     /**
25078      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25079      */
25080     endUpdate : function(){
25081         this.updating = false;
25082         this.autoSizeTabs();
25083     },
25084
25085     /**
25086      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25087      */
25088     autoSizeTabs : function(){
25089         var count = this.items.length;
25090         var vcount = count - this.hiddenCount;
25091         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25092         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25093         var availWidth = Math.floor(w / vcount);
25094         var b = this.stripBody;
25095         if(b.getWidth() > w){
25096             var tabs = this.items;
25097             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25098             if(availWidth < this.minTabWidth){
25099                 /*if(!this.sleft){    // incomplete scrolling code
25100                     this.createScrollButtons();
25101                 }
25102                 this.showScroll();
25103                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25104             }
25105         }else{
25106             if(this.currentTabWidth < this.preferredTabWidth){
25107                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25108             }
25109         }
25110     },
25111
25112     /**
25113      * Returns the number of tabs in this TabPanel.
25114      * @return {Number}
25115      */
25116      getCount : function(){
25117          return this.items.length;
25118      },
25119
25120     /**
25121      * Resizes all the tabs to the passed width
25122      * @param {Number} The new width
25123      */
25124     setTabWidth : function(width){
25125         this.currentTabWidth = width;
25126         for(var i = 0, len = this.items.length; i < len; i++) {
25127                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25128         }
25129     },
25130
25131     /**
25132      * Destroys this TabPanel
25133      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25134      */
25135     destroy : function(removeEl){
25136         Roo.EventManager.removeResizeListener(this.onResize, this);
25137         for(var i = 0, len = this.items.length; i < len; i++){
25138             this.items[i].purgeListeners();
25139         }
25140         if(removeEl === true){
25141             this.el.update("");
25142             this.el.remove();
25143         }
25144     }
25145 });
25146
25147 /**
25148  * @class Roo.TabPanelItem
25149  * @extends Roo.util.Observable
25150  * Represents an individual item (tab plus body) in a TabPanel.
25151  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25152  * @param {String} id The id of this TabPanelItem
25153  * @param {String} text The text for the tab of this TabPanelItem
25154  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25155  */
25156 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25157     /**
25158      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25159      * @type Roo.TabPanel
25160      */
25161     this.tabPanel = tabPanel;
25162     /**
25163      * The id for this TabPanelItem
25164      * @type String
25165      */
25166     this.id = id;
25167     /** @private */
25168     this.disabled = false;
25169     /** @private */
25170     this.text = text;
25171     /** @private */
25172     this.loaded = false;
25173     this.closable = closable;
25174
25175     /**
25176      * The body element for this TabPanelItem.
25177      * @type Roo.Element
25178      */
25179     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25180     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25181     this.bodyEl.setStyle("display", "block");
25182     this.bodyEl.setStyle("zoom", "1");
25183     this.hideAction();
25184
25185     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25186     /** @private */
25187     this.el = Roo.get(els.el, true);
25188     this.inner = Roo.get(els.inner, true);
25189     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25190     this.pnode = Roo.get(els.el.parentNode, true);
25191     this.el.on("mousedown", this.onTabMouseDown, this);
25192     this.el.on("click", this.onTabClick, this);
25193     /** @private */
25194     if(closable){
25195         var c = Roo.get(els.close, true);
25196         c.dom.title = this.closeText;
25197         c.addClassOnOver("close-over");
25198         c.on("click", this.closeClick, this);
25199      }
25200
25201     this.addEvents({
25202          /**
25203          * @event activate
25204          * Fires when this tab becomes the active tab.
25205          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25206          * @param {Roo.TabPanelItem} this
25207          */
25208         "activate": true,
25209         /**
25210          * @event beforeclose
25211          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25212          * @param {Roo.TabPanelItem} this
25213          * @param {Object} e Set cancel to true on this object to cancel the close.
25214          */
25215         "beforeclose": true,
25216         /**
25217          * @event close
25218          * Fires when this tab is closed.
25219          * @param {Roo.TabPanelItem} this
25220          */
25221          "close": true,
25222         /**
25223          * @event deactivate
25224          * Fires when this tab is no longer the active tab.
25225          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25226          * @param {Roo.TabPanelItem} this
25227          */
25228          "deactivate" : true
25229     });
25230     this.hidden = false;
25231
25232     Roo.TabPanelItem.superclass.constructor.call(this);
25233 };
25234
25235 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25236     purgeListeners : function(){
25237        Roo.util.Observable.prototype.purgeListeners.call(this);
25238        this.el.removeAllListeners();
25239     },
25240     /**
25241      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25242      */
25243     show : function(){
25244         this.pnode.addClass("on");
25245         this.showAction();
25246         if(Roo.isOpera){
25247             this.tabPanel.stripWrap.repaint();
25248         }
25249         this.fireEvent("activate", this.tabPanel, this);
25250     },
25251
25252     /**
25253      * Returns true if this tab is the active tab.
25254      * @return {Boolean}
25255      */
25256     isActive : function(){
25257         return this.tabPanel.getActiveTab() == this;
25258     },
25259
25260     /**
25261      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25262      */
25263     hide : function(){
25264         this.pnode.removeClass("on");
25265         this.hideAction();
25266         this.fireEvent("deactivate", this.tabPanel, this);
25267     },
25268
25269     hideAction : function(){
25270         this.bodyEl.hide();
25271         this.bodyEl.setStyle("position", "absolute");
25272         this.bodyEl.setLeft("-20000px");
25273         this.bodyEl.setTop("-20000px");
25274     },
25275
25276     showAction : function(){
25277         this.bodyEl.setStyle("position", "relative");
25278         this.bodyEl.setTop("");
25279         this.bodyEl.setLeft("");
25280         this.bodyEl.show();
25281     },
25282
25283     /**
25284      * Set the tooltip for the tab.
25285      * @param {String} tooltip The tab's tooltip
25286      */
25287     setTooltip : function(text){
25288         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25289             this.textEl.dom.qtip = text;
25290             this.textEl.dom.removeAttribute('title');
25291         }else{
25292             this.textEl.dom.title = text;
25293         }
25294     },
25295
25296     onTabClick : function(e){
25297         e.preventDefault();
25298         this.tabPanel.activate(this.id);
25299     },
25300
25301     onTabMouseDown : function(e){
25302         e.preventDefault();
25303         this.tabPanel.activate(this.id);
25304     },
25305
25306     getWidth : function(){
25307         return this.inner.getWidth();
25308     },
25309
25310     setWidth : function(width){
25311         var iwidth = width - this.pnode.getPadding("lr");
25312         this.inner.setWidth(iwidth);
25313         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25314         this.pnode.setWidth(width);
25315     },
25316
25317     /**
25318      * Show or hide the tab
25319      * @param {Boolean} hidden True to hide or false to show.
25320      */
25321     setHidden : function(hidden){
25322         this.hidden = hidden;
25323         this.pnode.setStyle("display", hidden ? "none" : "");
25324     },
25325
25326     /**
25327      * Returns true if this tab is "hidden"
25328      * @return {Boolean}
25329      */
25330     isHidden : function(){
25331         return this.hidden;
25332     },
25333
25334     /**
25335      * Returns the text for this tab
25336      * @return {String}
25337      */
25338     getText : function(){
25339         return this.text;
25340     },
25341
25342     autoSize : function(){
25343         //this.el.beginMeasure();
25344         this.textEl.setWidth(1);
25345         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25346         //this.el.endMeasure();
25347     },
25348
25349     /**
25350      * Sets the text for the tab (Note: this also sets the tooltip text)
25351      * @param {String} text The tab's text and tooltip
25352      */
25353     setText : function(text){
25354         this.text = text;
25355         this.textEl.update(text);
25356         this.setTooltip(text);
25357         if(!this.tabPanel.resizeTabs){
25358             this.autoSize();
25359         }
25360     },
25361     /**
25362      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25363      */
25364     activate : function(){
25365         this.tabPanel.activate(this.id);
25366     },
25367
25368     /**
25369      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25370      */
25371     disable : function(){
25372         if(this.tabPanel.active != this){
25373             this.disabled = true;
25374             this.pnode.addClass("disabled");
25375         }
25376     },
25377
25378     /**
25379      * Enables this TabPanelItem if it was previously disabled.
25380      */
25381     enable : function(){
25382         this.disabled = false;
25383         this.pnode.removeClass("disabled");
25384     },
25385
25386     /**
25387      * Sets the content for this TabPanelItem.
25388      * @param {String} content The content
25389      * @param {Boolean} loadScripts true to look for and load scripts
25390      */
25391     setContent : function(content, loadScripts){
25392         this.bodyEl.update(content, loadScripts);
25393     },
25394
25395     /**
25396      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25397      * @return {Roo.UpdateManager} The UpdateManager
25398      */
25399     getUpdateManager : function(){
25400         return this.bodyEl.getUpdateManager();
25401     },
25402
25403     /**
25404      * Set a URL to be used to load the content for this TabPanelItem.
25405      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25406      * @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)
25407      * @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)
25408      * @return {Roo.UpdateManager} The UpdateManager
25409      */
25410     setUrl : function(url, params, loadOnce){
25411         if(this.refreshDelegate){
25412             this.un('activate', this.refreshDelegate);
25413         }
25414         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25415         this.on("activate", this.refreshDelegate);
25416         return this.bodyEl.getUpdateManager();
25417     },
25418
25419     /** @private */
25420     _handleRefresh : function(url, params, loadOnce){
25421         if(!loadOnce || !this.loaded){
25422             var updater = this.bodyEl.getUpdateManager();
25423             updater.update(url, params, this._setLoaded.createDelegate(this));
25424         }
25425     },
25426
25427     /**
25428      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25429      *   Will fail silently if the setUrl method has not been called.
25430      *   This does not activate the panel, just updates its content.
25431      */
25432     refresh : function(){
25433         if(this.refreshDelegate){
25434            this.loaded = false;
25435            this.refreshDelegate();
25436         }
25437     },
25438
25439     /** @private */
25440     _setLoaded : function(){
25441         this.loaded = true;
25442     },
25443
25444     /** @private */
25445     closeClick : function(e){
25446         var o = {};
25447         e.stopEvent();
25448         this.fireEvent("beforeclose", this, o);
25449         if(o.cancel !== true){
25450             this.tabPanel.removeTab(this.id);
25451         }
25452     },
25453     /**
25454      * The text displayed in the tooltip for the close icon.
25455      * @type String
25456      */
25457     closeText : "Close this tab"
25458 });
25459
25460 /** @private */
25461 Roo.TabPanel.prototype.createStrip = function(container){
25462     var strip = document.createElement("div");
25463     strip.className = "x-tabs-wrap";
25464     container.appendChild(strip);
25465     return strip;
25466 };
25467 /** @private */
25468 Roo.TabPanel.prototype.createStripList = function(strip){
25469     // div wrapper for retard IE
25470     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>';
25471     return strip.firstChild.firstChild.firstChild.firstChild;
25472 };
25473 /** @private */
25474 Roo.TabPanel.prototype.createBody = function(container){
25475     var body = document.createElement("div");
25476     Roo.id(body, "tab-body");
25477     Roo.fly(body).addClass("x-tabs-body");
25478     container.appendChild(body);
25479     return body;
25480 };
25481 /** @private */
25482 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25483     var body = Roo.getDom(id);
25484     if(!body){
25485         body = document.createElement("div");
25486         body.id = id;
25487     }
25488     Roo.fly(body).addClass("x-tabs-item-body");
25489     bodyEl.insertBefore(body, bodyEl.firstChild);
25490     return body;
25491 };
25492 /** @private */
25493 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25494     var td = document.createElement("td");
25495     stripEl.appendChild(td);
25496     if(closable){
25497         td.className = "x-tabs-closable";
25498         if(!this.closeTpl){
25499             this.closeTpl = new Roo.Template(
25500                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25501                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25502                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25503             );
25504         }
25505         var el = this.closeTpl.overwrite(td, {"text": text});
25506         var close = el.getElementsByTagName("div")[0];
25507         var inner = el.getElementsByTagName("em")[0];
25508         return {"el": el, "close": close, "inner": inner};
25509     } else {
25510         if(!this.tabTpl){
25511             this.tabTpl = new Roo.Template(
25512                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25513                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25514             );
25515         }
25516         var el = this.tabTpl.overwrite(td, {"text": text});
25517         var inner = el.getElementsByTagName("em")[0];
25518         return {"el": el, "inner": inner};
25519     }
25520 };/*
25521  * Based on:
25522  * Ext JS Library 1.1.1
25523  * Copyright(c) 2006-2007, Ext JS, LLC.
25524  *
25525  * Originally Released Under LGPL - original licence link has changed is not relivant.
25526  *
25527  * Fork - LGPL
25528  * <script type="text/javascript">
25529  */
25530
25531 /**
25532  * @class Roo.Button
25533  * @extends Roo.util.Observable
25534  * Simple Button class
25535  * @cfg {String} text The button text
25536  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25537  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25538  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25539  * @cfg {Object} scope The scope of the handler
25540  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25541  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25542  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25543  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25544  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25545  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25546    applies if enableToggle = true)
25547  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25548  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25549   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25550  * @constructor
25551  * Create a new button
25552  * @param {Object} config The config object
25553  */
25554 Roo.Button = function(renderTo, config)
25555 {
25556     if (!config) {
25557         config = renderTo;
25558         renderTo = config.renderTo || false;
25559     }
25560     
25561     Roo.apply(this, config);
25562     this.addEvents({
25563         /**
25564              * @event click
25565              * Fires when this button is clicked
25566              * @param {Button} this
25567              * @param {EventObject} e The click event
25568              */
25569             "click" : true,
25570         /**
25571              * @event toggle
25572              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25573              * @param {Button} this
25574              * @param {Boolean} pressed
25575              */
25576             "toggle" : true,
25577         /**
25578              * @event mouseover
25579              * Fires when the mouse hovers over the button
25580              * @param {Button} this
25581              * @param {Event} e The event object
25582              */
25583         'mouseover' : true,
25584         /**
25585              * @event mouseout
25586              * Fires when the mouse exits the button
25587              * @param {Button} this
25588              * @param {Event} e The event object
25589              */
25590         'mouseout': true,
25591          /**
25592              * @event render
25593              * Fires when the button is rendered
25594              * @param {Button} this
25595              */
25596         'render': true
25597     });
25598     if(this.menu){
25599         this.menu = Roo.menu.MenuMgr.get(this.menu);
25600     }
25601     if(renderTo){
25602         this.render(renderTo);
25603     }
25604     
25605     Roo.util.Observable.call(this);
25606 };
25607
25608 Roo.extend(Roo.Button, Roo.util.Observable, {
25609     /**
25610      * 
25611      */
25612     
25613     /**
25614      * Read-only. True if this button is hidden
25615      * @type Boolean
25616      */
25617     hidden : false,
25618     /**
25619      * Read-only. True if this button is disabled
25620      * @type Boolean
25621      */
25622     disabled : false,
25623     /**
25624      * Read-only. True if this button is pressed (only if enableToggle = true)
25625      * @type Boolean
25626      */
25627     pressed : false,
25628
25629     /**
25630      * @cfg {Number} tabIndex 
25631      * The DOM tabIndex for this button (defaults to undefined)
25632      */
25633     tabIndex : undefined,
25634
25635     /**
25636      * @cfg {Boolean} enableToggle
25637      * True to enable pressed/not pressed toggling (defaults to false)
25638      */
25639     enableToggle: false,
25640     /**
25641      * @cfg {Mixed} menu
25642      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25643      */
25644     menu : undefined,
25645     /**
25646      * @cfg {String} menuAlign
25647      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25648      */
25649     menuAlign : "tl-bl?",
25650
25651     /**
25652      * @cfg {String} iconCls
25653      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25654      */
25655     iconCls : undefined,
25656     /**
25657      * @cfg {String} type
25658      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25659      */
25660     type : 'button',
25661
25662     // private
25663     menuClassTarget: 'tr',
25664
25665     /**
25666      * @cfg {String} clickEvent
25667      * The type of event to map to the button's event handler (defaults to 'click')
25668      */
25669     clickEvent : 'click',
25670
25671     /**
25672      * @cfg {Boolean} handleMouseEvents
25673      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25674      */
25675     handleMouseEvents : true,
25676
25677     /**
25678      * @cfg {String} tooltipType
25679      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25680      */
25681     tooltipType : 'qtip',
25682
25683     /**
25684      * @cfg {String} cls
25685      * A CSS class to apply to the button's main element.
25686      */
25687     
25688     /**
25689      * @cfg {Roo.Template} template (Optional)
25690      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25691      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25692      * require code modifications if required elements (e.g. a button) aren't present.
25693      */
25694
25695     // private
25696     render : function(renderTo){
25697         var btn;
25698         if(this.hideParent){
25699             this.parentEl = Roo.get(renderTo);
25700         }
25701         if(!this.dhconfig){
25702             if(!this.template){
25703                 if(!Roo.Button.buttonTemplate){
25704                     // hideous table template
25705                     Roo.Button.buttonTemplate = new Roo.Template(
25706                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25707                         '<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>',
25708                         "</tr></tbody></table>");
25709                 }
25710                 this.template = Roo.Button.buttonTemplate;
25711             }
25712             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25713             var btnEl = btn.child("button:first");
25714             btnEl.on('focus', this.onFocus, this);
25715             btnEl.on('blur', this.onBlur, this);
25716             if(this.cls){
25717                 btn.addClass(this.cls);
25718             }
25719             if(this.icon){
25720                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25721             }
25722             if(this.iconCls){
25723                 btnEl.addClass(this.iconCls);
25724                 if(!this.cls){
25725                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25726                 }
25727             }
25728             if(this.tabIndex !== undefined){
25729                 btnEl.dom.tabIndex = this.tabIndex;
25730             }
25731             if(this.tooltip){
25732                 if(typeof this.tooltip == 'object'){
25733                     Roo.QuickTips.tips(Roo.apply({
25734                           target: btnEl.id
25735                     }, this.tooltip));
25736                 } else {
25737                     btnEl.dom[this.tooltipType] = this.tooltip;
25738                 }
25739             }
25740         }else{
25741             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25742         }
25743         this.el = btn;
25744         if(this.id){
25745             this.el.dom.id = this.el.id = this.id;
25746         }
25747         if(this.menu){
25748             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25749             this.menu.on("show", this.onMenuShow, this);
25750             this.menu.on("hide", this.onMenuHide, this);
25751         }
25752         btn.addClass("x-btn");
25753         if(Roo.isIE && !Roo.isIE7){
25754             this.autoWidth.defer(1, this);
25755         }else{
25756             this.autoWidth();
25757         }
25758         if(this.handleMouseEvents){
25759             btn.on("mouseover", this.onMouseOver, this);
25760             btn.on("mouseout", this.onMouseOut, this);
25761             btn.on("mousedown", this.onMouseDown, this);
25762         }
25763         btn.on(this.clickEvent, this.onClick, this);
25764         //btn.on("mouseup", this.onMouseUp, this);
25765         if(this.hidden){
25766             this.hide();
25767         }
25768         if(this.disabled){
25769             this.disable();
25770         }
25771         Roo.ButtonToggleMgr.register(this);
25772         if(this.pressed){
25773             this.el.addClass("x-btn-pressed");
25774         }
25775         if(this.repeat){
25776             var repeater = new Roo.util.ClickRepeater(btn,
25777                 typeof this.repeat == "object" ? this.repeat : {}
25778             );
25779             repeater.on("click", this.onClick,  this);
25780         }
25781         this.fireEvent('render', this);
25782         
25783     },
25784     /**
25785      * Returns the button's underlying element
25786      * @return {Roo.Element} The element
25787      */
25788     getEl : function(){
25789         return this.el;  
25790     },
25791     
25792     /**
25793      * Destroys this Button and removes any listeners.
25794      */
25795     destroy : function(){
25796         Roo.ButtonToggleMgr.unregister(this);
25797         this.el.removeAllListeners();
25798         this.purgeListeners();
25799         this.el.remove();
25800     },
25801
25802     // private
25803     autoWidth : function(){
25804         if(this.el){
25805             this.el.setWidth("auto");
25806             if(Roo.isIE7 && Roo.isStrict){
25807                 var ib = this.el.child('button');
25808                 if(ib && ib.getWidth() > 20){
25809                     ib.clip();
25810                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25811                 }
25812             }
25813             if(this.minWidth){
25814                 if(this.hidden){
25815                     this.el.beginMeasure();
25816                 }
25817                 if(this.el.getWidth() < this.minWidth){
25818                     this.el.setWidth(this.minWidth);
25819                 }
25820                 if(this.hidden){
25821                     this.el.endMeasure();
25822                 }
25823             }
25824         }
25825     },
25826
25827     /**
25828      * Assigns this button's click handler
25829      * @param {Function} handler The function to call when the button is clicked
25830      * @param {Object} scope (optional) Scope for the function passed in
25831      */
25832     setHandler : function(handler, scope){
25833         this.handler = handler;
25834         this.scope = scope;  
25835     },
25836     
25837     /**
25838      * Sets this button's text
25839      * @param {String} text The button text
25840      */
25841     setText : function(text){
25842         this.text = text;
25843         if(this.el){
25844             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25845         }
25846         this.autoWidth();
25847     },
25848     
25849     /**
25850      * Gets the text for this button
25851      * @return {String} The button text
25852      */
25853     getText : function(){
25854         return this.text;  
25855     },
25856     
25857     /**
25858      * Show this button
25859      */
25860     show: function(){
25861         this.hidden = false;
25862         if(this.el){
25863             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
25864         }
25865     },
25866     
25867     /**
25868      * Hide this button
25869      */
25870     hide: function(){
25871         this.hidden = true;
25872         if(this.el){
25873             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
25874         }
25875     },
25876     
25877     /**
25878      * Convenience function for boolean show/hide
25879      * @param {Boolean} visible True to show, false to hide
25880      */
25881     setVisible: function(visible){
25882         if(visible) {
25883             this.show();
25884         }else{
25885             this.hide();
25886         }
25887     },
25888     
25889     /**
25890      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
25891      * @param {Boolean} state (optional) Force a particular state
25892      */
25893     toggle : function(state){
25894         state = state === undefined ? !this.pressed : state;
25895         if(state != this.pressed){
25896             if(state){
25897                 this.el.addClass("x-btn-pressed");
25898                 this.pressed = true;
25899                 this.fireEvent("toggle", this, true);
25900             }else{
25901                 this.el.removeClass("x-btn-pressed");
25902                 this.pressed = false;
25903                 this.fireEvent("toggle", this, false);
25904             }
25905             if(this.toggleHandler){
25906                 this.toggleHandler.call(this.scope || this, this, state);
25907             }
25908         }
25909     },
25910     
25911     /**
25912      * Focus the button
25913      */
25914     focus : function(){
25915         this.el.child('button:first').focus();
25916     },
25917     
25918     /**
25919      * Disable this button
25920      */
25921     disable : function(){
25922         if(this.el){
25923             this.el.addClass("x-btn-disabled");
25924         }
25925         this.disabled = true;
25926     },
25927     
25928     /**
25929      * Enable this button
25930      */
25931     enable : function(){
25932         if(this.el){
25933             this.el.removeClass("x-btn-disabled");
25934         }
25935         this.disabled = false;
25936     },
25937
25938     /**
25939      * Convenience function for boolean enable/disable
25940      * @param {Boolean} enabled True to enable, false to disable
25941      */
25942     setDisabled : function(v){
25943         this[v !== true ? "enable" : "disable"]();
25944     },
25945
25946     // private
25947     onClick : function(e){
25948         if(e){
25949             e.preventDefault();
25950         }
25951         if(e.button != 0){
25952             return;
25953         }
25954         if(!this.disabled){
25955             if(this.enableToggle){
25956                 this.toggle();
25957             }
25958             if(this.menu && !this.menu.isVisible()){
25959                 this.menu.show(this.el, this.menuAlign);
25960             }
25961             this.fireEvent("click", this, e);
25962             if(this.handler){
25963                 this.el.removeClass("x-btn-over");
25964                 this.handler.call(this.scope || this, this, e);
25965             }
25966         }
25967     },
25968     // private
25969     onMouseOver : function(e){
25970         if(!this.disabled){
25971             this.el.addClass("x-btn-over");
25972             this.fireEvent('mouseover', this, e);
25973         }
25974     },
25975     // private
25976     onMouseOut : function(e){
25977         if(!e.within(this.el,  true)){
25978             this.el.removeClass("x-btn-over");
25979             this.fireEvent('mouseout', this, e);
25980         }
25981     },
25982     // private
25983     onFocus : function(e){
25984         if(!this.disabled){
25985             this.el.addClass("x-btn-focus");
25986         }
25987     },
25988     // private
25989     onBlur : function(e){
25990         this.el.removeClass("x-btn-focus");
25991     },
25992     // private
25993     onMouseDown : function(e){
25994         if(!this.disabled && e.button == 0){
25995             this.el.addClass("x-btn-click");
25996             Roo.get(document).on('mouseup', this.onMouseUp, this);
25997         }
25998     },
25999     // private
26000     onMouseUp : function(e){
26001         if(e.button == 0){
26002             this.el.removeClass("x-btn-click");
26003             Roo.get(document).un('mouseup', this.onMouseUp, this);
26004         }
26005     },
26006     // private
26007     onMenuShow : function(e){
26008         this.el.addClass("x-btn-menu-active");
26009     },
26010     // private
26011     onMenuHide : function(e){
26012         this.el.removeClass("x-btn-menu-active");
26013     }   
26014 });
26015
26016 // Private utility class used by Button
26017 Roo.ButtonToggleMgr = function(){
26018    var groups = {};
26019    
26020    function toggleGroup(btn, state){
26021        if(state){
26022            var g = groups[btn.toggleGroup];
26023            for(var i = 0, l = g.length; i < l; i++){
26024                if(g[i] != btn){
26025                    g[i].toggle(false);
26026                }
26027            }
26028        }
26029    }
26030    
26031    return {
26032        register : function(btn){
26033            if(!btn.toggleGroup){
26034                return;
26035            }
26036            var g = groups[btn.toggleGroup];
26037            if(!g){
26038                g = groups[btn.toggleGroup] = [];
26039            }
26040            g.push(btn);
26041            btn.on("toggle", toggleGroup);
26042        },
26043        
26044        unregister : function(btn){
26045            if(!btn.toggleGroup){
26046                return;
26047            }
26048            var g = groups[btn.toggleGroup];
26049            if(g){
26050                g.remove(btn);
26051                btn.un("toggle", toggleGroup);
26052            }
26053        }
26054    };
26055 }();/*
26056  * Based on:
26057  * Ext JS Library 1.1.1
26058  * Copyright(c) 2006-2007, Ext JS, LLC.
26059  *
26060  * Originally Released Under LGPL - original licence link has changed is not relivant.
26061  *
26062  * Fork - LGPL
26063  * <script type="text/javascript">
26064  */
26065  
26066 /**
26067  * @class Roo.SplitButton
26068  * @extends Roo.Button
26069  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26070  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26071  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26072  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26073  * @cfg {String} arrowTooltip The title attribute of the arrow
26074  * @constructor
26075  * Create a new menu button
26076  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26077  * @param {Object} config The config object
26078  */
26079 Roo.SplitButton = function(renderTo, config){
26080     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26081     /**
26082      * @event arrowclick
26083      * Fires when this button's arrow is clicked
26084      * @param {SplitButton} this
26085      * @param {EventObject} e The click event
26086      */
26087     this.addEvents({"arrowclick":true});
26088 };
26089
26090 Roo.extend(Roo.SplitButton, Roo.Button, {
26091     render : function(renderTo){
26092         // this is one sweet looking template!
26093         var tpl = new Roo.Template(
26094             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26095             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26096             '<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>',
26097             "</tbody></table></td><td>",
26098             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26099             '<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>',
26100             "</tbody></table></td></tr></table>"
26101         );
26102         var btn = tpl.append(renderTo, [this.text, this.type], true);
26103         var btnEl = btn.child("button");
26104         if(this.cls){
26105             btn.addClass(this.cls);
26106         }
26107         if(this.icon){
26108             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26109         }
26110         if(this.iconCls){
26111             btnEl.addClass(this.iconCls);
26112             if(!this.cls){
26113                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26114             }
26115         }
26116         this.el = btn;
26117         if(this.handleMouseEvents){
26118             btn.on("mouseover", this.onMouseOver, this);
26119             btn.on("mouseout", this.onMouseOut, this);
26120             btn.on("mousedown", this.onMouseDown, this);
26121             btn.on("mouseup", this.onMouseUp, this);
26122         }
26123         btn.on(this.clickEvent, this.onClick, this);
26124         if(this.tooltip){
26125             if(typeof this.tooltip == 'object'){
26126                 Roo.QuickTips.tips(Roo.apply({
26127                       target: btnEl.id
26128                 }, this.tooltip));
26129             } else {
26130                 btnEl.dom[this.tooltipType] = this.tooltip;
26131             }
26132         }
26133         if(this.arrowTooltip){
26134             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26135         }
26136         if(this.hidden){
26137             this.hide();
26138         }
26139         if(this.disabled){
26140             this.disable();
26141         }
26142         if(this.pressed){
26143             this.el.addClass("x-btn-pressed");
26144         }
26145         if(Roo.isIE && !Roo.isIE7){
26146             this.autoWidth.defer(1, this);
26147         }else{
26148             this.autoWidth();
26149         }
26150         if(this.menu){
26151             this.menu.on("show", this.onMenuShow, this);
26152             this.menu.on("hide", this.onMenuHide, this);
26153         }
26154         this.fireEvent('render', this);
26155     },
26156
26157     // private
26158     autoWidth : function(){
26159         if(this.el){
26160             var tbl = this.el.child("table:first");
26161             var tbl2 = this.el.child("table:last");
26162             this.el.setWidth("auto");
26163             tbl.setWidth("auto");
26164             if(Roo.isIE7 && Roo.isStrict){
26165                 var ib = this.el.child('button:first');
26166                 if(ib && ib.getWidth() > 20){
26167                     ib.clip();
26168                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26169                 }
26170             }
26171             if(this.minWidth){
26172                 if(this.hidden){
26173                     this.el.beginMeasure();
26174                 }
26175                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26176                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26177                 }
26178                 if(this.hidden){
26179                     this.el.endMeasure();
26180                 }
26181             }
26182             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26183         } 
26184     },
26185     /**
26186      * Sets this button's click handler
26187      * @param {Function} handler The function to call when the button is clicked
26188      * @param {Object} scope (optional) Scope for the function passed above
26189      */
26190     setHandler : function(handler, scope){
26191         this.handler = handler;
26192         this.scope = scope;  
26193     },
26194     
26195     /**
26196      * Sets this button's arrow click handler
26197      * @param {Function} handler The function to call when the arrow is clicked
26198      * @param {Object} scope (optional) Scope for the function passed above
26199      */
26200     setArrowHandler : function(handler, scope){
26201         this.arrowHandler = handler;
26202         this.scope = scope;  
26203     },
26204     
26205     /**
26206      * Focus the button
26207      */
26208     focus : function(){
26209         if(this.el){
26210             this.el.child("button:first").focus();
26211         }
26212     },
26213
26214     // private
26215     onClick : function(e){
26216         e.preventDefault();
26217         if(!this.disabled){
26218             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26219                 if(this.menu && !this.menu.isVisible()){
26220                     this.menu.show(this.el, this.menuAlign);
26221                 }
26222                 this.fireEvent("arrowclick", this, e);
26223                 if(this.arrowHandler){
26224                     this.arrowHandler.call(this.scope || this, this, e);
26225                 }
26226             }else{
26227                 this.fireEvent("click", this, e);
26228                 if(this.handler){
26229                     this.handler.call(this.scope || this, this, e);
26230                 }
26231             }
26232         }
26233     },
26234     // private
26235     onMouseDown : function(e){
26236         if(!this.disabled){
26237             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26238         }
26239     },
26240     // private
26241     onMouseUp : function(e){
26242         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26243     }   
26244 });
26245
26246
26247 // backwards compat
26248 Roo.MenuButton = Roo.SplitButton;/*
26249  * Based on:
26250  * Ext JS Library 1.1.1
26251  * Copyright(c) 2006-2007, Ext JS, LLC.
26252  *
26253  * Originally Released Under LGPL - original licence link has changed is not relivant.
26254  *
26255  * Fork - LGPL
26256  * <script type="text/javascript">
26257  */
26258
26259 /**
26260  * @class Roo.Toolbar
26261  * Basic Toolbar class.
26262  * @constructor
26263  * Creates a new Toolbar
26264  * @param {Object} config The config object
26265  */ 
26266 Roo.Toolbar = function(container, buttons, config)
26267 {
26268     /// old consturctor format still supported..
26269     if(container instanceof Array){ // omit the container for later rendering
26270         buttons = container;
26271         config = buttons;
26272         container = null;
26273     }
26274     if (typeof(container) == 'object' && container.xtype) {
26275         config = container;
26276         container = config.container;
26277         buttons = config.buttons; // not really - use items!!
26278     }
26279     var xitems = [];
26280     if (config && config.items) {
26281         xitems = config.items;
26282         delete config.items;
26283     }
26284     Roo.apply(this, config);
26285     this.buttons = buttons;
26286     
26287     if(container){
26288         this.render(container);
26289     }
26290     Roo.each(xitems, function(b) {
26291         this.add(b);
26292     }, this);
26293     
26294 };
26295
26296 Roo.Toolbar.prototype = {
26297     /**
26298      * @cfg {Roo.data.Store} items
26299      * array of button configs or elements to add
26300      */
26301     
26302     /**
26303      * @cfg {String/HTMLElement/Element} container
26304      * The id or element that will contain the toolbar
26305      */
26306     // private
26307     render : function(ct){
26308         this.el = Roo.get(ct);
26309         if(this.cls){
26310             this.el.addClass(this.cls);
26311         }
26312         // using a table allows for vertical alignment
26313         // 100% width is needed by Safari...
26314         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26315         this.tr = this.el.child("tr", true);
26316         var autoId = 0;
26317         this.items = new Roo.util.MixedCollection(false, function(o){
26318             return o.id || ("item" + (++autoId));
26319         });
26320         if(this.buttons){
26321             this.add.apply(this, this.buttons);
26322             delete this.buttons;
26323         }
26324     },
26325
26326     /**
26327      * Adds element(s) to the toolbar -- this function takes a variable number of 
26328      * arguments of mixed type and adds them to the toolbar.
26329      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26330      * <ul>
26331      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26332      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26333      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26334      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26335      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26336      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26337      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26338      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26339      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26340      * </ul>
26341      * @param {Mixed} arg2
26342      * @param {Mixed} etc.
26343      */
26344     add : function(){
26345         var a = arguments, l = a.length;
26346         for(var i = 0; i < l; i++){
26347             this._add(a[i]);
26348         }
26349     },
26350     // private..
26351     _add : function(el) {
26352         
26353         if (el.xtype) {
26354             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26355         }
26356         
26357         if (el.applyTo){ // some kind of form field
26358             return this.addField(el);
26359         } 
26360         if (el.render){ // some kind of Toolbar.Item
26361             return this.addItem(el);
26362         }
26363         if (typeof el == "string"){ // string
26364             if(el == "separator" || el == "-"){
26365                 return this.addSeparator();
26366             }
26367             if (el == " "){
26368                 return this.addSpacer();
26369             }
26370             if(el == "->"){
26371                 return this.addFill();
26372             }
26373             return this.addText(el);
26374             
26375         }
26376         if(el.tagName){ // element
26377             return this.addElement(el);
26378         }
26379         if(typeof el == "object"){ // must be button config?
26380             return this.addButton(el);
26381         }
26382         // and now what?!?!
26383         return false;
26384         
26385     },
26386     
26387     /**
26388      * Add an Xtype element
26389      * @param {Object} xtype Xtype Object
26390      * @return {Object} created Object
26391      */
26392     addxtype : function(e){
26393         return this.add(e);  
26394     },
26395     
26396     /**
26397      * Returns the Element for this toolbar.
26398      * @return {Roo.Element}
26399      */
26400     getEl : function(){
26401         return this.el;  
26402     },
26403     
26404     /**
26405      * Adds a separator
26406      * @return {Roo.Toolbar.Item} The separator item
26407      */
26408     addSeparator : function(){
26409         return this.addItem(new Roo.Toolbar.Separator());
26410     },
26411
26412     /**
26413      * Adds a spacer element
26414      * @return {Roo.Toolbar.Spacer} The spacer item
26415      */
26416     addSpacer : function(){
26417         return this.addItem(new Roo.Toolbar.Spacer());
26418     },
26419
26420     /**
26421      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26422      * @return {Roo.Toolbar.Fill} The fill item
26423      */
26424     addFill : function(){
26425         return this.addItem(new Roo.Toolbar.Fill());
26426     },
26427
26428     /**
26429      * Adds any standard HTML element to the toolbar
26430      * @param {String/HTMLElement/Element} el The element or id of the element to add
26431      * @return {Roo.Toolbar.Item} The element's item
26432      */
26433     addElement : function(el){
26434         return this.addItem(new Roo.Toolbar.Item(el));
26435     },
26436     /**
26437      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26438      * @type Roo.util.MixedCollection  
26439      */
26440     items : false,
26441      
26442     /**
26443      * Adds any Toolbar.Item or subclass
26444      * @param {Roo.Toolbar.Item} item
26445      * @return {Roo.Toolbar.Item} The item
26446      */
26447     addItem : function(item){
26448         var td = this.nextBlock();
26449         item.render(td);
26450         this.items.add(item);
26451         return item;
26452     },
26453     
26454     /**
26455      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26456      * @param {Object/Array} config A button config or array of configs
26457      * @return {Roo.Toolbar.Button/Array}
26458      */
26459     addButton : function(config){
26460         if(config instanceof Array){
26461             var buttons = [];
26462             for(var i = 0, len = config.length; i < len; i++) {
26463                 buttons.push(this.addButton(config[i]));
26464             }
26465             return buttons;
26466         }
26467         var b = config;
26468         if(!(config instanceof Roo.Toolbar.Button)){
26469             b = config.split ?
26470                 new Roo.Toolbar.SplitButton(config) :
26471                 new Roo.Toolbar.Button(config);
26472         }
26473         var td = this.nextBlock();
26474         b.render(td);
26475         this.items.add(b);
26476         return b;
26477     },
26478     
26479     /**
26480      * Adds text to the toolbar
26481      * @param {String} text The text to add
26482      * @return {Roo.Toolbar.Item} The element's item
26483      */
26484     addText : function(text){
26485         return this.addItem(new Roo.Toolbar.TextItem(text));
26486     },
26487     
26488     /**
26489      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26490      * @param {Number} index The index where the item is to be inserted
26491      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26492      * @return {Roo.Toolbar.Button/Item}
26493      */
26494     insertButton : function(index, item){
26495         if(item instanceof Array){
26496             var buttons = [];
26497             for(var i = 0, len = item.length; i < len; i++) {
26498                buttons.push(this.insertButton(index + i, item[i]));
26499             }
26500             return buttons;
26501         }
26502         if (!(item instanceof Roo.Toolbar.Button)){
26503            item = new Roo.Toolbar.Button(item);
26504         }
26505         var td = document.createElement("td");
26506         this.tr.insertBefore(td, this.tr.childNodes[index]);
26507         item.render(td);
26508         this.items.insert(index, item);
26509         return item;
26510     },
26511     
26512     /**
26513      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26514      * @param {Object} config
26515      * @return {Roo.Toolbar.Item} The element's item
26516      */
26517     addDom : function(config, returnEl){
26518         var td = this.nextBlock();
26519         Roo.DomHelper.overwrite(td, config);
26520         var ti = new Roo.Toolbar.Item(td.firstChild);
26521         ti.render(td);
26522         this.items.add(ti);
26523         return ti;
26524     },
26525
26526     /**
26527      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26528      * @type Roo.util.MixedCollection  
26529      */
26530     fields : false,
26531     
26532     /**
26533      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26534      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26535      * @param {Roo.form.Field} field
26536      * @return {Roo.ToolbarItem}
26537      */
26538      
26539       
26540     addField : function(field) {
26541         if (!this.fields) {
26542             var autoId = 0;
26543             this.fields = new Roo.util.MixedCollection(false, function(o){
26544                 return o.id || ("item" + (++autoId));
26545             });
26546
26547         }
26548         
26549         var td = this.nextBlock();
26550         field.render(td);
26551         var ti = new Roo.Toolbar.Item(td.firstChild);
26552         ti.render(td);
26553         this.items.add(ti);
26554         this.fields.add(field);
26555         return ti;
26556     },
26557     /**
26558      * Hide the toolbar
26559      * @method hide
26560      */
26561      
26562       
26563     hide : function()
26564     {
26565         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26566         this.el.child('div').hide();
26567     },
26568     /**
26569      * Show the toolbar
26570      * @method show
26571      */
26572     show : function()
26573     {
26574         this.el.child('div').show();
26575     },
26576       
26577     // private
26578     nextBlock : function(){
26579         var td = document.createElement("td");
26580         this.tr.appendChild(td);
26581         return td;
26582     },
26583
26584     // private
26585     destroy : function(){
26586         if(this.items){ // rendered?
26587             Roo.destroy.apply(Roo, this.items.items);
26588         }
26589         if(this.fields){ // rendered?
26590             Roo.destroy.apply(Roo, this.fields.items);
26591         }
26592         Roo.Element.uncache(this.el, this.tr);
26593     }
26594 };
26595
26596 /**
26597  * @class Roo.Toolbar.Item
26598  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26599  * @constructor
26600  * Creates a new Item
26601  * @param {HTMLElement} el 
26602  */
26603 Roo.Toolbar.Item = function(el){
26604     this.el = Roo.getDom(el);
26605     this.id = Roo.id(this.el);
26606     this.hidden = false;
26607 };
26608
26609 Roo.Toolbar.Item.prototype = {
26610     
26611     /**
26612      * Get this item's HTML Element
26613      * @return {HTMLElement}
26614      */
26615     getEl : function(){
26616        return this.el;  
26617     },
26618
26619     // private
26620     render : function(td){
26621         this.td = td;
26622         td.appendChild(this.el);
26623     },
26624     
26625     /**
26626      * Removes and destroys this item.
26627      */
26628     destroy : function(){
26629         this.td.parentNode.removeChild(this.td);
26630     },
26631     
26632     /**
26633      * Shows this item.
26634      */
26635     show: function(){
26636         this.hidden = false;
26637         this.td.style.display = "";
26638     },
26639     
26640     /**
26641      * Hides this item.
26642      */
26643     hide: function(){
26644         this.hidden = true;
26645         this.td.style.display = "none";
26646     },
26647     
26648     /**
26649      * Convenience function for boolean show/hide.
26650      * @param {Boolean} visible true to show/false to hide
26651      */
26652     setVisible: function(visible){
26653         if(visible) {
26654             this.show();
26655         }else{
26656             this.hide();
26657         }
26658     },
26659     
26660     /**
26661      * Try to focus this item.
26662      */
26663     focus : function(){
26664         Roo.fly(this.el).focus();
26665     },
26666     
26667     /**
26668      * Disables this item.
26669      */
26670     disable : function(){
26671         Roo.fly(this.td).addClass("x-item-disabled");
26672         this.disabled = true;
26673         this.el.disabled = true;
26674     },
26675     
26676     /**
26677      * Enables this item.
26678      */
26679     enable : function(){
26680         Roo.fly(this.td).removeClass("x-item-disabled");
26681         this.disabled = false;
26682         this.el.disabled = false;
26683     }
26684 };
26685
26686
26687 /**
26688  * @class Roo.Toolbar.Separator
26689  * @extends Roo.Toolbar.Item
26690  * A simple toolbar separator class
26691  * @constructor
26692  * Creates a new Separator
26693  */
26694 Roo.Toolbar.Separator = function(){
26695     var s = document.createElement("span");
26696     s.className = "ytb-sep";
26697     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26698 };
26699 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26700     enable:Roo.emptyFn,
26701     disable:Roo.emptyFn,
26702     focus:Roo.emptyFn
26703 });
26704
26705 /**
26706  * @class Roo.Toolbar.Spacer
26707  * @extends Roo.Toolbar.Item
26708  * A simple element that adds extra horizontal space to a toolbar.
26709  * @constructor
26710  * Creates a new Spacer
26711  */
26712 Roo.Toolbar.Spacer = function(){
26713     var s = document.createElement("div");
26714     s.className = "ytb-spacer";
26715     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26716 };
26717 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26718     enable:Roo.emptyFn,
26719     disable:Roo.emptyFn,
26720     focus:Roo.emptyFn
26721 });
26722
26723 /**
26724  * @class Roo.Toolbar.Fill
26725  * @extends Roo.Toolbar.Spacer
26726  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26727  * @constructor
26728  * Creates a new Spacer
26729  */
26730 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26731     // private
26732     render : function(td){
26733         td.style.width = '100%';
26734         Roo.Toolbar.Fill.superclass.render.call(this, td);
26735     }
26736 });
26737
26738 /**
26739  * @class Roo.Toolbar.TextItem
26740  * @extends Roo.Toolbar.Item
26741  * A simple class that renders text directly into a toolbar.
26742  * @constructor
26743  * Creates a new TextItem
26744  * @param {String} text
26745  */
26746 Roo.Toolbar.TextItem = function(text){
26747     if (typeof(text) == 'object') {
26748         text = text.text;
26749     }
26750     var s = document.createElement("span");
26751     s.className = "ytb-text";
26752     s.innerHTML = text;
26753     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26754 };
26755 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26756     enable:Roo.emptyFn,
26757     disable:Roo.emptyFn,
26758     focus:Roo.emptyFn
26759 });
26760
26761 /**
26762  * @class Roo.Toolbar.Button
26763  * @extends Roo.Button
26764  * A button that renders into a toolbar.
26765  * @constructor
26766  * Creates a new Button
26767  * @param {Object} config A standard {@link Roo.Button} config object
26768  */
26769 Roo.Toolbar.Button = function(config){
26770     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26771 };
26772 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26773     render : function(td){
26774         this.td = td;
26775         Roo.Toolbar.Button.superclass.render.call(this, td);
26776     },
26777     
26778     /**
26779      * Removes and destroys this button
26780      */
26781     destroy : function(){
26782         Roo.Toolbar.Button.superclass.destroy.call(this);
26783         this.td.parentNode.removeChild(this.td);
26784     },
26785     
26786     /**
26787      * Shows this button
26788      */
26789     show: function(){
26790         this.hidden = false;
26791         this.td.style.display = "";
26792     },
26793     
26794     /**
26795      * Hides this button
26796      */
26797     hide: function(){
26798         this.hidden = true;
26799         this.td.style.display = "none";
26800     },
26801
26802     /**
26803      * Disables this item
26804      */
26805     disable : function(){
26806         Roo.fly(this.td).addClass("x-item-disabled");
26807         this.disabled = true;
26808     },
26809
26810     /**
26811      * Enables this item
26812      */
26813     enable : function(){
26814         Roo.fly(this.td).removeClass("x-item-disabled");
26815         this.disabled = false;
26816     }
26817 });
26818 // backwards compat
26819 Roo.ToolbarButton = Roo.Toolbar.Button;
26820
26821 /**
26822  * @class Roo.Toolbar.SplitButton
26823  * @extends Roo.SplitButton
26824  * A menu button that renders into a toolbar.
26825  * @constructor
26826  * Creates a new SplitButton
26827  * @param {Object} config A standard {@link Roo.SplitButton} config object
26828  */
26829 Roo.Toolbar.SplitButton = function(config){
26830     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26831 };
26832 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26833     render : function(td){
26834         this.td = td;
26835         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26836     },
26837     
26838     /**
26839      * Removes and destroys this button
26840      */
26841     destroy : function(){
26842         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26843         this.td.parentNode.removeChild(this.td);
26844     },
26845     
26846     /**
26847      * Shows this button
26848      */
26849     show: function(){
26850         this.hidden = false;
26851         this.td.style.display = "";
26852     },
26853     
26854     /**
26855      * Hides this button
26856      */
26857     hide: function(){
26858         this.hidden = true;
26859         this.td.style.display = "none";
26860     }
26861 });
26862
26863 // backwards compat
26864 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
26865  * Based on:
26866  * Ext JS Library 1.1.1
26867  * Copyright(c) 2006-2007, Ext JS, LLC.
26868  *
26869  * Originally Released Under LGPL - original licence link has changed is not relivant.
26870  *
26871  * Fork - LGPL
26872  * <script type="text/javascript">
26873  */
26874  
26875 /**
26876  * @class Roo.PagingToolbar
26877  * @extends Roo.Toolbar
26878  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26879  * @constructor
26880  * Create a new PagingToolbar
26881  * @param {Object} config The config object
26882  */
26883 Roo.PagingToolbar = function(el, ds, config)
26884 {
26885     // old args format still supported... - xtype is prefered..
26886     if (typeof(el) == 'object' && el.xtype) {
26887         // created from xtype...
26888         config = el;
26889         ds = el.dataSource;
26890         el = config.container;
26891     }
26892     
26893     
26894     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
26895     this.ds = ds;
26896     this.cursor = 0;
26897     this.renderButtons(this.el);
26898     this.bind(ds);
26899 };
26900
26901 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
26902     /**
26903      * @cfg {Roo.data.Store} dataSource
26904      * The underlying data store providing the paged data
26905      */
26906     /**
26907      * @cfg {String/HTMLElement/Element} container
26908      * container The id or element that will contain the toolbar
26909      */
26910     /**
26911      * @cfg {Boolean} displayInfo
26912      * True to display the displayMsg (defaults to false)
26913      */
26914     /**
26915      * @cfg {Number} pageSize
26916      * The number of records to display per page (defaults to 20)
26917      */
26918     pageSize: 20,
26919     /**
26920      * @cfg {String} displayMsg
26921      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26922      */
26923     displayMsg : 'Displaying {0} - {1} of {2}',
26924     /**
26925      * @cfg {String} emptyMsg
26926      * The message to display when no records are found (defaults to "No data to display")
26927      */
26928     emptyMsg : 'No data to display',
26929     /**
26930      * Customizable piece of the default paging text (defaults to "Page")
26931      * @type String
26932      */
26933     beforePageText : "Page",
26934     /**
26935      * Customizable piece of the default paging text (defaults to "of %0")
26936      * @type String
26937      */
26938     afterPageText : "of {0}",
26939     /**
26940      * Customizable piece of the default paging text (defaults to "First Page")
26941      * @type String
26942      */
26943     firstText : "First Page",
26944     /**
26945      * Customizable piece of the default paging text (defaults to "Previous Page")
26946      * @type String
26947      */
26948     prevText : "Previous Page",
26949     /**
26950      * Customizable piece of the default paging text (defaults to "Next Page")
26951      * @type String
26952      */
26953     nextText : "Next Page",
26954     /**
26955      * Customizable piece of the default paging text (defaults to "Last Page")
26956      * @type String
26957      */
26958     lastText : "Last Page",
26959     /**
26960      * Customizable piece of the default paging text (defaults to "Refresh")
26961      * @type String
26962      */
26963     refreshText : "Refresh",
26964
26965     // private
26966     renderButtons : function(el){
26967         Roo.PagingToolbar.superclass.render.call(this, el);
26968         this.first = this.addButton({
26969             tooltip: this.firstText,
26970             cls: "x-btn-icon x-grid-page-first",
26971             disabled: true,
26972             handler: this.onClick.createDelegate(this, ["first"])
26973         });
26974         this.prev = this.addButton({
26975             tooltip: this.prevText,
26976             cls: "x-btn-icon x-grid-page-prev",
26977             disabled: true,
26978             handler: this.onClick.createDelegate(this, ["prev"])
26979         });
26980         this.addSeparator();
26981         this.add(this.beforePageText);
26982         this.field = Roo.get(this.addDom({
26983            tag: "input",
26984            type: "text",
26985            size: "3",
26986            value: "1",
26987            cls: "x-grid-page-number"
26988         }).el);
26989         this.field.on("keydown", this.onPagingKeydown, this);
26990         this.field.on("focus", function(){this.dom.select();});
26991         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
26992         this.field.setHeight(18);
26993         this.addSeparator();
26994         this.next = this.addButton({
26995             tooltip: this.nextText,
26996             cls: "x-btn-icon x-grid-page-next",
26997             disabled: true,
26998             handler: this.onClick.createDelegate(this, ["next"])
26999         });
27000         this.last = this.addButton({
27001             tooltip: this.lastText,
27002             cls: "x-btn-icon x-grid-page-last",
27003             disabled: true,
27004             handler: this.onClick.createDelegate(this, ["last"])
27005         });
27006         this.addSeparator();
27007         this.loading = this.addButton({
27008             tooltip: this.refreshText,
27009             cls: "x-btn-icon x-grid-loading",
27010             handler: this.onClick.createDelegate(this, ["refresh"])
27011         });
27012
27013         if(this.displayInfo){
27014             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27015         }
27016     },
27017
27018     // private
27019     updateInfo : function(){
27020         if(this.displayEl){
27021             var count = this.ds.getCount();
27022             var msg = count == 0 ?
27023                 this.emptyMsg :
27024                 String.format(
27025                     this.displayMsg,
27026                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27027                 );
27028             this.displayEl.update(msg);
27029         }
27030     },
27031
27032     // private
27033     onLoad : function(ds, r, o){
27034        this.cursor = o.params ? o.params.start : 0;
27035        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27036
27037        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27038        this.field.dom.value = ap;
27039        this.first.setDisabled(ap == 1);
27040        this.prev.setDisabled(ap == 1);
27041        this.next.setDisabled(ap == ps);
27042        this.last.setDisabled(ap == ps);
27043        this.loading.enable();
27044        this.updateInfo();
27045     },
27046
27047     // private
27048     getPageData : function(){
27049         var total = this.ds.getTotalCount();
27050         return {
27051             total : total,
27052             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27053             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27054         };
27055     },
27056
27057     // private
27058     onLoadError : function(){
27059         this.loading.enable();
27060     },
27061
27062     // private
27063     onPagingKeydown : function(e){
27064         var k = e.getKey();
27065         var d = this.getPageData();
27066         if(k == e.RETURN){
27067             var v = this.field.dom.value, pageNum;
27068             if(!v || isNaN(pageNum = parseInt(v, 10))){
27069                 this.field.dom.value = d.activePage;
27070                 return;
27071             }
27072             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27073             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27074             e.stopEvent();
27075         }
27076         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))
27077         {
27078           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27079           this.field.dom.value = pageNum;
27080           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27081           e.stopEvent();
27082         }
27083         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27084         {
27085           var v = this.field.dom.value, pageNum; 
27086           var increment = (e.shiftKey) ? 10 : 1;
27087           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27088             increment *= -1;
27089           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27090             this.field.dom.value = d.activePage;
27091             return;
27092           }
27093           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27094           {
27095             this.field.dom.value = parseInt(v, 10) + increment;
27096             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27097             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27098           }
27099           e.stopEvent();
27100         }
27101     },
27102
27103     // private
27104     beforeLoad : function(){
27105         if(this.loading){
27106             this.loading.disable();
27107         }
27108     },
27109
27110     // private
27111     onClick : function(which){
27112         var ds = this.ds;
27113         switch(which){
27114             case "first":
27115                 ds.load({params:{start: 0, limit: this.pageSize}});
27116             break;
27117             case "prev":
27118                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27119             break;
27120             case "next":
27121                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27122             break;
27123             case "last":
27124                 var total = ds.getTotalCount();
27125                 var extra = total % this.pageSize;
27126                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27127                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27128             break;
27129             case "refresh":
27130                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27131             break;
27132         }
27133     },
27134
27135     /**
27136      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27137      * @param {Roo.data.Store} store The data store to unbind
27138      */
27139     unbind : function(ds){
27140         ds.un("beforeload", this.beforeLoad, this);
27141         ds.un("load", this.onLoad, this);
27142         ds.un("loadexception", this.onLoadError, this);
27143         ds.un("remove", this.updateInfo, this);
27144         ds.un("add", this.updateInfo, this);
27145         this.ds = undefined;
27146     },
27147
27148     /**
27149      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27150      * @param {Roo.data.Store} store The data store to bind
27151      */
27152     bind : function(ds){
27153         ds.on("beforeload", this.beforeLoad, this);
27154         ds.on("load", this.onLoad, this);
27155         ds.on("loadexception", this.onLoadError, this);
27156         ds.on("remove", this.updateInfo, this);
27157         ds.on("add", this.updateInfo, this);
27158         this.ds = ds;
27159     }
27160 });/*
27161  * Based on:
27162  * Ext JS Library 1.1.1
27163  * Copyright(c) 2006-2007, Ext JS, LLC.
27164  *
27165  * Originally Released Under LGPL - original licence link has changed is not relivant.
27166  *
27167  * Fork - LGPL
27168  * <script type="text/javascript">
27169  */
27170
27171 /**
27172  * @class Roo.Resizable
27173  * @extends Roo.util.Observable
27174  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27175  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27176  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27177  * the element will be wrapped for you automatically.</p>
27178  * <p>Here is the list of valid resize handles:</p>
27179  * <pre>
27180 Value   Description
27181 ------  -------------------
27182  'n'     north
27183  's'     south
27184  'e'     east
27185  'w'     west
27186  'nw'    northwest
27187  'sw'    southwest
27188  'se'    southeast
27189  'ne'    northeast
27190  'all'   all
27191 </pre>
27192  * <p>Here's an example showing the creation of a typical Resizable:</p>
27193  * <pre><code>
27194 var resizer = new Roo.Resizable("element-id", {
27195     handles: 'all',
27196     minWidth: 200,
27197     minHeight: 100,
27198     maxWidth: 500,
27199     maxHeight: 400,
27200     pinned: true
27201 });
27202 resizer.on("resize", myHandler);
27203 </code></pre>
27204  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27205  * resizer.east.setDisplayed(false);</p>
27206  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27207  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27208  * resize operation's new size (defaults to [0, 0])
27209  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27210  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27211  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27212  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27213  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27214  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27215  * @cfg {Number} width The width of the element in pixels (defaults to null)
27216  * @cfg {Number} height The height of the element in pixels (defaults to null)
27217  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27218  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27219  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27220  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27221  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27222  * in favor of the handles config option (defaults to false)
27223  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27224  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27225  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27226  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27227  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27228  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27229  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27230  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27231  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27232  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27233  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27234  * @constructor
27235  * Create a new resizable component
27236  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27237  * @param {Object} config configuration options
27238   */
27239 Roo.Resizable = function(el, config){
27240     this.el = Roo.get(el);
27241
27242     if(config && config.wrap){
27243         config.resizeChild = this.el;
27244         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27245         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27246         this.el.setStyle("overflow", "hidden");
27247         this.el.setPositioning(config.resizeChild.getPositioning());
27248         config.resizeChild.clearPositioning();
27249         if(!config.width || !config.height){
27250             var csize = config.resizeChild.getSize();
27251             this.el.setSize(csize.width, csize.height);
27252         }
27253         if(config.pinned && !config.adjustments){
27254             config.adjustments = "auto";
27255         }
27256     }
27257
27258     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27259     this.proxy.unselectable();
27260     this.proxy.enableDisplayMode('block');
27261
27262     Roo.apply(this, config);
27263
27264     if(this.pinned){
27265         this.disableTrackOver = true;
27266         this.el.addClass("x-resizable-pinned");
27267     }
27268     // if the element isn't positioned, make it relative
27269     var position = this.el.getStyle("position");
27270     if(position != "absolute" && position != "fixed"){
27271         this.el.setStyle("position", "relative");
27272     }
27273     if(!this.handles){ // no handles passed, must be legacy style
27274         this.handles = 's,e,se';
27275         if(this.multiDirectional){
27276             this.handles += ',n,w';
27277         }
27278     }
27279     if(this.handles == "all"){
27280         this.handles = "n s e w ne nw se sw";
27281     }
27282     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27283     var ps = Roo.Resizable.positions;
27284     for(var i = 0, len = hs.length; i < len; i++){
27285         if(hs[i] && ps[hs[i]]){
27286             var pos = ps[hs[i]];
27287             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27288         }
27289     }
27290     // legacy
27291     this.corner = this.southeast;
27292
27293     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27294         this.updateBox = true;
27295     }
27296
27297     this.activeHandle = null;
27298
27299     if(this.resizeChild){
27300         if(typeof this.resizeChild == "boolean"){
27301             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27302         }else{
27303             this.resizeChild = Roo.get(this.resizeChild, true);
27304         }
27305     }
27306
27307     if(this.adjustments == "auto"){
27308         var rc = this.resizeChild;
27309         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27310         if(rc && (hw || hn)){
27311             rc.position("relative");
27312             rc.setLeft(hw ? hw.el.getWidth() : 0);
27313             rc.setTop(hn ? hn.el.getHeight() : 0);
27314         }
27315         this.adjustments = [
27316             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27317             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27318         ];
27319     }
27320
27321     if(this.draggable){
27322         this.dd = this.dynamic ?
27323             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27324         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27325     }
27326
27327     // public events
27328     this.addEvents({
27329         /**
27330          * @event beforeresize
27331          * Fired before resize is allowed. Set enabled to false to cancel resize.
27332          * @param {Roo.Resizable} this
27333          * @param {Roo.EventObject} e The mousedown event
27334          */
27335         "beforeresize" : true,
27336         /**
27337          * @event resize
27338          * Fired after a resize.
27339          * @param {Roo.Resizable} this
27340          * @param {Number} width The new width
27341          * @param {Number} height The new height
27342          * @param {Roo.EventObject} e The mouseup event
27343          */
27344         "resize" : true
27345     });
27346
27347     if(this.width !== null && this.height !== null){
27348         this.resizeTo(this.width, this.height);
27349     }else{
27350         this.updateChildSize();
27351     }
27352     if(Roo.isIE){
27353         this.el.dom.style.zoom = 1;
27354     }
27355     Roo.Resizable.superclass.constructor.call(this);
27356 };
27357
27358 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27359         resizeChild : false,
27360         adjustments : [0, 0],
27361         minWidth : 5,
27362         minHeight : 5,
27363         maxWidth : 10000,
27364         maxHeight : 10000,
27365         enabled : true,
27366         animate : false,
27367         duration : .35,
27368         dynamic : false,
27369         handles : false,
27370         multiDirectional : false,
27371         disableTrackOver : false,
27372         easing : 'easeOutStrong',
27373         widthIncrement : 0,
27374         heightIncrement : 0,
27375         pinned : false,
27376         width : null,
27377         height : null,
27378         preserveRatio : false,
27379         transparent: false,
27380         minX: 0,
27381         minY: 0,
27382         draggable: false,
27383
27384         /**
27385          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27386          */
27387         constrainTo: undefined,
27388         /**
27389          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27390          */
27391         resizeRegion: undefined,
27392
27393
27394     /**
27395      * Perform a manual resize
27396      * @param {Number} width
27397      * @param {Number} height
27398      */
27399     resizeTo : function(width, height){
27400         this.el.setSize(width, height);
27401         this.updateChildSize();
27402         this.fireEvent("resize", this, width, height, null);
27403     },
27404
27405     // private
27406     startSizing : function(e, handle){
27407         this.fireEvent("beforeresize", this, e);
27408         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27409
27410             if(!this.overlay){
27411                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27412                 this.overlay.unselectable();
27413                 this.overlay.enableDisplayMode("block");
27414                 this.overlay.on("mousemove", this.onMouseMove, this);
27415                 this.overlay.on("mouseup", this.onMouseUp, this);
27416             }
27417             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27418
27419             this.resizing = true;
27420             this.startBox = this.el.getBox();
27421             this.startPoint = e.getXY();
27422             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27423                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27424
27425             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27426             this.overlay.show();
27427
27428             if(this.constrainTo) {
27429                 var ct = Roo.get(this.constrainTo);
27430                 this.resizeRegion = ct.getRegion().adjust(
27431                     ct.getFrameWidth('t'),
27432                     ct.getFrameWidth('l'),
27433                     -ct.getFrameWidth('b'),
27434                     -ct.getFrameWidth('r')
27435                 );
27436             }
27437
27438             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27439             this.proxy.show();
27440             this.proxy.setBox(this.startBox);
27441             if(!this.dynamic){
27442                 this.proxy.setStyle('visibility', 'visible');
27443             }
27444         }
27445     },
27446
27447     // private
27448     onMouseDown : function(handle, e){
27449         if(this.enabled){
27450             e.stopEvent();
27451             this.activeHandle = handle;
27452             this.startSizing(e, handle);
27453         }
27454     },
27455
27456     // private
27457     onMouseUp : function(e){
27458         var size = this.resizeElement();
27459         this.resizing = false;
27460         this.handleOut();
27461         this.overlay.hide();
27462         this.proxy.hide();
27463         this.fireEvent("resize", this, size.width, size.height, e);
27464     },
27465
27466     // private
27467     updateChildSize : function(){
27468         if(this.resizeChild){
27469             var el = this.el;
27470             var child = this.resizeChild;
27471             var adj = this.adjustments;
27472             if(el.dom.offsetWidth){
27473                 var b = el.getSize(true);
27474                 child.setSize(b.width+adj[0], b.height+adj[1]);
27475             }
27476             // Second call here for IE
27477             // The first call enables instant resizing and
27478             // the second call corrects scroll bars if they
27479             // exist
27480             if(Roo.isIE){
27481                 setTimeout(function(){
27482                     if(el.dom.offsetWidth){
27483                         var b = el.getSize(true);
27484                         child.setSize(b.width+adj[0], b.height+adj[1]);
27485                     }
27486                 }, 10);
27487             }
27488         }
27489     },
27490
27491     // private
27492     snap : function(value, inc, min){
27493         if(!inc || !value) return value;
27494         var newValue = value;
27495         var m = value % inc;
27496         if(m > 0){
27497             if(m > (inc/2)){
27498                 newValue = value + (inc-m);
27499             }else{
27500                 newValue = value - m;
27501             }
27502         }
27503         return Math.max(min, newValue);
27504     },
27505
27506     // private
27507     resizeElement : function(){
27508         var box = this.proxy.getBox();
27509         if(this.updateBox){
27510             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27511         }else{
27512             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27513         }
27514         this.updateChildSize();
27515         if(!this.dynamic){
27516             this.proxy.hide();
27517         }
27518         return box;
27519     },
27520
27521     // private
27522     constrain : function(v, diff, m, mx){
27523         if(v - diff < m){
27524             diff = v - m;
27525         }else if(v - diff > mx){
27526             diff = mx - v;
27527         }
27528         return diff;
27529     },
27530
27531     // private
27532     onMouseMove : function(e){
27533         if(this.enabled){
27534             try{// try catch so if something goes wrong the user doesn't get hung
27535
27536             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27537                 return;
27538             }
27539
27540             //var curXY = this.startPoint;
27541             var curSize = this.curSize || this.startBox;
27542             var x = this.startBox.x, y = this.startBox.y;
27543             var ox = x, oy = y;
27544             var w = curSize.width, h = curSize.height;
27545             var ow = w, oh = h;
27546             var mw = this.minWidth, mh = this.minHeight;
27547             var mxw = this.maxWidth, mxh = this.maxHeight;
27548             var wi = this.widthIncrement;
27549             var hi = this.heightIncrement;
27550
27551             var eventXY = e.getXY();
27552             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27553             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27554
27555             var pos = this.activeHandle.position;
27556
27557             switch(pos){
27558                 case "east":
27559                     w += diffX;
27560                     w = Math.min(Math.max(mw, w), mxw);
27561                     break;
27562                 case "south":
27563                     h += diffY;
27564                     h = Math.min(Math.max(mh, h), mxh);
27565                     break;
27566                 case "southeast":
27567                     w += diffX;
27568                     h += diffY;
27569                     w = Math.min(Math.max(mw, w), mxw);
27570                     h = Math.min(Math.max(mh, h), mxh);
27571                     break;
27572                 case "north":
27573                     diffY = this.constrain(h, diffY, mh, mxh);
27574                     y += diffY;
27575                     h -= diffY;
27576                     break;
27577                 case "west":
27578                     diffX = this.constrain(w, diffX, mw, mxw);
27579                     x += diffX;
27580                     w -= diffX;
27581                     break;
27582                 case "northeast":
27583                     w += diffX;
27584                     w = Math.min(Math.max(mw, w), mxw);
27585                     diffY = this.constrain(h, diffY, mh, mxh);
27586                     y += diffY;
27587                     h -= diffY;
27588                     break;
27589                 case "northwest":
27590                     diffX = this.constrain(w, diffX, mw, mxw);
27591                     diffY = this.constrain(h, diffY, mh, mxh);
27592                     y += diffY;
27593                     h -= diffY;
27594                     x += diffX;
27595                     w -= diffX;
27596                     break;
27597                case "southwest":
27598                     diffX = this.constrain(w, diffX, mw, mxw);
27599                     h += diffY;
27600                     h = Math.min(Math.max(mh, h), mxh);
27601                     x += diffX;
27602                     w -= diffX;
27603                     break;
27604             }
27605
27606             var sw = this.snap(w, wi, mw);
27607             var sh = this.snap(h, hi, mh);
27608             if(sw != w || sh != h){
27609                 switch(pos){
27610                     case "northeast":
27611                         y -= sh - h;
27612                     break;
27613                     case "north":
27614                         y -= sh - h;
27615                         break;
27616                     case "southwest":
27617                         x -= sw - w;
27618                     break;
27619                     case "west":
27620                         x -= sw - w;
27621                         break;
27622                     case "northwest":
27623                         x -= sw - w;
27624                         y -= sh - h;
27625                     break;
27626                 }
27627                 w = sw;
27628                 h = sh;
27629             }
27630
27631             if(this.preserveRatio){
27632                 switch(pos){
27633                     case "southeast":
27634                     case "east":
27635                         h = oh * (w/ow);
27636                         h = Math.min(Math.max(mh, h), mxh);
27637                         w = ow * (h/oh);
27638                        break;
27639                     case "south":
27640                         w = ow * (h/oh);
27641                         w = Math.min(Math.max(mw, w), mxw);
27642                         h = oh * (w/ow);
27643                         break;
27644                     case "northeast":
27645                         w = ow * (h/oh);
27646                         w = Math.min(Math.max(mw, w), mxw);
27647                         h = oh * (w/ow);
27648                     break;
27649                     case "north":
27650                         var tw = w;
27651                         w = ow * (h/oh);
27652                         w = Math.min(Math.max(mw, w), mxw);
27653                         h = oh * (w/ow);
27654                         x += (tw - w) / 2;
27655                         break;
27656                     case "southwest":
27657                         h = oh * (w/ow);
27658                         h = Math.min(Math.max(mh, h), mxh);
27659                         var tw = w;
27660                         w = ow * (h/oh);
27661                         x += tw - w;
27662                         break;
27663                     case "west":
27664                         var th = h;
27665                         h = oh * (w/ow);
27666                         h = Math.min(Math.max(mh, h), mxh);
27667                         y += (th - h) / 2;
27668                         var tw = w;
27669                         w = ow * (h/oh);
27670                         x += tw - w;
27671                        break;
27672                     case "northwest":
27673                         var tw = w;
27674                         var th = h;
27675                         h = oh * (w/ow);
27676                         h = Math.min(Math.max(mh, h), mxh);
27677                         w = ow * (h/oh);
27678                         y += th - h;
27679                          x += tw - w;
27680                        break;
27681
27682                 }
27683             }
27684             this.proxy.setBounds(x, y, w, h);
27685             if(this.dynamic){
27686                 this.resizeElement();
27687             }
27688             }catch(e){}
27689         }
27690     },
27691
27692     // private
27693     handleOver : function(){
27694         if(this.enabled){
27695             this.el.addClass("x-resizable-over");
27696         }
27697     },
27698
27699     // private
27700     handleOut : function(){
27701         if(!this.resizing){
27702             this.el.removeClass("x-resizable-over");
27703         }
27704     },
27705
27706     /**
27707      * Returns the element this component is bound to.
27708      * @return {Roo.Element}
27709      */
27710     getEl : function(){
27711         return this.el;
27712     },
27713
27714     /**
27715      * Returns the resizeChild element (or null).
27716      * @return {Roo.Element}
27717      */
27718     getResizeChild : function(){
27719         return this.resizeChild;
27720     },
27721
27722     /**
27723      * Destroys this resizable. If the element was wrapped and
27724      * removeEl is not true then the element remains.
27725      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27726      */
27727     destroy : function(removeEl){
27728         this.proxy.remove();
27729         if(this.overlay){
27730             this.overlay.removeAllListeners();
27731             this.overlay.remove();
27732         }
27733         var ps = Roo.Resizable.positions;
27734         for(var k in ps){
27735             if(typeof ps[k] != "function" && this[ps[k]]){
27736                 var h = this[ps[k]];
27737                 h.el.removeAllListeners();
27738                 h.el.remove();
27739             }
27740         }
27741         if(removeEl){
27742             this.el.update("");
27743             this.el.remove();
27744         }
27745     }
27746 });
27747
27748 // private
27749 // hash to map config positions to true positions
27750 Roo.Resizable.positions = {
27751     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27752 };
27753
27754 // private
27755 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27756     if(!this.tpl){
27757         // only initialize the template if resizable is used
27758         var tpl = Roo.DomHelper.createTemplate(
27759             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27760         );
27761         tpl.compile();
27762         Roo.Resizable.Handle.prototype.tpl = tpl;
27763     }
27764     this.position = pos;
27765     this.rz = rz;
27766     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27767     this.el.unselectable();
27768     if(transparent){
27769         this.el.setOpacity(0);
27770     }
27771     this.el.on("mousedown", this.onMouseDown, this);
27772     if(!disableTrackOver){
27773         this.el.on("mouseover", this.onMouseOver, this);
27774         this.el.on("mouseout", this.onMouseOut, this);
27775     }
27776 };
27777
27778 // private
27779 Roo.Resizable.Handle.prototype = {
27780     afterResize : function(rz){
27781         // do nothing
27782     },
27783     // private
27784     onMouseDown : function(e){
27785         this.rz.onMouseDown(this, e);
27786     },
27787     // private
27788     onMouseOver : function(e){
27789         this.rz.handleOver(this, e);
27790     },
27791     // private
27792     onMouseOut : function(e){
27793         this.rz.handleOut(this, e);
27794     }
27795 };/*
27796  * Based on:
27797  * Ext JS Library 1.1.1
27798  * Copyright(c) 2006-2007, Ext JS, LLC.
27799  *
27800  * Originally Released Under LGPL - original licence link has changed is not relivant.
27801  *
27802  * Fork - LGPL
27803  * <script type="text/javascript">
27804  */
27805
27806 /**
27807  * @class Roo.Editor
27808  * @extends Roo.Component
27809  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27810  * @constructor
27811  * Create a new Editor
27812  * @param {Roo.form.Field} field The Field object (or descendant)
27813  * @param {Object} config The config object
27814  */
27815 Roo.Editor = function(field, config){
27816     Roo.Editor.superclass.constructor.call(this, config);
27817     this.field = field;
27818     this.addEvents({
27819         /**
27820              * @event beforestartedit
27821              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27822              * false from the handler of this event.
27823              * @param {Editor} this
27824              * @param {Roo.Element} boundEl The underlying element bound to this editor
27825              * @param {Mixed} value The field value being set
27826              */
27827         "beforestartedit" : true,
27828         /**
27829              * @event startedit
27830              * Fires when this editor is displayed
27831              * @param {Roo.Element} boundEl The underlying element bound to this editor
27832              * @param {Mixed} value The starting field value
27833              */
27834         "startedit" : true,
27835         /**
27836              * @event beforecomplete
27837              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27838              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27839              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27840              * event will not fire since no edit actually occurred.
27841              * @param {Editor} this
27842              * @param {Mixed} value The current field value
27843              * @param {Mixed} startValue The original field value
27844              */
27845         "beforecomplete" : true,
27846         /**
27847              * @event complete
27848              * Fires after editing is complete and any changed value has been written to the underlying field.
27849              * @param {Editor} this
27850              * @param {Mixed} value The current field value
27851              * @param {Mixed} startValue The original field value
27852              */
27853         "complete" : true,
27854         /**
27855          * @event specialkey
27856          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
27857          * {@link Roo.EventObject#getKey} to determine which key was pressed.
27858          * @param {Roo.form.Field} this
27859          * @param {Roo.EventObject} e The event object
27860          */
27861         "specialkey" : true
27862     });
27863 };
27864
27865 Roo.extend(Roo.Editor, Roo.Component, {
27866     /**
27867      * @cfg {Boolean/String} autosize
27868      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
27869      * or "height" to adopt the height only (defaults to false)
27870      */
27871     /**
27872      * @cfg {Boolean} revertInvalid
27873      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
27874      * validation fails (defaults to true)
27875      */
27876     /**
27877      * @cfg {Boolean} ignoreNoChange
27878      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
27879      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
27880      * will never be ignored.
27881      */
27882     /**
27883      * @cfg {Boolean} hideEl
27884      * False to keep the bound element visible while the editor is displayed (defaults to true)
27885      */
27886     /**
27887      * @cfg {Mixed} value
27888      * The data value of the underlying field (defaults to "")
27889      */
27890     value : "",
27891     /**
27892      * @cfg {String} alignment
27893      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
27894      */
27895     alignment: "c-c?",
27896     /**
27897      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
27898      * for bottom-right shadow (defaults to "frame")
27899      */
27900     shadow : "frame",
27901     /**
27902      * @cfg {Boolean} constrain True to constrain the editor to the viewport
27903      */
27904     constrain : false,
27905     /**
27906      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
27907      */
27908     completeOnEnter : false,
27909     /**
27910      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
27911      */
27912     cancelOnEsc : false,
27913     /**
27914      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
27915      */
27916     updateEl : false,
27917
27918     // private
27919     onRender : function(ct, position){
27920         this.el = new Roo.Layer({
27921             shadow: this.shadow,
27922             cls: "x-editor",
27923             parentEl : ct,
27924             shim : this.shim,
27925             shadowOffset:4,
27926             id: this.id,
27927             constrain: this.constrain
27928         });
27929         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
27930         if(this.field.msgTarget != 'title'){
27931             this.field.msgTarget = 'qtip';
27932         }
27933         this.field.render(this.el);
27934         if(Roo.isGecko){
27935             this.field.el.dom.setAttribute('autocomplete', 'off');
27936         }
27937         this.field.on("specialkey", this.onSpecialKey, this);
27938         if(this.swallowKeys){
27939             this.field.el.swallowEvent(['keydown','keypress']);
27940         }
27941         this.field.show();
27942         this.field.on("blur", this.onBlur, this);
27943         if(this.field.grow){
27944             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
27945         }
27946     },
27947
27948     onSpecialKey : function(field, e){
27949         if(this.completeOnEnter && e.getKey() == e.ENTER){
27950             e.stopEvent();
27951             this.completeEdit();
27952         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
27953             this.cancelEdit();
27954         }else{
27955             this.fireEvent('specialkey', field, e);
27956         }
27957     },
27958
27959     /**
27960      * Starts the editing process and shows the editor.
27961      * @param {String/HTMLElement/Element} el The element to edit
27962      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
27963       * to the innerHTML of el.
27964      */
27965     startEdit : function(el, value){
27966         if(this.editing){
27967             this.completeEdit();
27968         }
27969         this.boundEl = Roo.get(el);
27970         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
27971         if(!this.rendered){
27972             this.render(this.parentEl || document.body);
27973         }
27974         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
27975             return;
27976         }
27977         this.startValue = v;
27978         this.field.setValue(v);
27979         if(this.autoSize){
27980             var sz = this.boundEl.getSize();
27981             switch(this.autoSize){
27982                 case "width":
27983                 this.setSize(sz.width,  "");
27984                 break;
27985                 case "height":
27986                 this.setSize("",  sz.height);
27987                 break;
27988                 default:
27989                 this.setSize(sz.width,  sz.height);
27990             }
27991         }
27992         this.el.alignTo(this.boundEl, this.alignment);
27993         this.editing = true;
27994         if(Roo.QuickTips){
27995             Roo.QuickTips.disable();
27996         }
27997         this.show();
27998     },
27999
28000     /**
28001      * Sets the height and width of this editor.
28002      * @param {Number} width The new width
28003      * @param {Number} height The new height
28004      */
28005     setSize : function(w, h){
28006         this.field.setSize(w, h);
28007         if(this.el){
28008             this.el.sync();
28009         }
28010     },
28011
28012     /**
28013      * Realigns the editor to the bound field based on the current alignment config value.
28014      */
28015     realign : function(){
28016         this.el.alignTo(this.boundEl, this.alignment);
28017     },
28018
28019     /**
28020      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28021      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28022      */
28023     completeEdit : function(remainVisible){
28024         if(!this.editing){
28025             return;
28026         }
28027         var v = this.getValue();
28028         if(this.revertInvalid !== false && !this.field.isValid()){
28029             v = this.startValue;
28030             this.cancelEdit(true);
28031         }
28032         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28033             this.editing = false;
28034             this.hide();
28035             return;
28036         }
28037         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28038             this.editing = false;
28039             if(this.updateEl && this.boundEl){
28040                 this.boundEl.update(v);
28041             }
28042             if(remainVisible !== true){
28043                 this.hide();
28044             }
28045             this.fireEvent("complete", this, v, this.startValue);
28046         }
28047     },
28048
28049     // private
28050     onShow : function(){
28051         this.el.show();
28052         if(this.hideEl !== false){
28053             this.boundEl.hide();
28054         }
28055         this.field.show();
28056         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28057             this.fixIEFocus = true;
28058             this.deferredFocus.defer(50, this);
28059         }else{
28060             this.field.focus();
28061         }
28062         this.fireEvent("startedit", this.boundEl, this.startValue);
28063     },
28064
28065     deferredFocus : function(){
28066         if(this.editing){
28067             this.field.focus();
28068         }
28069     },
28070
28071     /**
28072      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28073      * reverted to the original starting value.
28074      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28075      * cancel (defaults to false)
28076      */
28077     cancelEdit : function(remainVisible){
28078         if(this.editing){
28079             this.setValue(this.startValue);
28080             if(remainVisible !== true){
28081                 this.hide();
28082             }
28083         }
28084     },
28085
28086     // private
28087     onBlur : function(){
28088         if(this.allowBlur !== true && this.editing){
28089             this.completeEdit();
28090         }
28091     },
28092
28093     // private
28094     onHide : function(){
28095         if(this.editing){
28096             this.completeEdit();
28097             return;
28098         }
28099         this.field.blur();
28100         if(this.field.collapse){
28101             this.field.collapse();
28102         }
28103         this.el.hide();
28104         if(this.hideEl !== false){
28105             this.boundEl.show();
28106         }
28107         if(Roo.QuickTips){
28108             Roo.QuickTips.enable();
28109         }
28110     },
28111
28112     /**
28113      * Sets the data value of the editor
28114      * @param {Mixed} value Any valid value supported by the underlying field
28115      */
28116     setValue : function(v){
28117         this.field.setValue(v);
28118     },
28119
28120     /**
28121      * Gets the data value of the editor
28122      * @return {Mixed} The data value
28123      */
28124     getValue : function(){
28125         return this.field.getValue();
28126     }
28127 });/*
28128  * Based on:
28129  * Ext JS Library 1.1.1
28130  * Copyright(c) 2006-2007, Ext JS, LLC.
28131  *
28132  * Originally Released Under LGPL - original licence link has changed is not relivant.
28133  *
28134  * Fork - LGPL
28135  * <script type="text/javascript">
28136  */
28137  
28138 /**
28139  * @class Roo.BasicDialog
28140  * @extends Roo.util.Observable
28141  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28142  * <pre><code>
28143 var dlg = new Roo.BasicDialog("my-dlg", {
28144     height: 200,
28145     width: 300,
28146     minHeight: 100,
28147     minWidth: 150,
28148     modal: true,
28149     proxyDrag: true,
28150     shadow: true
28151 });
28152 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28153 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28154 dlg.addButton('Cancel', dlg.hide, dlg);
28155 dlg.show();
28156 </code></pre>
28157   <b>A Dialog should always be a direct child of the body element.</b>
28158  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28159  * @cfg {String} title Default text to display in the title bar (defaults to null)
28160  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28161  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28162  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28163  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28164  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28165  * (defaults to null with no animation)
28166  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28167  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28168  * property for valid values (defaults to 'all')
28169  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28170  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28171  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28172  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28173  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28174  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28175  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28176  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28177  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28178  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28179  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28180  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28181  * draggable = true (defaults to false)
28182  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28183  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28184  * shadow (defaults to false)
28185  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28186  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28187  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28188  * @cfg {Array} buttons Array of buttons
28189  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28190  * @constructor
28191  * Create a new BasicDialog.
28192  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28193  * @param {Object} config Configuration options
28194  */
28195 Roo.BasicDialog = function(el, config){
28196     this.el = Roo.get(el);
28197     var dh = Roo.DomHelper;
28198     if(!this.el && config && config.autoCreate){
28199         if(typeof config.autoCreate == "object"){
28200             if(!config.autoCreate.id){
28201                 config.autoCreate.id = el;
28202             }
28203             this.el = dh.append(document.body,
28204                         config.autoCreate, true);
28205         }else{
28206             this.el = dh.append(document.body,
28207                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28208         }
28209     }
28210     el = this.el;
28211     el.setDisplayed(true);
28212     el.hide = this.hideAction;
28213     this.id = el.id;
28214     el.addClass("x-dlg");
28215
28216     Roo.apply(this, config);
28217
28218     this.proxy = el.createProxy("x-dlg-proxy");
28219     this.proxy.hide = this.hideAction;
28220     this.proxy.setOpacity(.5);
28221     this.proxy.hide();
28222
28223     if(config.width){
28224         el.setWidth(config.width);
28225     }
28226     if(config.height){
28227         el.setHeight(config.height);
28228     }
28229     this.size = el.getSize();
28230     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28231         this.xy = [config.x,config.y];
28232     }else{
28233         this.xy = el.getCenterXY(true);
28234     }
28235     /** The header element @type Roo.Element */
28236     this.header = el.child("> .x-dlg-hd");
28237     /** The body element @type Roo.Element */
28238     this.body = el.child("> .x-dlg-bd");
28239     /** The footer element @type Roo.Element */
28240     this.footer = el.child("> .x-dlg-ft");
28241
28242     if(!this.header){
28243         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28244     }
28245     if(!this.body){
28246         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28247     }
28248
28249     this.header.unselectable();
28250     if(this.title){
28251         this.header.update(this.title);
28252     }
28253     // this element allows the dialog to be focused for keyboard event
28254     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28255     this.focusEl.swallowEvent("click", true);
28256
28257     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28258
28259     // wrap the body and footer for special rendering
28260     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28261     if(this.footer){
28262         this.bwrap.dom.appendChild(this.footer.dom);
28263     }
28264
28265     this.bg = this.el.createChild({
28266         tag: "div", cls:"x-dlg-bg",
28267         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28268     });
28269     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28270
28271
28272     if(this.autoScroll !== false && !this.autoTabs){
28273         this.body.setStyle("overflow", "auto");
28274     }
28275
28276     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28277
28278     if(this.closable !== false){
28279         this.el.addClass("x-dlg-closable");
28280         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28281         this.close.on("click", this.closeClick, this);
28282         this.close.addClassOnOver("x-dlg-close-over");
28283     }
28284     if(this.collapsible !== false){
28285         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28286         this.collapseBtn.on("click", this.collapseClick, this);
28287         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28288         this.header.on("dblclick", this.collapseClick, this);
28289     }
28290     if(this.resizable !== false){
28291         this.el.addClass("x-dlg-resizable");
28292         this.resizer = new Roo.Resizable(el, {
28293             minWidth: this.minWidth || 80,
28294             minHeight:this.minHeight || 80,
28295             handles: this.resizeHandles || "all",
28296             pinned: true
28297         });
28298         this.resizer.on("beforeresize", this.beforeResize, this);
28299         this.resizer.on("resize", this.onResize, this);
28300     }
28301     if(this.draggable !== false){
28302         el.addClass("x-dlg-draggable");
28303         if (!this.proxyDrag) {
28304             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28305         }
28306         else {
28307             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28308         }
28309         dd.setHandleElId(this.header.id);
28310         dd.endDrag = this.endMove.createDelegate(this);
28311         dd.startDrag = this.startMove.createDelegate(this);
28312         dd.onDrag = this.onDrag.createDelegate(this);
28313         dd.scroll = false;
28314         this.dd = dd;
28315     }
28316     if(this.modal){
28317         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28318         this.mask.enableDisplayMode("block");
28319         this.mask.hide();
28320         this.el.addClass("x-dlg-modal");
28321     }
28322     if(this.shadow){
28323         this.shadow = new Roo.Shadow({
28324             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28325             offset : this.shadowOffset
28326         });
28327     }else{
28328         this.shadowOffset = 0;
28329     }
28330     if(Roo.useShims && this.shim !== false){
28331         this.shim = this.el.createShim();
28332         this.shim.hide = this.hideAction;
28333         this.shim.hide();
28334     }else{
28335         this.shim = false;
28336     }
28337     if(this.autoTabs){
28338         this.initTabs();
28339     }
28340     if (this.buttons) { 
28341         var bts= this.buttons;
28342         this.buttons = [];
28343         Roo.each(bts, function(b) {
28344             this.addButton(b);
28345         }, this);
28346     }
28347     
28348     
28349     this.addEvents({
28350         /**
28351          * @event keydown
28352          * Fires when a key is pressed
28353          * @param {Roo.BasicDialog} this
28354          * @param {Roo.EventObject} e
28355          */
28356         "keydown" : true,
28357         /**
28358          * @event move
28359          * Fires when this dialog is moved by the user.
28360          * @param {Roo.BasicDialog} this
28361          * @param {Number} x The new page X
28362          * @param {Number} y The new page Y
28363          */
28364         "move" : true,
28365         /**
28366          * @event resize
28367          * Fires when this dialog is resized by the user.
28368          * @param {Roo.BasicDialog} this
28369          * @param {Number} width The new width
28370          * @param {Number} height The new height
28371          */
28372         "resize" : true,
28373         /**
28374          * @event beforehide
28375          * Fires before this dialog is hidden.
28376          * @param {Roo.BasicDialog} this
28377          */
28378         "beforehide" : true,
28379         /**
28380          * @event hide
28381          * Fires when this dialog is hidden.
28382          * @param {Roo.BasicDialog} this
28383          */
28384         "hide" : true,
28385         /**
28386          * @event beforeshow
28387          * Fires before this dialog is shown.
28388          * @param {Roo.BasicDialog} this
28389          */
28390         "beforeshow" : true,
28391         /**
28392          * @event show
28393          * Fires when this dialog is shown.
28394          * @param {Roo.BasicDialog} this
28395          */
28396         "show" : true
28397     });
28398     el.on("keydown", this.onKeyDown, this);
28399     el.on("mousedown", this.toFront, this);
28400     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28401     this.el.hide();
28402     Roo.DialogManager.register(this);
28403     Roo.BasicDialog.superclass.constructor.call(this);
28404 };
28405
28406 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28407     shadowOffset: Roo.isIE ? 6 : 5,
28408     minHeight: 80,
28409     minWidth: 200,
28410     minButtonWidth: 75,
28411     defaultButton: null,
28412     buttonAlign: "right",
28413     tabTag: 'div',
28414     firstShow: true,
28415
28416     /**
28417      * Sets the dialog title text
28418      * @param {String} text The title text to display
28419      * @return {Roo.BasicDialog} this
28420      */
28421     setTitle : function(text){
28422         this.header.update(text);
28423         return this;
28424     },
28425
28426     // private
28427     closeClick : function(){
28428         this.hide();
28429     },
28430
28431     // private
28432     collapseClick : function(){
28433         this[this.collapsed ? "expand" : "collapse"]();
28434     },
28435
28436     /**
28437      * Collapses the dialog to its minimized state (only the title bar is visible).
28438      * Equivalent to the user clicking the collapse dialog button.
28439      */
28440     collapse : function(){
28441         if(!this.collapsed){
28442             this.collapsed = true;
28443             this.el.addClass("x-dlg-collapsed");
28444             this.restoreHeight = this.el.getHeight();
28445             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28446         }
28447     },
28448
28449     /**
28450      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28451      * clicking the expand dialog button.
28452      */
28453     expand : function(){
28454         if(this.collapsed){
28455             this.collapsed = false;
28456             this.el.removeClass("x-dlg-collapsed");
28457             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28458         }
28459     },
28460
28461     /**
28462      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28463      * @return {Roo.TabPanel} The tabs component
28464      */
28465     initTabs : function(){
28466         var tabs = this.getTabs();
28467         while(tabs.getTab(0)){
28468             tabs.removeTab(0);
28469         }
28470         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28471             var dom = el.dom;
28472             tabs.addTab(Roo.id(dom), dom.title);
28473             dom.title = "";
28474         });
28475         tabs.activate(0);
28476         return tabs;
28477     },
28478
28479     // private
28480     beforeResize : function(){
28481         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28482     },
28483
28484     // private
28485     onResize : function(){
28486         this.refreshSize();
28487         this.syncBodyHeight();
28488         this.adjustAssets();
28489         this.focus();
28490         this.fireEvent("resize", this, this.size.width, this.size.height);
28491     },
28492
28493     // private
28494     onKeyDown : function(e){
28495         if(this.isVisible()){
28496             this.fireEvent("keydown", this, e);
28497         }
28498     },
28499
28500     /**
28501      * Resizes the dialog.
28502      * @param {Number} width
28503      * @param {Number} height
28504      * @return {Roo.BasicDialog} this
28505      */
28506     resizeTo : function(width, height){
28507         this.el.setSize(width, height);
28508         this.size = {width: width, height: height};
28509         this.syncBodyHeight();
28510         if(this.fixedcenter){
28511             this.center();
28512         }
28513         if(this.isVisible()){
28514             this.constrainXY();
28515             this.adjustAssets();
28516         }
28517         this.fireEvent("resize", this, width, height);
28518         return this;
28519     },
28520
28521
28522     /**
28523      * Resizes the dialog to fit the specified content size.
28524      * @param {Number} width
28525      * @param {Number} height
28526      * @return {Roo.BasicDialog} this
28527      */
28528     setContentSize : function(w, h){
28529         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28530         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28531         //if(!this.el.isBorderBox()){
28532             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28533             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28534         //}
28535         if(this.tabs){
28536             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28537             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28538         }
28539         this.resizeTo(w, h);
28540         return this;
28541     },
28542
28543     /**
28544      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28545      * executed in response to a particular key being pressed while the dialog is active.
28546      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28547      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28548      * @param {Function} fn The function to call
28549      * @param {Object} scope (optional) The scope of the function
28550      * @return {Roo.BasicDialog} this
28551      */
28552     addKeyListener : function(key, fn, scope){
28553         var keyCode, shift, ctrl, alt;
28554         if(typeof key == "object" && !(key instanceof Array)){
28555             keyCode = key["key"];
28556             shift = key["shift"];
28557             ctrl = key["ctrl"];
28558             alt = key["alt"];
28559         }else{
28560             keyCode = key;
28561         }
28562         var handler = function(dlg, e){
28563             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28564                 var k = e.getKey();
28565                 if(keyCode instanceof Array){
28566                     for(var i = 0, len = keyCode.length; i < len; i++){
28567                         if(keyCode[i] == k){
28568                           fn.call(scope || window, dlg, k, e);
28569                           return;
28570                         }
28571                     }
28572                 }else{
28573                     if(k == keyCode){
28574                         fn.call(scope || window, dlg, k, e);
28575                     }
28576                 }
28577             }
28578         };
28579         this.on("keydown", handler);
28580         return this;
28581     },
28582
28583     /**
28584      * Returns the TabPanel component (creates it if it doesn't exist).
28585      * Note: If you wish to simply check for the existence of tabs without creating them,
28586      * check for a null 'tabs' property.
28587      * @return {Roo.TabPanel} The tabs component
28588      */
28589     getTabs : function(){
28590         if(!this.tabs){
28591             this.el.addClass("x-dlg-auto-tabs");
28592             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28593             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28594         }
28595         return this.tabs;
28596     },
28597
28598     /**
28599      * Adds a button to the footer section of the dialog.
28600      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28601      * object or a valid Roo.DomHelper element config
28602      * @param {Function} handler The function called when the button is clicked
28603      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28604      * @return {Roo.Button} The new button
28605      */
28606     addButton : function(config, handler, scope){
28607         var dh = Roo.DomHelper;
28608         if(!this.footer){
28609             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28610         }
28611         if(!this.btnContainer){
28612             var tb = this.footer.createChild({
28613
28614                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28615                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28616             }, null, true);
28617             this.btnContainer = tb.firstChild.firstChild.firstChild;
28618         }
28619         var bconfig = {
28620             handler: handler,
28621             scope: scope,
28622             minWidth: this.minButtonWidth,
28623             hideParent:true
28624         };
28625         if(typeof config == "string"){
28626             bconfig.text = config;
28627         }else{
28628             if(config.tag){
28629                 bconfig.dhconfig = config;
28630             }else{
28631                 Roo.apply(bconfig, config);
28632             }
28633         }
28634         var fc = false;
28635         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28636             bconfig.position = Math.max(0, bconfig.position);
28637             fc = this.btnContainer.childNodes[bconfig.position];
28638         }
28639          
28640         var btn = new Roo.Button(
28641             fc ? 
28642                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28643                 : this.btnContainer.appendChild(document.createElement("td")),
28644             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28645             bconfig
28646         );
28647         this.syncBodyHeight();
28648         if(!this.buttons){
28649             /**
28650              * Array of all the buttons that have been added to this dialog via addButton
28651              * @type Array
28652              */
28653             this.buttons = [];
28654         }
28655         this.buttons.push(btn);
28656         return btn;
28657     },
28658
28659     /**
28660      * Sets the default button to be focused when the dialog is displayed.
28661      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28662      * @return {Roo.BasicDialog} this
28663      */
28664     setDefaultButton : function(btn){
28665         this.defaultButton = btn;
28666         return this;
28667     },
28668
28669     // private
28670     getHeaderFooterHeight : function(safe){
28671         var height = 0;
28672         if(this.header){
28673            height += this.header.getHeight();
28674         }
28675         if(this.footer){
28676            var fm = this.footer.getMargins();
28677             height += (this.footer.getHeight()+fm.top+fm.bottom);
28678         }
28679         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28680         height += this.centerBg.getPadding("tb");
28681         return height;
28682     },
28683
28684     // private
28685     syncBodyHeight : function(){
28686         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28687         var height = this.size.height - this.getHeaderFooterHeight(false);
28688         bd.setHeight(height-bd.getMargins("tb"));
28689         var hh = this.header.getHeight();
28690         var h = this.size.height-hh;
28691         cb.setHeight(h);
28692         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28693         bw.setHeight(h-cb.getPadding("tb"));
28694         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28695         bd.setWidth(bw.getWidth(true));
28696         if(this.tabs){
28697             this.tabs.syncHeight();
28698             if(Roo.isIE){
28699                 this.tabs.el.repaint();
28700             }
28701         }
28702     },
28703
28704     /**
28705      * Restores the previous state of the dialog if Roo.state is configured.
28706      * @return {Roo.BasicDialog} this
28707      */
28708     restoreState : function(){
28709         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28710         if(box && box.width){
28711             this.xy = [box.x, box.y];
28712             this.resizeTo(box.width, box.height);
28713         }
28714         return this;
28715     },
28716
28717     // private
28718     beforeShow : function(){
28719         this.expand();
28720         if(this.fixedcenter){
28721             this.xy = this.el.getCenterXY(true);
28722         }
28723         if(this.modal){
28724             Roo.get(document.body).addClass("x-body-masked");
28725             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28726             this.mask.show();
28727         }
28728         this.constrainXY();
28729     },
28730
28731     // private
28732     animShow : function(){
28733         var b = Roo.get(this.animateTarget, true).getBox();
28734         this.proxy.setSize(b.width, b.height);
28735         this.proxy.setLocation(b.x, b.y);
28736         this.proxy.show();
28737         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28738                     true, .35, this.showEl.createDelegate(this));
28739     },
28740
28741     /**
28742      * Shows the dialog.
28743      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28744      * @return {Roo.BasicDialog} this
28745      */
28746     show : function(animateTarget){
28747         if (this.fireEvent("beforeshow", this) === false){
28748             return;
28749         }
28750         if(this.syncHeightBeforeShow){
28751             this.syncBodyHeight();
28752         }else if(this.firstShow){
28753             this.firstShow = false;
28754             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28755         }
28756         this.animateTarget = animateTarget || this.animateTarget;
28757         if(!this.el.isVisible()){
28758             this.beforeShow();
28759             if(this.animateTarget){
28760                 this.animShow();
28761             }else{
28762                 this.showEl();
28763             }
28764         }
28765         return this;
28766     },
28767
28768     // private
28769     showEl : function(){
28770         this.proxy.hide();
28771         this.el.setXY(this.xy);
28772         this.el.show();
28773         this.adjustAssets(true);
28774         this.toFront();
28775         this.focus();
28776         // IE peekaboo bug - fix found by Dave Fenwick
28777         if(Roo.isIE){
28778             this.el.repaint();
28779         }
28780         this.fireEvent("show", this);
28781     },
28782
28783     /**
28784      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28785      * dialog itself will receive focus.
28786      */
28787     focus : function(){
28788         if(this.defaultButton){
28789             this.defaultButton.focus();
28790         }else{
28791             this.focusEl.focus();
28792         }
28793     },
28794
28795     // private
28796     constrainXY : function(){
28797         if(this.constraintoviewport !== false){
28798             if(!this.viewSize){
28799                 if(this.container){
28800                     var s = this.container.getSize();
28801                     this.viewSize = [s.width, s.height];
28802                 }else{
28803                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28804                 }
28805             }
28806             var s = Roo.get(this.container||document).getScroll();
28807
28808             var x = this.xy[0], y = this.xy[1];
28809             var w = this.size.width, h = this.size.height;
28810             var vw = this.viewSize[0], vh = this.viewSize[1];
28811             // only move it if it needs it
28812             var moved = false;
28813             // first validate right/bottom
28814             if(x + w > vw+s.left){
28815                 x = vw - w;
28816                 moved = true;
28817             }
28818             if(y + h > vh+s.top){
28819                 y = vh - h;
28820                 moved = true;
28821             }
28822             // then make sure top/left isn't negative
28823             if(x < s.left){
28824                 x = s.left;
28825                 moved = true;
28826             }
28827             if(y < s.top){
28828                 y = s.top;
28829                 moved = true;
28830             }
28831             if(moved){
28832                 // cache xy
28833                 this.xy = [x, y];
28834                 if(this.isVisible()){
28835                     this.el.setLocation(x, y);
28836                     this.adjustAssets();
28837                 }
28838             }
28839         }
28840     },
28841
28842     // private
28843     onDrag : function(){
28844         if(!this.proxyDrag){
28845             this.xy = this.el.getXY();
28846             this.adjustAssets();
28847         }
28848     },
28849
28850     // private
28851     adjustAssets : function(doShow){
28852         var x = this.xy[0], y = this.xy[1];
28853         var w = this.size.width, h = this.size.height;
28854         if(doShow === true){
28855             if(this.shadow){
28856                 this.shadow.show(this.el);
28857             }
28858             if(this.shim){
28859                 this.shim.show();
28860             }
28861         }
28862         if(this.shadow && this.shadow.isVisible()){
28863             this.shadow.show(this.el);
28864         }
28865         if(this.shim && this.shim.isVisible()){
28866             this.shim.setBounds(x, y, w, h);
28867         }
28868     },
28869
28870     // private
28871     adjustViewport : function(w, h){
28872         if(!w || !h){
28873             w = Roo.lib.Dom.getViewWidth();
28874             h = Roo.lib.Dom.getViewHeight();
28875         }
28876         // cache the size
28877         this.viewSize = [w, h];
28878         if(this.modal && this.mask.isVisible()){
28879             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
28880             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28881         }
28882         if(this.isVisible()){
28883             this.constrainXY();
28884         }
28885     },
28886
28887     /**
28888      * Destroys this dialog and all its supporting elements (including any tabs, shim,
28889      * shadow, proxy, mask, etc.)  Also removes all event listeners.
28890      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28891      */
28892     destroy : function(removeEl){
28893         if(this.isVisible()){
28894             this.animateTarget = null;
28895             this.hide();
28896         }
28897         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
28898         if(this.tabs){
28899             this.tabs.destroy(removeEl);
28900         }
28901         Roo.destroy(
28902              this.shim,
28903              this.proxy,
28904              this.resizer,
28905              this.close,
28906              this.mask
28907         );
28908         if(this.dd){
28909             this.dd.unreg();
28910         }
28911         if(this.buttons){
28912            for(var i = 0, len = this.buttons.length; i < len; i++){
28913                this.buttons[i].destroy();
28914            }
28915         }
28916         this.el.removeAllListeners();
28917         if(removeEl === true){
28918             this.el.update("");
28919             this.el.remove();
28920         }
28921         Roo.DialogManager.unregister(this);
28922     },
28923
28924     // private
28925     startMove : function(){
28926         if(this.proxyDrag){
28927             this.proxy.show();
28928         }
28929         if(this.constraintoviewport !== false){
28930             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
28931         }
28932     },
28933
28934     // private
28935     endMove : function(){
28936         if(!this.proxyDrag){
28937             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
28938         }else{
28939             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
28940             this.proxy.hide();
28941         }
28942         this.refreshSize();
28943         this.adjustAssets();
28944         this.focus();
28945         this.fireEvent("move", this, this.xy[0], this.xy[1]);
28946     },
28947
28948     /**
28949      * Brings this dialog to the front of any other visible dialogs
28950      * @return {Roo.BasicDialog} this
28951      */
28952     toFront : function(){
28953         Roo.DialogManager.bringToFront(this);
28954         return this;
28955     },
28956
28957     /**
28958      * Sends this dialog to the back (under) of any other visible dialogs
28959      * @return {Roo.BasicDialog} this
28960      */
28961     toBack : function(){
28962         Roo.DialogManager.sendToBack(this);
28963         return this;
28964     },
28965
28966     /**
28967      * Centers this dialog in the viewport
28968      * @return {Roo.BasicDialog} this
28969      */
28970     center : function(){
28971         var xy = this.el.getCenterXY(true);
28972         this.moveTo(xy[0], xy[1]);
28973         return this;
28974     },
28975
28976     /**
28977      * Moves the dialog's top-left corner to the specified point
28978      * @param {Number} x
28979      * @param {Number} y
28980      * @return {Roo.BasicDialog} this
28981      */
28982     moveTo : function(x, y){
28983         this.xy = [x,y];
28984         if(this.isVisible()){
28985             this.el.setXY(this.xy);
28986             this.adjustAssets();
28987         }
28988         return this;
28989     },
28990
28991     /**
28992      * Aligns the dialog to the specified element
28993      * @param {String/HTMLElement/Roo.Element} element The element to align to.
28994      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
28995      * @param {Array} offsets (optional) Offset the positioning by [x, y]
28996      * @return {Roo.BasicDialog} this
28997      */
28998     alignTo : function(element, position, offsets){
28999         this.xy = this.el.getAlignToXY(element, position, offsets);
29000         if(this.isVisible()){
29001             this.el.setXY(this.xy);
29002             this.adjustAssets();
29003         }
29004         return this;
29005     },
29006
29007     /**
29008      * Anchors an element to another element and realigns it when the window is resized.
29009      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29010      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29011      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29012      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29013      * is a number, it is used as the buffer delay (defaults to 50ms).
29014      * @return {Roo.BasicDialog} this
29015      */
29016     anchorTo : function(el, alignment, offsets, monitorScroll){
29017         var action = function(){
29018             this.alignTo(el, alignment, offsets);
29019         };
29020         Roo.EventManager.onWindowResize(action, this);
29021         var tm = typeof monitorScroll;
29022         if(tm != 'undefined'){
29023             Roo.EventManager.on(window, 'scroll', action, this,
29024                 {buffer: tm == 'number' ? monitorScroll : 50});
29025         }
29026         action.call(this);
29027         return this;
29028     },
29029
29030     /**
29031      * Returns true if the dialog is visible
29032      * @return {Boolean}
29033      */
29034     isVisible : function(){
29035         return this.el.isVisible();
29036     },
29037
29038     // private
29039     animHide : function(callback){
29040         var b = Roo.get(this.animateTarget).getBox();
29041         this.proxy.show();
29042         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29043         this.el.hide();
29044         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29045                     this.hideEl.createDelegate(this, [callback]));
29046     },
29047
29048     /**
29049      * Hides the dialog.
29050      * @param {Function} callback (optional) Function to call when the dialog is hidden
29051      * @return {Roo.BasicDialog} this
29052      */
29053     hide : function(callback){
29054         if (this.fireEvent("beforehide", this) === false){
29055             return;
29056         }
29057         if(this.shadow){
29058             this.shadow.hide();
29059         }
29060         if(this.shim) {
29061           this.shim.hide();
29062         }
29063         if(this.animateTarget){
29064            this.animHide(callback);
29065         }else{
29066             this.el.hide();
29067             this.hideEl(callback);
29068         }
29069         return this;
29070     },
29071
29072     // private
29073     hideEl : function(callback){
29074         this.proxy.hide();
29075         if(this.modal){
29076             this.mask.hide();
29077             Roo.get(document.body).removeClass("x-body-masked");
29078         }
29079         this.fireEvent("hide", this);
29080         if(typeof callback == "function"){
29081             callback();
29082         }
29083     },
29084
29085     // private
29086     hideAction : function(){
29087         this.setLeft("-10000px");
29088         this.setTop("-10000px");
29089         this.setStyle("visibility", "hidden");
29090     },
29091
29092     // private
29093     refreshSize : function(){
29094         this.size = this.el.getSize();
29095         this.xy = this.el.getXY();
29096         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29097     },
29098
29099     // private
29100     // z-index is managed by the DialogManager and may be overwritten at any time
29101     setZIndex : function(index){
29102         if(this.modal){
29103             this.mask.setStyle("z-index", index);
29104         }
29105         if(this.shim){
29106             this.shim.setStyle("z-index", ++index);
29107         }
29108         if(this.shadow){
29109             this.shadow.setZIndex(++index);
29110         }
29111         this.el.setStyle("z-index", ++index);
29112         if(this.proxy){
29113             this.proxy.setStyle("z-index", ++index);
29114         }
29115         if(this.resizer){
29116             this.resizer.proxy.setStyle("z-index", ++index);
29117         }
29118
29119         this.lastZIndex = index;
29120     },
29121
29122     /**
29123      * Returns the element for this dialog
29124      * @return {Roo.Element} The underlying dialog Element
29125      */
29126     getEl : function(){
29127         return this.el;
29128     }
29129 });
29130
29131 /**
29132  * @class Roo.DialogManager
29133  * Provides global access to BasicDialogs that have been created and
29134  * support for z-indexing (layering) multiple open dialogs.
29135  */
29136 Roo.DialogManager = function(){
29137     var list = {};
29138     var accessList = [];
29139     var front = null;
29140
29141     // private
29142     var sortDialogs = function(d1, d2){
29143         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29144     };
29145
29146     // private
29147     var orderDialogs = function(){
29148         accessList.sort(sortDialogs);
29149         var seed = Roo.DialogManager.zseed;
29150         for(var i = 0, len = accessList.length; i < len; i++){
29151             var dlg = accessList[i];
29152             if(dlg){
29153                 dlg.setZIndex(seed + (i*10));
29154             }
29155         }
29156     };
29157
29158     return {
29159         /**
29160          * The starting z-index for BasicDialogs (defaults to 9000)
29161          * @type Number The z-index value
29162          */
29163         zseed : 9000,
29164
29165         // private
29166         register : function(dlg){
29167             list[dlg.id] = dlg;
29168             accessList.push(dlg);
29169         },
29170
29171         // private
29172         unregister : function(dlg){
29173             delete list[dlg.id];
29174             var i=0;
29175             var len=0;
29176             if(!accessList.indexOf){
29177                 for(  i = 0, len = accessList.length; i < len; i++){
29178                     if(accessList[i] == dlg){
29179                         accessList.splice(i, 1);
29180                         return;
29181                     }
29182                 }
29183             }else{
29184                  i = accessList.indexOf(dlg);
29185                 if(i != -1){
29186                     accessList.splice(i, 1);
29187                 }
29188             }
29189         },
29190
29191         /**
29192          * Gets a registered dialog by id
29193          * @param {String/Object} id The id of the dialog or a dialog
29194          * @return {Roo.BasicDialog} this
29195          */
29196         get : function(id){
29197             return typeof id == "object" ? id : list[id];
29198         },
29199
29200         /**
29201          * Brings the specified dialog to the front
29202          * @param {String/Object} dlg The id of the dialog or a dialog
29203          * @return {Roo.BasicDialog} this
29204          */
29205         bringToFront : function(dlg){
29206             dlg = this.get(dlg);
29207             if(dlg != front){
29208                 front = dlg;
29209                 dlg._lastAccess = new Date().getTime();
29210                 orderDialogs();
29211             }
29212             return dlg;
29213         },
29214
29215         /**
29216          * Sends the specified dialog to the back
29217          * @param {String/Object} dlg The id of the dialog or a dialog
29218          * @return {Roo.BasicDialog} this
29219          */
29220         sendToBack : function(dlg){
29221             dlg = this.get(dlg);
29222             dlg._lastAccess = -(new Date().getTime());
29223             orderDialogs();
29224             return dlg;
29225         },
29226
29227         /**
29228          * Hides all dialogs
29229          */
29230         hideAll : function(){
29231             for(var id in list){
29232                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29233                     list[id].hide();
29234                 }
29235             }
29236         }
29237     };
29238 }();
29239
29240 /**
29241  * @class Roo.LayoutDialog
29242  * @extends Roo.BasicDialog
29243  * Dialog which provides adjustments for working with a layout in a Dialog.
29244  * Add your necessary layout config options to the dialog's config.<br>
29245  * Example usage (including a nested layout):
29246  * <pre><code>
29247 if(!dialog){
29248     dialog = new Roo.LayoutDialog("download-dlg", {
29249         modal: true,
29250         width:600,
29251         height:450,
29252         shadow:true,
29253         minWidth:500,
29254         minHeight:350,
29255         autoTabs:true,
29256         proxyDrag:true,
29257         // layout config merges with the dialog config
29258         center:{
29259             tabPosition: "top",
29260             alwaysShowTabs: true
29261         }
29262     });
29263     dialog.addKeyListener(27, dialog.hide, dialog);
29264     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29265     dialog.addButton("Build It!", this.getDownload, this);
29266
29267     // we can even add nested layouts
29268     var innerLayout = new Roo.BorderLayout("dl-inner", {
29269         east: {
29270             initialSize: 200,
29271             autoScroll:true,
29272             split:true
29273         },
29274         center: {
29275             autoScroll:true
29276         }
29277     });
29278     innerLayout.beginUpdate();
29279     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29280     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29281     innerLayout.endUpdate(true);
29282
29283     var layout = dialog.getLayout();
29284     layout.beginUpdate();
29285     layout.add("center", new Roo.ContentPanel("standard-panel",
29286                         {title: "Download the Source", fitToFrame:true}));
29287     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29288                {title: "Build your own roo.js"}));
29289     layout.getRegion("center").showPanel(sp);
29290     layout.endUpdate();
29291 }
29292 </code></pre>
29293     * @constructor
29294     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29295     * @param {Object} config configuration options
29296   */
29297 Roo.LayoutDialog = function(el, cfg){
29298     
29299     var config=  cfg;
29300     if (typeof(cfg) == 'undefined') {
29301         config = Roo.apply({}, el);
29302         el = Roo.get( document.documentElement || document.body).createChild();
29303         //config.autoCreate = true;
29304     }
29305     
29306     
29307     config.autoTabs = false;
29308     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29309     this.body.setStyle({overflow:"hidden", position:"relative"});
29310     this.layout = new Roo.BorderLayout(this.body.dom, config);
29311     this.layout.monitorWindowResize = false;
29312     this.el.addClass("x-dlg-auto-layout");
29313     // fix case when center region overwrites center function
29314     this.center = Roo.BasicDialog.prototype.center;
29315     this.on("show", this.layout.layout, this.layout, true);
29316     if (config.items) {
29317         var xitems = config.items;
29318         delete config.items;
29319         Roo.each(xitems, this.addxtype, this);
29320     }
29321     
29322     
29323 };
29324 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29325     /**
29326      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29327      * @deprecated
29328      */
29329     endUpdate : function(){
29330         this.layout.endUpdate();
29331     },
29332
29333     /**
29334      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29335      *  @deprecated
29336      */
29337     beginUpdate : function(){
29338         this.layout.beginUpdate();
29339     },
29340
29341     /**
29342      * Get the BorderLayout for this dialog
29343      * @return {Roo.BorderLayout}
29344      */
29345     getLayout : function(){
29346         return this.layout;
29347     },
29348
29349     showEl : function(){
29350         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29351         if(Roo.isIE7){
29352             this.layout.layout();
29353         }
29354     },
29355
29356     // private
29357     // Use the syncHeightBeforeShow config option to control this automatically
29358     syncBodyHeight : function(){
29359         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29360         if(this.layout){this.layout.layout();}
29361     },
29362     
29363       /**
29364      * Add an xtype element (actually adds to the layout.)
29365      * @return {Object} xdata xtype object data.
29366      */
29367     
29368     addxtype : function(c) {
29369         return this.layout.addxtype(c);
29370     }
29371 });/*
29372  * Based on:
29373  * Ext JS Library 1.1.1
29374  * Copyright(c) 2006-2007, Ext JS, LLC.
29375  *
29376  * Originally Released Under LGPL - original licence link has changed is not relivant.
29377  *
29378  * Fork - LGPL
29379  * <script type="text/javascript">
29380  */
29381  
29382 /**
29383  * @class Roo.MessageBox
29384  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29385  * Example usage:
29386  *<pre><code>
29387 // Basic alert:
29388 Roo.Msg.alert('Status', 'Changes saved successfully.');
29389
29390 // Prompt for user data:
29391 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29392     if (btn == 'ok'){
29393         // process text value...
29394     }
29395 });
29396
29397 // Show a dialog using config options:
29398 Roo.Msg.show({
29399    title:'Save Changes?',
29400    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29401    buttons: Roo.Msg.YESNOCANCEL,
29402    fn: processResult,
29403    animEl: 'elId'
29404 });
29405 </code></pre>
29406  * @singleton
29407  */
29408 Roo.MessageBox = function(){
29409     var dlg, opt, mask, waitTimer;
29410     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29411     var buttons, activeTextEl, bwidth;
29412
29413     // private
29414     var handleButton = function(button){
29415         dlg.hide();
29416         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29417     };
29418
29419     // private
29420     var handleHide = function(){
29421         if(opt && opt.cls){
29422             dlg.el.removeClass(opt.cls);
29423         }
29424         if(waitTimer){
29425             Roo.TaskMgr.stop(waitTimer);
29426             waitTimer = null;
29427         }
29428     };
29429
29430     // private
29431     var updateButtons = function(b){
29432         var width = 0;
29433         if(!b){
29434             buttons["ok"].hide();
29435             buttons["cancel"].hide();
29436             buttons["yes"].hide();
29437             buttons["no"].hide();
29438             dlg.footer.dom.style.display = 'none';
29439             return width;
29440         }
29441         dlg.footer.dom.style.display = '';
29442         for(var k in buttons){
29443             if(typeof buttons[k] != "function"){
29444                 if(b[k]){
29445                     buttons[k].show();
29446                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29447                     width += buttons[k].el.getWidth()+15;
29448                 }else{
29449                     buttons[k].hide();
29450                 }
29451             }
29452         }
29453         return width;
29454     };
29455
29456     // private
29457     var handleEsc = function(d, k, e){
29458         if(opt && opt.closable !== false){
29459             dlg.hide();
29460         }
29461         if(e){
29462             e.stopEvent();
29463         }
29464     };
29465
29466     return {
29467         /**
29468          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29469          * @return {Roo.BasicDialog} The BasicDialog element
29470          */
29471         getDialog : function(){
29472            if(!dlg){
29473                 dlg = new Roo.BasicDialog("x-msg-box", {
29474                     autoCreate : true,
29475                     shadow: true,
29476                     draggable: true,
29477                     resizable:false,
29478                     constraintoviewport:false,
29479                     fixedcenter:true,
29480                     collapsible : false,
29481                     shim:true,
29482                     modal: true,
29483                     width:400, height:100,
29484                     buttonAlign:"center",
29485                     closeClick : function(){
29486                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29487                             handleButton("no");
29488                         }else{
29489                             handleButton("cancel");
29490                         }
29491                     }
29492                 });
29493                 dlg.on("hide", handleHide);
29494                 mask = dlg.mask;
29495                 dlg.addKeyListener(27, handleEsc);
29496                 buttons = {};
29497                 var bt = this.buttonText;
29498                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29499                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29500                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29501                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29502                 bodyEl = dlg.body.createChild({
29503
29504                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
29505                 });
29506                 msgEl = bodyEl.dom.firstChild;
29507                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29508                 textboxEl.enableDisplayMode();
29509                 textboxEl.addKeyListener([10,13], function(){
29510                     if(dlg.isVisible() && opt && opt.buttons){
29511                         if(opt.buttons.ok){
29512                             handleButton("ok");
29513                         }else if(opt.buttons.yes){
29514                             handleButton("yes");
29515                         }
29516                     }
29517                 });
29518                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29519                 textareaEl.enableDisplayMode();
29520                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29521                 progressEl.enableDisplayMode();
29522                 var pf = progressEl.dom.firstChild;
29523                 if (pf) {
29524                     pp = Roo.get(pf.firstChild);
29525                     pp.setHeight(pf.offsetHeight);
29526                 }
29527                 
29528             }
29529             return dlg;
29530         },
29531
29532         /**
29533          * Updates the message box body text
29534          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29535          * the XHTML-compliant non-breaking space character '&amp;#160;')
29536          * @return {Roo.MessageBox} This message box
29537          */
29538         updateText : function(text){
29539             if(!dlg.isVisible() && !opt.width){
29540                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29541             }
29542             msgEl.innerHTML = text || '&#160;';
29543             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29544                         Math.max(opt.minWidth || this.minWidth, bwidth));
29545             if(opt.prompt){
29546                 activeTextEl.setWidth(w);
29547             }
29548             if(dlg.isVisible()){
29549                 dlg.fixedcenter = false;
29550             }
29551             dlg.setContentSize(w, bodyEl.getHeight());
29552             if(dlg.isVisible()){
29553                 dlg.fixedcenter = true;
29554             }
29555             return this;
29556         },
29557
29558         /**
29559          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29560          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29561          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29562          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29563          * @return {Roo.MessageBox} This message box
29564          */
29565         updateProgress : function(value, text){
29566             if(text){
29567                 this.updateText(text);
29568             }
29569             if (pp) { // weird bug on my firefox - for some reason this is not defined
29570                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29571             }
29572             return this;
29573         },        
29574
29575         /**
29576          * Returns true if the message box is currently displayed
29577          * @return {Boolean} True if the message box is visible, else false
29578          */
29579         isVisible : function(){
29580             return dlg && dlg.isVisible();  
29581         },
29582
29583         /**
29584          * Hides the message box if it is displayed
29585          */
29586         hide : function(){
29587             if(this.isVisible()){
29588                 dlg.hide();
29589             }  
29590         },
29591
29592         /**
29593          * Displays a new message box, or reinitializes an existing message box, based on the config options
29594          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29595          * The following config object properties are supported:
29596          * <pre>
29597 Property    Type             Description
29598 ----------  ---------------  ------------------------------------------------------------------------------------
29599 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29600                                    closes (defaults to undefined)
29601 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29602                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29603 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29604                                    progress and wait dialogs will ignore this property and always hide the
29605                                    close button as they can only be closed programmatically.
29606 cls               String           A custom CSS class to apply to the message box element
29607 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29608                                    displayed (defaults to 75)
29609 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29610                                    function will be btn (the name of the button that was clicked, if applicable,
29611                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29612                                    Progress and wait dialogs will ignore this option since they do not respond to
29613                                    user actions and can only be closed programmatically, so any required function
29614                                    should be called by the same code after it closes the dialog.
29615 icon              String           A CSS class that provides a background image to be used as an icon for
29616                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29617 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29618 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29619 modal             Boolean          False to allow user interaction with the page while the message box is
29620                                    displayed (defaults to true)
29621 msg               String           A string that will replace the existing message box body text (defaults
29622                                    to the XHTML-compliant non-breaking space character '&#160;')
29623 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29624 progress          Boolean          True to display a progress bar (defaults to false)
29625 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29626 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29627 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29628 title             String           The title text
29629 value             String           The string value to set into the active textbox element if displayed
29630 wait              Boolean          True to display a progress bar (defaults to false)
29631 width             Number           The width of the dialog in pixels
29632 </pre>
29633          *
29634          * Example usage:
29635          * <pre><code>
29636 Roo.Msg.show({
29637    title: 'Address',
29638    msg: 'Please enter your address:',
29639    width: 300,
29640    buttons: Roo.MessageBox.OKCANCEL,
29641    multiline: true,
29642    fn: saveAddress,
29643    animEl: 'addAddressBtn'
29644 });
29645 </code></pre>
29646          * @param {Object} config Configuration options
29647          * @return {Roo.MessageBox} This message box
29648          */
29649         show : function(options){
29650             if(this.isVisible()){
29651                 this.hide();
29652             }
29653             var d = this.getDialog();
29654             opt = options;
29655             d.setTitle(opt.title || "&#160;");
29656             d.close.setDisplayed(opt.closable !== false);
29657             activeTextEl = textboxEl;
29658             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29659             if(opt.prompt){
29660                 if(opt.multiline){
29661                     textboxEl.hide();
29662                     textareaEl.show();
29663                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29664                         opt.multiline : this.defaultTextHeight);
29665                     activeTextEl = textareaEl;
29666                 }else{
29667                     textboxEl.show();
29668                     textareaEl.hide();
29669                 }
29670             }else{
29671                 textboxEl.hide();
29672                 textareaEl.hide();
29673             }
29674             progressEl.setDisplayed(opt.progress === true);
29675             this.updateProgress(0);
29676             activeTextEl.dom.value = opt.value || "";
29677             if(opt.prompt){
29678                 dlg.setDefaultButton(activeTextEl);
29679             }else{
29680                 var bs = opt.buttons;
29681                 var db = null;
29682                 if(bs && bs.ok){
29683                     db = buttons["ok"];
29684                 }else if(bs && bs.yes){
29685                     db = buttons["yes"];
29686                 }
29687                 dlg.setDefaultButton(db);
29688             }
29689             bwidth = updateButtons(opt.buttons);
29690             this.updateText(opt.msg);
29691             if(opt.cls){
29692                 d.el.addClass(opt.cls);
29693             }
29694             d.proxyDrag = opt.proxyDrag === true;
29695             d.modal = opt.modal !== false;
29696             d.mask = opt.modal !== false ? mask : false;
29697             if(!d.isVisible()){
29698                 // force it to the end of the z-index stack so it gets a cursor in FF
29699                 document.body.appendChild(dlg.el.dom);
29700                 d.animateTarget = null;
29701                 d.show(options.animEl);
29702             }
29703             return this;
29704         },
29705
29706         /**
29707          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29708          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29709          * and closing the message box when the process is complete.
29710          * @param {String} title The title bar text
29711          * @param {String} msg The message box body text
29712          * @return {Roo.MessageBox} This message box
29713          */
29714         progress : function(title, msg){
29715             this.show({
29716                 title : title,
29717                 msg : msg,
29718                 buttons: false,
29719                 progress:true,
29720                 closable:false,
29721                 minWidth: this.minProgressWidth,
29722                 modal : true
29723             });
29724             return this;
29725         },
29726
29727         /**
29728          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29729          * If a callback function is passed it will be called after the user clicks the button, and the
29730          * id of the button that was clicked will be passed as the only parameter to the callback
29731          * (could also be the top-right close button).
29732          * @param {String} title The title bar text
29733          * @param {String} msg The message box body text
29734          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29735          * @param {Object} scope (optional) The scope of the callback function
29736          * @return {Roo.MessageBox} This message box
29737          */
29738         alert : function(title, msg, fn, scope){
29739             this.show({
29740                 title : title,
29741                 msg : msg,
29742                 buttons: this.OK,
29743                 fn: fn,
29744                 scope : scope,
29745                 modal : true
29746             });
29747             return this;
29748         },
29749
29750         /**
29751          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29752          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29753          * You are responsible for closing the message box when the process is complete.
29754          * @param {String} msg The message box body text
29755          * @param {String} title (optional) The title bar text
29756          * @return {Roo.MessageBox} This message box
29757          */
29758         wait : function(msg, title){
29759             this.show({
29760                 title : title,
29761                 msg : msg,
29762                 buttons: false,
29763                 closable:false,
29764                 progress:true,
29765                 modal:true,
29766                 width:300,
29767                 wait:true
29768             });
29769             waitTimer = Roo.TaskMgr.start({
29770                 run: function(i){
29771                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29772                 },
29773                 interval: 1000
29774             });
29775             return this;
29776         },
29777
29778         /**
29779          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29780          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29781          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29782          * @param {String} title The title bar text
29783          * @param {String} msg The message box body text
29784          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29785          * @param {Object} scope (optional) The scope of the callback function
29786          * @return {Roo.MessageBox} This message box
29787          */
29788         confirm : function(title, msg, fn, scope){
29789             this.show({
29790                 title : title,
29791                 msg : msg,
29792                 buttons: this.YESNO,
29793                 fn: fn,
29794                 scope : scope,
29795                 modal : true
29796             });
29797             return this;
29798         },
29799
29800         /**
29801          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29802          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29803          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29804          * (could also be the top-right close button) and the text that was entered will be passed as the two
29805          * parameters to the callback.
29806          * @param {String} title The title bar text
29807          * @param {String} msg The message box body text
29808          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29809          * @param {Object} scope (optional) The scope of the callback function
29810          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29811          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29812          * @return {Roo.MessageBox} This message box
29813          */
29814         prompt : function(title, msg, fn, scope, multiline){
29815             this.show({
29816                 title : title,
29817                 msg : msg,
29818                 buttons: this.OKCANCEL,
29819                 fn: fn,
29820                 minWidth:250,
29821                 scope : scope,
29822                 prompt:true,
29823                 multiline: multiline,
29824                 modal : true
29825             });
29826             return this;
29827         },
29828
29829         /**
29830          * Button config that displays a single OK button
29831          * @type Object
29832          */
29833         OK : {ok:true},
29834         /**
29835          * Button config that displays Yes and No buttons
29836          * @type Object
29837          */
29838         YESNO : {yes:true, no:true},
29839         /**
29840          * Button config that displays OK and Cancel buttons
29841          * @type Object
29842          */
29843         OKCANCEL : {ok:true, cancel:true},
29844         /**
29845          * Button config that displays Yes, No and Cancel buttons
29846          * @type Object
29847          */
29848         YESNOCANCEL : {yes:true, no:true, cancel:true},
29849
29850         /**
29851          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
29852          * @type Number
29853          */
29854         defaultTextHeight : 75,
29855         /**
29856          * The maximum width in pixels of the message box (defaults to 600)
29857          * @type Number
29858          */
29859         maxWidth : 600,
29860         /**
29861          * The minimum width in pixels of the message box (defaults to 100)
29862          * @type Number
29863          */
29864         minWidth : 100,
29865         /**
29866          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
29867          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
29868          * @type Number
29869          */
29870         minProgressWidth : 250,
29871         /**
29872          * An object containing the default button text strings that can be overriden for localized language support.
29873          * Supported properties are: ok, cancel, yes and no.
29874          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
29875          * @type Object
29876          */
29877         buttonText : {
29878             ok : "OK",
29879             cancel : "Cancel",
29880             yes : "Yes",
29881             no : "No"
29882         }
29883     };
29884 }();
29885
29886 /**
29887  * Shorthand for {@link Roo.MessageBox}
29888  */
29889 Roo.Msg = Roo.MessageBox;/*
29890  * Based on:
29891  * Ext JS Library 1.1.1
29892  * Copyright(c) 2006-2007, Ext JS, LLC.
29893  *
29894  * Originally Released Under LGPL - original licence link has changed is not relivant.
29895  *
29896  * Fork - LGPL
29897  * <script type="text/javascript">
29898  */
29899 /**
29900  * @class Roo.QuickTips
29901  * Provides attractive and customizable tooltips for any element.
29902  * @singleton
29903  */
29904 Roo.QuickTips = function(){
29905     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
29906     var ce, bd, xy, dd;
29907     var visible = false, disabled = true, inited = false;
29908     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
29909     
29910     var onOver = function(e){
29911         if(disabled){
29912             return;
29913         }
29914         var t = e.getTarget();
29915         if(!t || t.nodeType !== 1 || t == document || t == document.body){
29916             return;
29917         }
29918         if(ce && t == ce.el){
29919             clearTimeout(hideProc);
29920             return;
29921         }
29922         if(t && tagEls[t.id]){
29923             tagEls[t.id].el = t;
29924             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
29925             return;
29926         }
29927         var ttp, et = Roo.fly(t);
29928         var ns = cfg.namespace;
29929         if(tm.interceptTitles && t.title){
29930             ttp = t.title;
29931             t.qtip = ttp;
29932             t.removeAttribute("title");
29933             e.preventDefault();
29934         }else{
29935             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
29936         }
29937         if(ttp){
29938             showProc = show.defer(tm.showDelay, tm, [{
29939                 el: t, 
29940                 text: ttp, 
29941                 width: et.getAttributeNS(ns, cfg.width),
29942                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
29943                 title: et.getAttributeNS(ns, cfg.title),
29944                     cls: et.getAttributeNS(ns, cfg.cls)
29945             }]);
29946         }
29947     };
29948     
29949     var onOut = function(e){
29950         clearTimeout(showProc);
29951         var t = e.getTarget();
29952         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
29953             hideProc = setTimeout(hide, tm.hideDelay);
29954         }
29955     };
29956     
29957     var onMove = function(e){
29958         if(disabled){
29959             return;
29960         }
29961         xy = e.getXY();
29962         xy[1] += 18;
29963         if(tm.trackMouse && ce){
29964             el.setXY(xy);
29965         }
29966     };
29967     
29968     var onDown = function(e){
29969         clearTimeout(showProc);
29970         clearTimeout(hideProc);
29971         if(!e.within(el)){
29972             if(tm.hideOnClick){
29973                 hide();
29974                 tm.disable();
29975                 tm.enable.defer(100, tm);
29976             }
29977         }
29978     };
29979     
29980     var getPad = function(){
29981         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
29982     };
29983
29984     var show = function(o){
29985         if(disabled){
29986             return;
29987         }
29988         clearTimeout(dismissProc);
29989         ce = o;
29990         if(removeCls){ // in case manually hidden
29991             el.removeClass(removeCls);
29992             removeCls = null;
29993         }
29994         if(ce.cls){
29995             el.addClass(ce.cls);
29996             removeCls = ce.cls;
29997         }
29998         if(ce.title){
29999             tipTitle.update(ce.title);
30000             tipTitle.show();
30001         }else{
30002             tipTitle.update('');
30003             tipTitle.hide();
30004         }
30005         el.dom.style.width  = tm.maxWidth+'px';
30006         //tipBody.dom.style.width = '';
30007         tipBodyText.update(o.text);
30008         var p = getPad(), w = ce.width;
30009         if(!w){
30010             var td = tipBodyText.dom;
30011             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30012             if(aw > tm.maxWidth){
30013                 w = tm.maxWidth;
30014             }else if(aw < tm.minWidth){
30015                 w = tm.minWidth;
30016             }else{
30017                 w = aw;
30018             }
30019         }
30020         //tipBody.setWidth(w);
30021         el.setWidth(parseInt(w, 10) + p);
30022         if(ce.autoHide === false){
30023             close.setDisplayed(true);
30024             if(dd){
30025                 dd.unlock();
30026             }
30027         }else{
30028             close.setDisplayed(false);
30029             if(dd){
30030                 dd.lock();
30031             }
30032         }
30033         if(xy){
30034             el.avoidY = xy[1]-18;
30035             el.setXY(xy);
30036         }
30037         if(tm.animate){
30038             el.setOpacity(.1);
30039             el.setStyle("visibility", "visible");
30040             el.fadeIn({callback: afterShow});
30041         }else{
30042             afterShow();
30043         }
30044     };
30045     
30046     var afterShow = function(){
30047         if(ce){
30048             el.show();
30049             esc.enable();
30050             if(tm.autoDismiss && ce.autoHide !== false){
30051                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30052             }
30053         }
30054     };
30055     
30056     var hide = function(noanim){
30057         clearTimeout(dismissProc);
30058         clearTimeout(hideProc);
30059         ce = null;
30060         if(el.isVisible()){
30061             esc.disable();
30062             if(noanim !== true && tm.animate){
30063                 el.fadeOut({callback: afterHide});
30064             }else{
30065                 afterHide();
30066             } 
30067         }
30068     };
30069     
30070     var afterHide = function(){
30071         el.hide();
30072         if(removeCls){
30073             el.removeClass(removeCls);
30074             removeCls = null;
30075         }
30076     };
30077     
30078     return {
30079         /**
30080         * @cfg {Number} minWidth
30081         * The minimum width of the quick tip (defaults to 40)
30082         */
30083        minWidth : 40,
30084         /**
30085         * @cfg {Number} maxWidth
30086         * The maximum width of the quick tip (defaults to 300)
30087         */
30088        maxWidth : 300,
30089         /**
30090         * @cfg {Boolean} interceptTitles
30091         * True to automatically use the element's DOM title value if available (defaults to false)
30092         */
30093        interceptTitles : false,
30094         /**
30095         * @cfg {Boolean} trackMouse
30096         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30097         */
30098        trackMouse : false,
30099         /**
30100         * @cfg {Boolean} hideOnClick
30101         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30102         */
30103        hideOnClick : true,
30104         /**
30105         * @cfg {Number} showDelay
30106         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30107         */
30108        showDelay : 500,
30109         /**
30110         * @cfg {Number} hideDelay
30111         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30112         */
30113        hideDelay : 200,
30114         /**
30115         * @cfg {Boolean} autoHide
30116         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30117         * Used in conjunction with hideDelay.
30118         */
30119        autoHide : true,
30120         /**
30121         * @cfg {Boolean}
30122         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30123         * (defaults to true).  Used in conjunction with autoDismissDelay.
30124         */
30125        autoDismiss : true,
30126         /**
30127         * @cfg {Number}
30128         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30129         */
30130        autoDismissDelay : 5000,
30131        /**
30132         * @cfg {Boolean} animate
30133         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30134         */
30135        animate : false,
30136
30137        /**
30138         * @cfg {String} title
30139         * Title text to display (defaults to '').  This can be any valid HTML markup.
30140         */
30141         title: '',
30142        /**
30143         * @cfg {String} text
30144         * Body text to display (defaults to '').  This can be any valid HTML markup.
30145         */
30146         text : '',
30147        /**
30148         * @cfg {String} cls
30149         * A CSS class to apply to the base quick tip element (defaults to '').
30150         */
30151         cls : '',
30152        /**
30153         * @cfg {Number} width
30154         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30155         * minWidth or maxWidth.
30156         */
30157         width : null,
30158
30159     /**
30160      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30161      * or display QuickTips in a page.
30162      */
30163        init : function(){
30164           tm = Roo.QuickTips;
30165           cfg = tm.tagConfig;
30166           if(!inited){
30167               if(!Roo.isReady){ // allow calling of init() before onReady
30168                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30169                   return;
30170               }
30171               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30172               el.fxDefaults = {stopFx: true};
30173               // maximum custom styling
30174               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30175               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30176               tipTitle = el.child('h3');
30177               tipTitle.enableDisplayMode("block");
30178               tipBody = el.child('div.x-tip-bd');
30179               tipBodyText = el.child('div.x-tip-bd-inner');
30180               //bdLeft = el.child('div.x-tip-bd-left');
30181               //bdRight = el.child('div.x-tip-bd-right');
30182               close = el.child('div.x-tip-close');
30183               close.enableDisplayMode("block");
30184               close.on("click", hide);
30185               var d = Roo.get(document);
30186               d.on("mousedown", onDown);
30187               d.on("mouseover", onOver);
30188               d.on("mouseout", onOut);
30189               d.on("mousemove", onMove);
30190               esc = d.addKeyListener(27, hide);
30191               esc.disable();
30192               if(Roo.dd.DD){
30193                   dd = el.initDD("default", null, {
30194                       onDrag : function(){
30195                           el.sync();  
30196                       }
30197                   });
30198                   dd.setHandleElId(tipTitle.id);
30199                   dd.lock();
30200               }
30201               inited = true;
30202           }
30203           this.enable(); 
30204        },
30205
30206     /**
30207      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30208      * are supported:
30209      * <pre>
30210 Property    Type                   Description
30211 ----------  ---------------------  ------------------------------------------------------------------------
30212 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30213      * </ul>
30214      * @param {Object} config The config object
30215      */
30216        register : function(config){
30217            var cs = config instanceof Array ? config : arguments;
30218            for(var i = 0, len = cs.length; i < len; i++) {
30219                var c = cs[i];
30220                var target = c.target;
30221                if(target){
30222                    if(target instanceof Array){
30223                        for(var j = 0, jlen = target.length; j < jlen; j++){
30224                            tagEls[target[j]] = c;
30225                        }
30226                    }else{
30227                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30228                    }
30229                }
30230            }
30231        },
30232
30233     /**
30234      * Removes this quick tip from its element and destroys it.
30235      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30236      */
30237        unregister : function(el){
30238            delete tagEls[Roo.id(el)];
30239        },
30240
30241     /**
30242      * Enable this quick tip.
30243      */
30244        enable : function(){
30245            if(inited && disabled){
30246                locks.pop();
30247                if(locks.length < 1){
30248                    disabled = false;
30249                }
30250            }
30251        },
30252
30253     /**
30254      * Disable this quick tip.
30255      */
30256        disable : function(){
30257           disabled = true;
30258           clearTimeout(showProc);
30259           clearTimeout(hideProc);
30260           clearTimeout(dismissProc);
30261           if(ce){
30262               hide(true);
30263           }
30264           locks.push(1);
30265        },
30266
30267     /**
30268      * Returns true if the quick tip is enabled, else false.
30269      */
30270        isEnabled : function(){
30271             return !disabled;
30272        },
30273
30274         // private
30275        tagConfig : {
30276            namespace : "ext",
30277            attribute : "qtip",
30278            width : "width",
30279            target : "target",
30280            title : "qtitle",
30281            hide : "hide",
30282            cls : "qclass"
30283        }
30284    };
30285 }();
30286
30287 // backwards compat
30288 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30289  * Based on:
30290  * Ext JS Library 1.1.1
30291  * Copyright(c) 2006-2007, Ext JS, LLC.
30292  *
30293  * Originally Released Under LGPL - original licence link has changed is not relivant.
30294  *
30295  * Fork - LGPL
30296  * <script type="text/javascript">
30297  */
30298  
30299
30300 /**
30301  * @class Roo.tree.TreePanel
30302  * @extends Roo.data.Tree
30303
30304  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30305  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30306  * @cfg {Boolean} enableDD true to enable drag and drop
30307  * @cfg {Boolean} enableDrag true to enable just drag
30308  * @cfg {Boolean} enableDrop true to enable just drop
30309  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30310  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30311  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30312  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30313  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30314  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30315  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30316  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30317  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30318  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30319  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30320  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30321  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30322  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30323  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30324  * 
30325  * @constructor
30326  * @param {String/HTMLElement/Element} el The container element
30327  * @param {Object} config
30328  */
30329 Roo.tree.TreePanel = function(el, config){
30330     var root = false;
30331     var loader = false;
30332     if (config.root) {
30333         root = config.root;
30334         delete config.root;
30335     }
30336     if (config.loader) {
30337         loader = config.loader;
30338         delete config.loader;
30339     }
30340     
30341     Roo.apply(this, config);
30342     Roo.tree.TreePanel.superclass.constructor.call(this);
30343     this.el = Roo.get(el);
30344     this.el.addClass('x-tree');
30345     //console.log(root);
30346     if (root) {
30347         this.setRootNode( Roo.factory(root, Roo.tree));
30348     }
30349     if (loader) {
30350         this.loader = Roo.factory(loader, Roo.tree);
30351     }
30352    /**
30353     * Read-only. The id of the container element becomes this TreePanel's id.
30354     */
30355    this.id = this.el.id;
30356    this.addEvents({
30357         /**
30358         * @event beforeload
30359         * Fires before a node is loaded, return false to cancel
30360         * @param {Node} node The node being loaded
30361         */
30362         "beforeload" : true,
30363         /**
30364         * @event load
30365         * Fires when a node is loaded
30366         * @param {Node} node The node that was loaded
30367         */
30368         "load" : true,
30369         /**
30370         * @event textchange
30371         * Fires when the text for a node is changed
30372         * @param {Node} node The node
30373         * @param {String} text The new text
30374         * @param {String} oldText The old text
30375         */
30376         "textchange" : true,
30377         /**
30378         * @event beforeexpand
30379         * Fires before a node is expanded, return false to cancel.
30380         * @param {Node} node The node
30381         * @param {Boolean} deep
30382         * @param {Boolean} anim
30383         */
30384         "beforeexpand" : true,
30385         /**
30386         * @event beforecollapse
30387         * Fires before a node is collapsed, return false to cancel.
30388         * @param {Node} node The node
30389         * @param {Boolean} deep
30390         * @param {Boolean} anim
30391         */
30392         "beforecollapse" : true,
30393         /**
30394         * @event expand
30395         * Fires when a node is expanded
30396         * @param {Node} node The node
30397         */
30398         "expand" : true,
30399         /**
30400         * @event disabledchange
30401         * Fires when the disabled status of a node changes
30402         * @param {Node} node The node
30403         * @param {Boolean} disabled
30404         */
30405         "disabledchange" : true,
30406         /**
30407         * @event collapse
30408         * Fires when a node is collapsed
30409         * @param {Node} node The node
30410         */
30411         "collapse" : true,
30412         /**
30413         * @event beforeclick
30414         * Fires before click processing on a node. Return false to cancel the default action.
30415         * @param {Node} node The node
30416         * @param {Roo.EventObject} e The event object
30417         */
30418         "beforeclick":true,
30419         /**
30420         * @event checkchange
30421         * Fires when a node with a checkbox's checked property changes
30422         * @param {Node} this This node
30423         * @param {Boolean} checked
30424         */
30425         "checkchange":true,
30426         /**
30427         * @event click
30428         * Fires when a node is clicked
30429         * @param {Node} node The node
30430         * @param {Roo.EventObject} e The event object
30431         */
30432         "click":true,
30433         /**
30434         * @event dblclick
30435         * Fires when a node is double clicked
30436         * @param {Node} node The node
30437         * @param {Roo.EventObject} e The event object
30438         */
30439         "dblclick":true,
30440         /**
30441         * @event contextmenu
30442         * Fires when a node is right clicked
30443         * @param {Node} node The node
30444         * @param {Roo.EventObject} e The event object
30445         */
30446         "contextmenu":true,
30447         /**
30448         * @event beforechildrenrendered
30449         * Fires right before the child nodes for a node are rendered
30450         * @param {Node} node The node
30451         */
30452         "beforechildrenrendered":true,
30453        /**
30454              * @event startdrag
30455              * Fires when a node starts being dragged
30456              * @param {Roo.tree.TreePanel} this
30457              * @param {Roo.tree.TreeNode} node
30458              * @param {event} e The raw browser event
30459              */ 
30460             "startdrag" : true,
30461             /**
30462              * @event enddrag
30463              * Fires when a drag operation is complete
30464              * @param {Roo.tree.TreePanel} this
30465              * @param {Roo.tree.TreeNode} node
30466              * @param {event} e The raw browser event
30467              */
30468             "enddrag" : true,
30469             /**
30470              * @event dragdrop
30471              * Fires when a dragged node is dropped on a valid DD target
30472              * @param {Roo.tree.TreePanel} this
30473              * @param {Roo.tree.TreeNode} node
30474              * @param {DD} dd The dd it was dropped on
30475              * @param {event} e The raw browser event
30476              */
30477             "dragdrop" : true,
30478             /**
30479              * @event beforenodedrop
30480              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30481              * passed to handlers has the following properties:<br />
30482              * <ul style="padding:5px;padding-left:16px;">
30483              * <li>tree - The TreePanel</li>
30484              * <li>target - The node being targeted for the drop</li>
30485              * <li>data - The drag data from the drag source</li>
30486              * <li>point - The point of the drop - append, above or below</li>
30487              * <li>source - The drag source</li>
30488              * <li>rawEvent - Raw mouse event</li>
30489              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30490              * to be inserted by setting them on this object.</li>
30491              * <li>cancel - Set this to true to cancel the drop.</li>
30492              * </ul>
30493              * @param {Object} dropEvent
30494              */
30495             "beforenodedrop" : true,
30496             /**
30497              * @event nodedrop
30498              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30499              * passed to handlers has the following properties:<br />
30500              * <ul style="padding:5px;padding-left:16px;">
30501              * <li>tree - The TreePanel</li>
30502              * <li>target - The node being targeted for the drop</li>
30503              * <li>data - The drag data from the drag source</li>
30504              * <li>point - The point of the drop - append, above or below</li>
30505              * <li>source - The drag source</li>
30506              * <li>rawEvent - Raw mouse event</li>
30507              * <li>dropNode - Dropped node(s).</li>
30508              * </ul>
30509              * @param {Object} dropEvent
30510              */
30511             "nodedrop" : true,
30512              /**
30513              * @event nodedragover
30514              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30515              * passed to handlers has the following properties:<br />
30516              * <ul style="padding:5px;padding-left:16px;">
30517              * <li>tree - The TreePanel</li>
30518              * <li>target - The node being targeted for the drop</li>
30519              * <li>data - The drag data from the drag source</li>
30520              * <li>point - The point of the drop - append, above or below</li>
30521              * <li>source - The drag source</li>
30522              * <li>rawEvent - Raw mouse event</li>
30523              * <li>dropNode - Drop node(s) provided by the source.</li>
30524              * <li>cancel - Set this to true to signal drop not allowed.</li>
30525              * </ul>
30526              * @param {Object} dragOverEvent
30527              */
30528             "nodedragover" : true
30529         
30530    });
30531    if(this.singleExpand){
30532        this.on("beforeexpand", this.restrictExpand, this);
30533    }
30534 };
30535 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30536     rootVisible : true,
30537     animate: Roo.enableFx,
30538     lines : true,
30539     enableDD : false,
30540     hlDrop : Roo.enableFx,
30541   
30542     renderer: false,
30543     
30544     rendererTip: false,
30545     // private
30546     restrictExpand : function(node){
30547         var p = node.parentNode;
30548         if(p){
30549             if(p.expandedChild && p.expandedChild.parentNode == p){
30550                 p.expandedChild.collapse();
30551             }
30552             p.expandedChild = node;
30553         }
30554     },
30555
30556     // private override
30557     setRootNode : function(node){
30558         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30559         if(!this.rootVisible){
30560             node.ui = new Roo.tree.RootTreeNodeUI(node);
30561         }
30562         return node;
30563     },
30564
30565     /**
30566      * Returns the container element for this TreePanel
30567      */
30568     getEl : function(){
30569         return this.el;
30570     },
30571
30572     /**
30573      * Returns the default TreeLoader for this TreePanel
30574      */
30575     getLoader : function(){
30576         return this.loader;
30577     },
30578
30579     /**
30580      * Expand all nodes
30581      */
30582     expandAll : function(){
30583         this.root.expand(true);
30584     },
30585
30586     /**
30587      * Collapse all nodes
30588      */
30589     collapseAll : function(){
30590         this.root.collapse(true);
30591     },
30592
30593     /**
30594      * Returns the selection model used by this TreePanel
30595      */
30596     getSelectionModel : function(){
30597         if(!this.selModel){
30598             this.selModel = new Roo.tree.DefaultSelectionModel();
30599         }
30600         return this.selModel;
30601     },
30602
30603     /**
30604      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30605      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30606      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30607      * @return {Array}
30608      */
30609     getChecked : function(a, startNode){
30610         startNode = startNode || this.root;
30611         var r = [];
30612         var f = function(){
30613             if(this.attributes.checked){
30614                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30615             }
30616         }
30617         startNode.cascade(f);
30618         return r;
30619     },
30620
30621     /**
30622      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30623      * @param {String} path
30624      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30625      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30626      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30627      */
30628     expandPath : function(path, attr, callback){
30629         attr = attr || "id";
30630         var keys = path.split(this.pathSeparator);
30631         var curNode = this.root;
30632         if(curNode.attributes[attr] != keys[1]){ // invalid root
30633             if(callback){
30634                 callback(false, null);
30635             }
30636             return;
30637         }
30638         var index = 1;
30639         var f = function(){
30640             if(++index == keys.length){
30641                 if(callback){
30642                     callback(true, curNode);
30643                 }
30644                 return;
30645             }
30646             var c = curNode.findChild(attr, keys[index]);
30647             if(!c){
30648                 if(callback){
30649                     callback(false, curNode);
30650                 }
30651                 return;
30652             }
30653             curNode = c;
30654             c.expand(false, false, f);
30655         };
30656         curNode.expand(false, false, f);
30657     },
30658
30659     /**
30660      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30661      * @param {String} path
30662      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30663      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30664      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30665      */
30666     selectPath : function(path, attr, callback){
30667         attr = attr || "id";
30668         var keys = path.split(this.pathSeparator);
30669         var v = keys.pop();
30670         if(keys.length > 0){
30671             var f = function(success, node){
30672                 if(success && node){
30673                     var n = node.findChild(attr, v);
30674                     if(n){
30675                         n.select();
30676                         if(callback){
30677                             callback(true, n);
30678                         }
30679                     }else if(callback){
30680                         callback(false, n);
30681                     }
30682                 }else{
30683                     if(callback){
30684                         callback(false, n);
30685                     }
30686                 }
30687             };
30688             this.expandPath(keys.join(this.pathSeparator), attr, f);
30689         }else{
30690             this.root.select();
30691             if(callback){
30692                 callback(true, this.root);
30693             }
30694         }
30695     },
30696
30697     getTreeEl : function(){
30698         return this.el;
30699     },
30700
30701     /**
30702      * Trigger rendering of this TreePanel
30703      */
30704     render : function(){
30705         if (this.innerCt) {
30706             return this; // stop it rendering more than once!!
30707         }
30708         
30709         this.innerCt = this.el.createChild({tag:"ul",
30710                cls:"x-tree-root-ct " +
30711                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30712
30713         if(this.containerScroll){
30714             Roo.dd.ScrollManager.register(this.el);
30715         }
30716         if((this.enableDD || this.enableDrop) && !this.dropZone){
30717            /**
30718             * The dropZone used by this tree if drop is enabled
30719             * @type Roo.tree.TreeDropZone
30720             */
30721              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30722                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30723            });
30724         }
30725         if((this.enableDD || this.enableDrag) && !this.dragZone){
30726            /**
30727             * The dragZone used by this tree if drag is enabled
30728             * @type Roo.tree.TreeDragZone
30729             */
30730             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30731                ddGroup: this.ddGroup || "TreeDD",
30732                scroll: this.ddScroll
30733            });
30734         }
30735         this.getSelectionModel().init(this);
30736         if (!this.root) {
30737             console.log("ROOT not set in tree");
30738             return;
30739         }
30740         this.root.render();
30741         if(!this.rootVisible){
30742             this.root.renderChildren();
30743         }
30744         return this;
30745     }
30746 });/*
30747  * Based on:
30748  * Ext JS Library 1.1.1
30749  * Copyright(c) 2006-2007, Ext JS, LLC.
30750  *
30751  * Originally Released Under LGPL - original licence link has changed is not relivant.
30752  *
30753  * Fork - LGPL
30754  * <script type="text/javascript">
30755  */
30756  
30757
30758 /**
30759  * @class Roo.tree.DefaultSelectionModel
30760  * @extends Roo.util.Observable
30761  * The default single selection for a TreePanel.
30762  */
30763 Roo.tree.DefaultSelectionModel = function(){
30764    this.selNode = null;
30765    
30766    this.addEvents({
30767        /**
30768         * @event selectionchange
30769         * Fires when the selected node changes
30770         * @param {DefaultSelectionModel} this
30771         * @param {TreeNode} node the new selection
30772         */
30773        "selectionchange" : true,
30774
30775        /**
30776         * @event beforeselect
30777         * Fires before the selected node changes, return false to cancel the change
30778         * @param {DefaultSelectionModel} this
30779         * @param {TreeNode} node the new selection
30780         * @param {TreeNode} node the old selection
30781         */
30782        "beforeselect" : true
30783    });
30784 };
30785
30786 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30787     init : function(tree){
30788         this.tree = tree;
30789         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30790         tree.on("click", this.onNodeClick, this);
30791     },
30792     
30793     onNodeClick : function(node, e){
30794         if (e.ctrlKey && this.selNode == node)  {
30795             this.unselect(node);
30796             return;
30797         }
30798         this.select(node);
30799     },
30800     
30801     /**
30802      * Select a node.
30803      * @param {TreeNode} node The node to select
30804      * @return {TreeNode} The selected node
30805      */
30806     select : function(node){
30807         var last = this.selNode;
30808         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30809             if(last){
30810                 last.ui.onSelectedChange(false);
30811             }
30812             this.selNode = node;
30813             node.ui.onSelectedChange(true);
30814             this.fireEvent("selectionchange", this, node, last);
30815         }
30816         return node;
30817     },
30818     
30819     /**
30820      * Deselect a node.
30821      * @param {TreeNode} node The node to unselect
30822      */
30823     unselect : function(node){
30824         if(this.selNode == node){
30825             this.clearSelections();
30826         }    
30827     },
30828     
30829     /**
30830      * Clear all selections
30831      */
30832     clearSelections : function(){
30833         var n = this.selNode;
30834         if(n){
30835             n.ui.onSelectedChange(false);
30836             this.selNode = null;
30837             this.fireEvent("selectionchange", this, null);
30838         }
30839         return n;
30840     },
30841     
30842     /**
30843      * Get the selected node
30844      * @return {TreeNode} The selected node
30845      */
30846     getSelectedNode : function(){
30847         return this.selNode;    
30848     },
30849     
30850     /**
30851      * Returns true if the node is selected
30852      * @param {TreeNode} node The node to check
30853      * @return {Boolean}
30854      */
30855     isSelected : function(node){
30856         return this.selNode == node;  
30857     },
30858
30859     /**
30860      * Selects the node above the selected node in the tree, intelligently walking the nodes
30861      * @return TreeNode The new selection
30862      */
30863     selectPrevious : function(){
30864         var s = this.selNode || this.lastSelNode;
30865         if(!s){
30866             return null;
30867         }
30868         var ps = s.previousSibling;
30869         if(ps){
30870             if(!ps.isExpanded() || ps.childNodes.length < 1){
30871                 return this.select(ps);
30872             } else{
30873                 var lc = ps.lastChild;
30874                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
30875                     lc = lc.lastChild;
30876                 }
30877                 return this.select(lc);
30878             }
30879         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
30880             return this.select(s.parentNode);
30881         }
30882         return null;
30883     },
30884
30885     /**
30886      * Selects the node above the selected node in the tree, intelligently walking the nodes
30887      * @return TreeNode The new selection
30888      */
30889     selectNext : function(){
30890         var s = this.selNode || this.lastSelNode;
30891         if(!s){
30892             return null;
30893         }
30894         if(s.firstChild && s.isExpanded()){
30895              return this.select(s.firstChild);
30896          }else if(s.nextSibling){
30897              return this.select(s.nextSibling);
30898          }else if(s.parentNode){
30899             var newS = null;
30900             s.parentNode.bubble(function(){
30901                 if(this.nextSibling){
30902                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
30903                     return false;
30904                 }
30905             });
30906             return newS;
30907          }
30908         return null;
30909     },
30910
30911     onKeyDown : function(e){
30912         var s = this.selNode || this.lastSelNode;
30913         // undesirable, but required
30914         var sm = this;
30915         if(!s){
30916             return;
30917         }
30918         var k = e.getKey();
30919         switch(k){
30920              case e.DOWN:
30921                  e.stopEvent();
30922                  this.selectNext();
30923              break;
30924              case e.UP:
30925                  e.stopEvent();
30926                  this.selectPrevious();
30927              break;
30928              case e.RIGHT:
30929                  e.preventDefault();
30930                  if(s.hasChildNodes()){
30931                      if(!s.isExpanded()){
30932                          s.expand();
30933                      }else if(s.firstChild){
30934                          this.select(s.firstChild, e);
30935                      }
30936                  }
30937              break;
30938              case e.LEFT:
30939                  e.preventDefault();
30940                  if(s.hasChildNodes() && s.isExpanded()){
30941                      s.collapse();
30942                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
30943                      this.select(s.parentNode, e);
30944                  }
30945              break;
30946         };
30947     }
30948 });
30949
30950 /**
30951  * @class Roo.tree.MultiSelectionModel
30952  * @extends Roo.util.Observable
30953  * Multi selection for a TreePanel.
30954  */
30955 Roo.tree.MultiSelectionModel = function(){
30956    this.selNodes = [];
30957    this.selMap = {};
30958    this.addEvents({
30959        /**
30960         * @event selectionchange
30961         * Fires when the selected nodes change
30962         * @param {MultiSelectionModel} this
30963         * @param {Array} nodes Array of the selected nodes
30964         */
30965        "selectionchange" : true
30966    });
30967 };
30968
30969 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
30970     init : function(tree){
30971         this.tree = tree;
30972         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30973         tree.on("click", this.onNodeClick, this);
30974     },
30975     
30976     onNodeClick : function(node, e){
30977         this.select(node, e, e.ctrlKey);
30978     },
30979     
30980     /**
30981      * Select a node.
30982      * @param {TreeNode} node The node to select
30983      * @param {EventObject} e (optional) An event associated with the selection
30984      * @param {Boolean} keepExisting True to retain existing selections
30985      * @return {TreeNode} The selected node
30986      */
30987     select : function(node, e, keepExisting){
30988         if(keepExisting !== true){
30989             this.clearSelections(true);
30990         }
30991         if(this.isSelected(node)){
30992             this.lastSelNode = node;
30993             return node;
30994         }
30995         this.selNodes.push(node);
30996         this.selMap[node.id] = node;
30997         this.lastSelNode = node;
30998         node.ui.onSelectedChange(true);
30999         this.fireEvent("selectionchange", this, this.selNodes);
31000         return node;
31001     },
31002     
31003     /**
31004      * Deselect a node.
31005      * @param {TreeNode} node The node to unselect
31006      */
31007     unselect : function(node){
31008         if(this.selMap[node.id]){
31009             node.ui.onSelectedChange(false);
31010             var sn = this.selNodes;
31011             var index = -1;
31012             if(sn.indexOf){
31013                 index = sn.indexOf(node);
31014             }else{
31015                 for(var i = 0, len = sn.length; i < len; i++){
31016                     if(sn[i] == node){
31017                         index = i;
31018                         break;
31019                     }
31020                 }
31021             }
31022             if(index != -1){
31023                 this.selNodes.splice(index, 1);
31024             }
31025             delete this.selMap[node.id];
31026             this.fireEvent("selectionchange", this, this.selNodes);
31027         }
31028     },
31029     
31030     /**
31031      * Clear all selections
31032      */
31033     clearSelections : function(suppressEvent){
31034         var sn = this.selNodes;
31035         if(sn.length > 0){
31036             for(var i = 0, len = sn.length; i < len; i++){
31037                 sn[i].ui.onSelectedChange(false);
31038             }
31039             this.selNodes = [];
31040             this.selMap = {};
31041             if(suppressEvent !== true){
31042                 this.fireEvent("selectionchange", this, this.selNodes);
31043             }
31044         }
31045     },
31046     
31047     /**
31048      * Returns true if the node is selected
31049      * @param {TreeNode} node The node to check
31050      * @return {Boolean}
31051      */
31052     isSelected : function(node){
31053         return this.selMap[node.id] ? true : false;  
31054     },
31055     
31056     /**
31057      * Returns an array of the selected nodes
31058      * @return {Array}
31059      */
31060     getSelectedNodes : function(){
31061         return this.selNodes;    
31062     },
31063
31064     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31065
31066     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31067
31068     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31069 });/*
31070  * Based on:
31071  * Ext JS Library 1.1.1
31072  * Copyright(c) 2006-2007, Ext JS, LLC.
31073  *
31074  * Originally Released Under LGPL - original licence link has changed is not relivant.
31075  *
31076  * Fork - LGPL
31077  * <script type="text/javascript">
31078  */
31079  
31080 /**
31081  * @class Roo.tree.TreeNode
31082  * @extends Roo.data.Node
31083  * @cfg {String} text The text for this node
31084  * @cfg {Boolean} expanded true to start the node expanded
31085  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31086  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31087  * @cfg {Boolean} disabled true to start the node disabled
31088  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31089  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31090  * @cfg {String} cls A css class to be added to the node
31091  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31092  * @cfg {String} href URL of the link used for the node (defaults to #)
31093  * @cfg {String} hrefTarget target frame for the link
31094  * @cfg {String} qtip An Ext QuickTip for the node
31095  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31096  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31097  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31098  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31099  * (defaults to undefined with no checkbox rendered)
31100  * @constructor
31101  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31102  */
31103 Roo.tree.TreeNode = function(attributes){
31104     attributes = attributes || {};
31105     if(typeof attributes == "string"){
31106         attributes = {text: attributes};
31107     }
31108     this.childrenRendered = false;
31109     this.rendered = false;
31110     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31111     this.expanded = attributes.expanded === true;
31112     this.isTarget = attributes.isTarget !== false;
31113     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31114     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31115
31116     /**
31117      * Read-only. The text for this node. To change it use setText().
31118      * @type String
31119      */
31120     this.text = attributes.text;
31121     /**
31122      * True if this node is disabled.
31123      * @type Boolean
31124      */
31125     this.disabled = attributes.disabled === true;
31126
31127     this.addEvents({
31128         /**
31129         * @event textchange
31130         * Fires when the text for this node is changed
31131         * @param {Node} this This node
31132         * @param {String} text The new text
31133         * @param {String} oldText The old text
31134         */
31135         "textchange" : true,
31136         /**
31137         * @event beforeexpand
31138         * Fires before this node is expanded, return false to cancel.
31139         * @param {Node} this This node
31140         * @param {Boolean} deep
31141         * @param {Boolean} anim
31142         */
31143         "beforeexpand" : true,
31144         /**
31145         * @event beforecollapse
31146         * Fires before this node is collapsed, return false to cancel.
31147         * @param {Node} this This node
31148         * @param {Boolean} deep
31149         * @param {Boolean} anim
31150         */
31151         "beforecollapse" : true,
31152         /**
31153         * @event expand
31154         * Fires when this node is expanded
31155         * @param {Node} this This node
31156         */
31157         "expand" : true,
31158         /**
31159         * @event disabledchange
31160         * Fires when the disabled status of this node changes
31161         * @param {Node} this This node
31162         * @param {Boolean} disabled
31163         */
31164         "disabledchange" : true,
31165         /**
31166         * @event collapse
31167         * Fires when this node is collapsed
31168         * @param {Node} this This node
31169         */
31170         "collapse" : true,
31171         /**
31172         * @event beforeclick
31173         * Fires before click processing. Return false to cancel the default action.
31174         * @param {Node} this This node
31175         * @param {Roo.EventObject} e The event object
31176         */
31177         "beforeclick":true,
31178         /**
31179         * @event checkchange
31180         * Fires when a node with a checkbox's checked property changes
31181         * @param {Node} this This node
31182         * @param {Boolean} checked
31183         */
31184         "checkchange":true,
31185         /**
31186         * @event click
31187         * Fires when this node is clicked
31188         * @param {Node} this This node
31189         * @param {Roo.EventObject} e The event object
31190         */
31191         "click":true,
31192         /**
31193         * @event dblclick
31194         * Fires when this node is double clicked
31195         * @param {Node} this This node
31196         * @param {Roo.EventObject} e The event object
31197         */
31198         "dblclick":true,
31199         /**
31200         * @event contextmenu
31201         * Fires when this node is right clicked
31202         * @param {Node} this This node
31203         * @param {Roo.EventObject} e The event object
31204         */
31205         "contextmenu":true,
31206         /**
31207         * @event beforechildrenrendered
31208         * Fires right before the child nodes for this node are rendered
31209         * @param {Node} this This node
31210         */
31211         "beforechildrenrendered":true
31212     });
31213
31214     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31215
31216     /**
31217      * Read-only. The UI for this node
31218      * @type TreeNodeUI
31219      */
31220     this.ui = new uiClass(this);
31221 };
31222 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31223     preventHScroll: true,
31224     /**
31225      * Returns true if this node is expanded
31226      * @return {Boolean}
31227      */
31228     isExpanded : function(){
31229         return this.expanded;
31230     },
31231
31232     /**
31233      * Returns the UI object for this node
31234      * @return {TreeNodeUI}
31235      */
31236     getUI : function(){
31237         return this.ui;
31238     },
31239
31240     // private override
31241     setFirstChild : function(node){
31242         var of = this.firstChild;
31243         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31244         if(this.childrenRendered && of && node != of){
31245             of.renderIndent(true, true);
31246         }
31247         if(this.rendered){
31248             this.renderIndent(true, true);
31249         }
31250     },
31251
31252     // private override
31253     setLastChild : function(node){
31254         var ol = this.lastChild;
31255         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31256         if(this.childrenRendered && ol && node != ol){
31257             ol.renderIndent(true, true);
31258         }
31259         if(this.rendered){
31260             this.renderIndent(true, true);
31261         }
31262     },
31263
31264     // these methods are overridden to provide lazy rendering support
31265     // private override
31266     appendChild : function(){
31267         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31268         if(node && this.childrenRendered){
31269             node.render();
31270         }
31271         this.ui.updateExpandIcon();
31272         return node;
31273     },
31274
31275     // private override
31276     removeChild : function(node){
31277         this.ownerTree.getSelectionModel().unselect(node);
31278         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31279         // if it's been rendered remove dom node
31280         if(this.childrenRendered){
31281             node.ui.remove();
31282         }
31283         if(this.childNodes.length < 1){
31284             this.collapse(false, false);
31285         }else{
31286             this.ui.updateExpandIcon();
31287         }
31288         if(!this.firstChild) {
31289             this.childrenRendered = false;
31290         }
31291         return node;
31292     },
31293
31294     // private override
31295     insertBefore : function(node, refNode){
31296         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31297         if(newNode && refNode && this.childrenRendered){
31298             node.render();
31299         }
31300         this.ui.updateExpandIcon();
31301         return newNode;
31302     },
31303
31304     /**
31305      * Sets the text for this node
31306      * @param {String} text
31307      */
31308     setText : function(text){
31309         var oldText = this.text;
31310         this.text = text;
31311         this.attributes.text = text;
31312         if(this.rendered){ // event without subscribing
31313             this.ui.onTextChange(this, text, oldText);
31314         }
31315         this.fireEvent("textchange", this, text, oldText);
31316     },
31317
31318     /**
31319      * Triggers selection of this node
31320      */
31321     select : function(){
31322         this.getOwnerTree().getSelectionModel().select(this);
31323     },
31324
31325     /**
31326      * Triggers deselection of this node
31327      */
31328     unselect : function(){
31329         this.getOwnerTree().getSelectionModel().unselect(this);
31330     },
31331
31332     /**
31333      * Returns true if this node is selected
31334      * @return {Boolean}
31335      */
31336     isSelected : function(){
31337         return this.getOwnerTree().getSelectionModel().isSelected(this);
31338     },
31339
31340     /**
31341      * Expand this node.
31342      * @param {Boolean} deep (optional) True to expand all children as well
31343      * @param {Boolean} anim (optional) false to cancel the default animation
31344      * @param {Function} callback (optional) A callback to be called when
31345      * expanding this node completes (does not wait for deep expand to complete).
31346      * Called with 1 parameter, this node.
31347      */
31348     expand : function(deep, anim, callback){
31349         if(!this.expanded){
31350             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31351                 return;
31352             }
31353             if(!this.childrenRendered){
31354                 this.renderChildren();
31355             }
31356             this.expanded = true;
31357             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31358                 this.ui.animExpand(function(){
31359                     this.fireEvent("expand", this);
31360                     if(typeof callback == "function"){
31361                         callback(this);
31362                     }
31363                     if(deep === true){
31364                         this.expandChildNodes(true);
31365                     }
31366                 }.createDelegate(this));
31367                 return;
31368             }else{
31369                 this.ui.expand();
31370                 this.fireEvent("expand", this);
31371                 if(typeof callback == "function"){
31372                     callback(this);
31373                 }
31374             }
31375         }else{
31376            if(typeof callback == "function"){
31377                callback(this);
31378            }
31379         }
31380         if(deep === true){
31381             this.expandChildNodes(true);
31382         }
31383     },
31384
31385     isHiddenRoot : function(){
31386         return this.isRoot && !this.getOwnerTree().rootVisible;
31387     },
31388
31389     /**
31390      * Collapse this node.
31391      * @param {Boolean} deep (optional) True to collapse all children as well
31392      * @param {Boolean} anim (optional) false to cancel the default animation
31393      */
31394     collapse : function(deep, anim){
31395         if(this.expanded && !this.isHiddenRoot()){
31396             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31397                 return;
31398             }
31399             this.expanded = false;
31400             if((this.getOwnerTree().animate && anim !== false) || anim){
31401                 this.ui.animCollapse(function(){
31402                     this.fireEvent("collapse", this);
31403                     if(deep === true){
31404                         this.collapseChildNodes(true);
31405                     }
31406                 }.createDelegate(this));
31407                 return;
31408             }else{
31409                 this.ui.collapse();
31410                 this.fireEvent("collapse", this);
31411             }
31412         }
31413         if(deep === true){
31414             var cs = this.childNodes;
31415             for(var i = 0, len = cs.length; i < len; i++) {
31416                 cs[i].collapse(true, false);
31417             }
31418         }
31419     },
31420
31421     // private
31422     delayedExpand : function(delay){
31423         if(!this.expandProcId){
31424             this.expandProcId = this.expand.defer(delay, this);
31425         }
31426     },
31427
31428     // private
31429     cancelExpand : function(){
31430         if(this.expandProcId){
31431             clearTimeout(this.expandProcId);
31432         }
31433         this.expandProcId = false;
31434     },
31435
31436     /**
31437      * Toggles expanded/collapsed state of the node
31438      */
31439     toggle : function(){
31440         if(this.expanded){
31441             this.collapse();
31442         }else{
31443             this.expand();
31444         }
31445     },
31446
31447     /**
31448      * Ensures all parent nodes are expanded
31449      */
31450     ensureVisible : function(callback){
31451         var tree = this.getOwnerTree();
31452         tree.expandPath(this.parentNode.getPath(), false, function(){
31453             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31454             Roo.callback(callback);
31455         }.createDelegate(this));
31456     },
31457
31458     /**
31459      * Expand all child nodes
31460      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31461      */
31462     expandChildNodes : function(deep){
31463         var cs = this.childNodes;
31464         for(var i = 0, len = cs.length; i < len; i++) {
31465                 cs[i].expand(deep);
31466         }
31467     },
31468
31469     /**
31470      * Collapse all child nodes
31471      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31472      */
31473     collapseChildNodes : function(deep){
31474         var cs = this.childNodes;
31475         for(var i = 0, len = cs.length; i < len; i++) {
31476                 cs[i].collapse(deep);
31477         }
31478     },
31479
31480     /**
31481      * Disables this node
31482      */
31483     disable : function(){
31484         this.disabled = true;
31485         this.unselect();
31486         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31487             this.ui.onDisableChange(this, true);
31488         }
31489         this.fireEvent("disabledchange", this, true);
31490     },
31491
31492     /**
31493      * Enables this node
31494      */
31495     enable : function(){
31496         this.disabled = false;
31497         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31498             this.ui.onDisableChange(this, false);
31499         }
31500         this.fireEvent("disabledchange", this, false);
31501     },
31502
31503     // private
31504     renderChildren : function(suppressEvent){
31505         if(suppressEvent !== false){
31506             this.fireEvent("beforechildrenrendered", this);
31507         }
31508         var cs = this.childNodes;
31509         for(var i = 0, len = cs.length; i < len; i++){
31510             cs[i].render(true);
31511         }
31512         this.childrenRendered = true;
31513     },
31514
31515     // private
31516     sort : function(fn, scope){
31517         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31518         if(this.childrenRendered){
31519             var cs = this.childNodes;
31520             for(var i = 0, len = cs.length; i < len; i++){
31521                 cs[i].render(true);
31522             }
31523         }
31524     },
31525
31526     // private
31527     render : function(bulkRender){
31528         this.ui.render(bulkRender);
31529         if(!this.rendered){
31530             this.rendered = true;
31531             if(this.expanded){
31532                 this.expanded = false;
31533                 this.expand(false, false);
31534             }
31535         }
31536     },
31537
31538     // private
31539     renderIndent : function(deep, refresh){
31540         if(refresh){
31541             this.ui.childIndent = null;
31542         }
31543         this.ui.renderIndent();
31544         if(deep === true && this.childrenRendered){
31545             var cs = this.childNodes;
31546             for(var i = 0, len = cs.length; i < len; i++){
31547                 cs[i].renderIndent(true, refresh);
31548             }
31549         }
31550     }
31551 });/*
31552  * Based on:
31553  * Ext JS Library 1.1.1
31554  * Copyright(c) 2006-2007, Ext JS, LLC.
31555  *
31556  * Originally Released Under LGPL - original licence link has changed is not relivant.
31557  *
31558  * Fork - LGPL
31559  * <script type="text/javascript">
31560  */
31561  
31562 /**
31563  * @class Roo.tree.AsyncTreeNode
31564  * @extends Roo.tree.TreeNode
31565  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31566  * @constructor
31567  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31568  */
31569  Roo.tree.AsyncTreeNode = function(config){
31570     this.loaded = false;
31571     this.loading = false;
31572     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31573     /**
31574     * @event beforeload
31575     * Fires before this node is loaded, return false to cancel
31576     * @param {Node} this This node
31577     */
31578     this.addEvents({'beforeload':true, 'load': true});
31579     /**
31580     * @event load
31581     * Fires when this node is loaded
31582     * @param {Node} this This node
31583     */
31584     /**
31585      * The loader used by this node (defaults to using the tree's defined loader)
31586      * @type TreeLoader
31587      * @property loader
31588      */
31589 };
31590 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31591     expand : function(deep, anim, callback){
31592         if(this.loading){ // if an async load is already running, waiting til it's done
31593             var timer;
31594             var f = function(){
31595                 if(!this.loading){ // done loading
31596                     clearInterval(timer);
31597                     this.expand(deep, anim, callback);
31598                 }
31599             }.createDelegate(this);
31600             timer = setInterval(f, 200);
31601             return;
31602         }
31603         if(!this.loaded){
31604             if(this.fireEvent("beforeload", this) === false){
31605                 return;
31606             }
31607             this.loading = true;
31608             this.ui.beforeLoad(this);
31609             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31610             if(loader){
31611                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31612                 return;
31613             }
31614         }
31615         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31616     },
31617     
31618     /**
31619      * Returns true if this node is currently loading
31620      * @return {Boolean}
31621      */
31622     isLoading : function(){
31623         return this.loading;  
31624     },
31625     
31626     loadComplete : function(deep, anim, callback){
31627         this.loading = false;
31628         this.loaded = true;
31629         this.ui.afterLoad(this);
31630         this.fireEvent("load", this);
31631         this.expand(deep, anim, callback);
31632     },
31633     
31634     /**
31635      * Returns true if this node has been loaded
31636      * @return {Boolean}
31637      */
31638     isLoaded : function(){
31639         return this.loaded;
31640     },
31641     
31642     hasChildNodes : function(){
31643         if(!this.isLeaf() && !this.loaded){
31644             return true;
31645         }else{
31646             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31647         }
31648     },
31649
31650     /**
31651      * Trigger a reload for this node
31652      * @param {Function} callback
31653      */
31654     reload : function(callback){
31655         this.collapse(false, false);
31656         while(this.firstChild){
31657             this.removeChild(this.firstChild);
31658         }
31659         this.childrenRendered = false;
31660         this.loaded = false;
31661         if(this.isHiddenRoot()){
31662             this.expanded = false;
31663         }
31664         this.expand(false, false, callback);
31665     }
31666 });/*
31667  * Based on:
31668  * Ext JS Library 1.1.1
31669  * Copyright(c) 2006-2007, Ext JS, LLC.
31670  *
31671  * Originally Released Under LGPL - original licence link has changed is not relivant.
31672  *
31673  * Fork - LGPL
31674  * <script type="text/javascript">
31675  */
31676  
31677 /**
31678  * @class Roo.tree.TreeNodeUI
31679  * @constructor
31680  * @param {Object} node The node to render
31681  * The TreeNode UI implementation is separate from the
31682  * tree implementation. Unless you are customizing the tree UI,
31683  * you should never have to use this directly.
31684  */
31685 Roo.tree.TreeNodeUI = function(node){
31686     this.node = node;
31687     this.rendered = false;
31688     this.animating = false;
31689     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31690 };
31691
31692 Roo.tree.TreeNodeUI.prototype = {
31693     removeChild : function(node){
31694         if(this.rendered){
31695             this.ctNode.removeChild(node.ui.getEl());
31696         }
31697     },
31698
31699     beforeLoad : function(){
31700          this.addClass("x-tree-node-loading");
31701     },
31702
31703     afterLoad : function(){
31704          this.removeClass("x-tree-node-loading");
31705     },
31706
31707     onTextChange : function(node, text, oldText){
31708         if(this.rendered){
31709             this.textNode.innerHTML = text;
31710         }
31711     },
31712
31713     onDisableChange : function(node, state){
31714         this.disabled = state;
31715         if(state){
31716             this.addClass("x-tree-node-disabled");
31717         }else{
31718             this.removeClass("x-tree-node-disabled");
31719         }
31720     },
31721
31722     onSelectedChange : function(state){
31723         if(state){
31724             this.focus();
31725             this.addClass("x-tree-selected");
31726         }else{
31727             //this.blur();
31728             this.removeClass("x-tree-selected");
31729         }
31730     },
31731
31732     onMove : function(tree, node, oldParent, newParent, index, refNode){
31733         this.childIndent = null;
31734         if(this.rendered){
31735             var targetNode = newParent.ui.getContainer();
31736             if(!targetNode){//target not rendered
31737                 this.holder = document.createElement("div");
31738                 this.holder.appendChild(this.wrap);
31739                 return;
31740             }
31741             var insertBefore = refNode ? refNode.ui.getEl() : null;
31742             if(insertBefore){
31743                 targetNode.insertBefore(this.wrap, insertBefore);
31744             }else{
31745                 targetNode.appendChild(this.wrap);
31746             }
31747             this.node.renderIndent(true);
31748         }
31749     },
31750
31751     addClass : function(cls){
31752         if(this.elNode){
31753             Roo.fly(this.elNode).addClass(cls);
31754         }
31755     },
31756
31757     removeClass : function(cls){
31758         if(this.elNode){
31759             Roo.fly(this.elNode).removeClass(cls);
31760         }
31761     },
31762
31763     remove : function(){
31764         if(this.rendered){
31765             this.holder = document.createElement("div");
31766             this.holder.appendChild(this.wrap);
31767         }
31768     },
31769
31770     fireEvent : function(){
31771         return this.node.fireEvent.apply(this.node, arguments);
31772     },
31773
31774     initEvents : function(){
31775         this.node.on("move", this.onMove, this);
31776         var E = Roo.EventManager;
31777         var a = this.anchor;
31778
31779         var el = Roo.fly(a, '_treeui');
31780
31781         if(Roo.isOpera){ // opera render bug ignores the CSS
31782             el.setStyle("text-decoration", "none");
31783         }
31784
31785         el.on("click", this.onClick, this);
31786         el.on("dblclick", this.onDblClick, this);
31787
31788         if(this.checkbox){
31789             Roo.EventManager.on(this.checkbox,
31790                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31791         }
31792
31793         el.on("contextmenu", this.onContextMenu, this);
31794
31795         var icon = Roo.fly(this.iconNode);
31796         icon.on("click", this.onClick, this);
31797         icon.on("dblclick", this.onDblClick, this);
31798         icon.on("contextmenu", this.onContextMenu, this);
31799         E.on(this.ecNode, "click", this.ecClick, this, true);
31800
31801         if(this.node.disabled){
31802             this.addClass("x-tree-node-disabled");
31803         }
31804         if(this.node.hidden){
31805             this.addClass("x-tree-node-disabled");
31806         }
31807         var ot = this.node.getOwnerTree();
31808         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31809         if(dd && (!this.node.isRoot || ot.rootVisible)){
31810             Roo.dd.Registry.register(this.elNode, {
31811                 node: this.node,
31812                 handles: this.getDDHandles(),
31813                 isHandle: false
31814             });
31815         }
31816     },
31817
31818     getDDHandles : function(){
31819         return [this.iconNode, this.textNode];
31820     },
31821
31822     hide : function(){
31823         if(this.rendered){
31824             this.wrap.style.display = "none";
31825         }
31826     },
31827
31828     show : function(){
31829         if(this.rendered){
31830             this.wrap.style.display = "";
31831         }
31832     },
31833
31834     onContextMenu : function(e){
31835         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31836             e.preventDefault();
31837             this.focus();
31838             this.fireEvent("contextmenu", this.node, e);
31839         }
31840     },
31841
31842     onClick : function(e){
31843         if(this.dropping){
31844             e.stopEvent();
31845             return;
31846         }
31847         if(this.fireEvent("beforeclick", this.node, e) !== false){
31848             if(!this.disabled && this.node.attributes.href){
31849                 this.fireEvent("click", this.node, e);
31850                 return;
31851             }
31852             e.preventDefault();
31853             if(this.disabled){
31854                 return;
31855             }
31856
31857             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
31858                 this.node.toggle();
31859             }
31860
31861             this.fireEvent("click", this.node, e);
31862         }else{
31863             e.stopEvent();
31864         }
31865     },
31866
31867     onDblClick : function(e){
31868         e.preventDefault();
31869         if(this.disabled){
31870             return;
31871         }
31872         if(this.checkbox){
31873             this.toggleCheck();
31874         }
31875         if(!this.animating && this.node.hasChildNodes()){
31876             this.node.toggle();
31877         }
31878         this.fireEvent("dblclick", this.node, e);
31879     },
31880
31881     onCheckChange : function(){
31882         var checked = this.checkbox.checked;
31883         this.node.attributes.checked = checked;
31884         this.fireEvent('checkchange', this.node, checked);
31885     },
31886
31887     ecClick : function(e){
31888         if(!this.animating && this.node.hasChildNodes()){
31889             this.node.toggle();
31890         }
31891     },
31892
31893     startDrop : function(){
31894         this.dropping = true;
31895     },
31896
31897     // delayed drop so the click event doesn't get fired on a drop
31898     endDrop : function(){
31899        setTimeout(function(){
31900            this.dropping = false;
31901        }.createDelegate(this), 50);
31902     },
31903
31904     expand : function(){
31905         this.updateExpandIcon();
31906         this.ctNode.style.display = "";
31907     },
31908
31909     focus : function(){
31910         if(!this.node.preventHScroll){
31911             try{this.anchor.focus();
31912             }catch(e){}
31913         }else if(!Roo.isIE){
31914             try{
31915                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
31916                 var l = noscroll.scrollLeft;
31917                 this.anchor.focus();
31918                 noscroll.scrollLeft = l;
31919             }catch(e){}
31920         }
31921     },
31922
31923     toggleCheck : function(value){
31924         var cb = this.checkbox;
31925         if(cb){
31926             cb.checked = (value === undefined ? !cb.checked : value);
31927         }
31928     },
31929
31930     blur : function(){
31931         try{
31932             this.anchor.blur();
31933         }catch(e){}
31934     },
31935
31936     animExpand : function(callback){
31937         var ct = Roo.get(this.ctNode);
31938         ct.stopFx();
31939         if(!this.node.hasChildNodes()){
31940             this.updateExpandIcon();
31941             this.ctNode.style.display = "";
31942             Roo.callback(callback);
31943             return;
31944         }
31945         this.animating = true;
31946         this.updateExpandIcon();
31947
31948         ct.slideIn('t', {
31949            callback : function(){
31950                this.animating = false;
31951                Roo.callback(callback);
31952             },
31953             scope: this,
31954             duration: this.node.ownerTree.duration || .25
31955         });
31956     },
31957
31958     highlight : function(){
31959         var tree = this.node.getOwnerTree();
31960         Roo.fly(this.wrap).highlight(
31961             tree.hlColor || "C3DAF9",
31962             {endColor: tree.hlBaseColor}
31963         );
31964     },
31965
31966     collapse : function(){
31967         this.updateExpandIcon();
31968         this.ctNode.style.display = "none";
31969     },
31970
31971     animCollapse : function(callback){
31972         var ct = Roo.get(this.ctNode);
31973         ct.enableDisplayMode('block');
31974         ct.stopFx();
31975
31976         this.animating = true;
31977         this.updateExpandIcon();
31978
31979         ct.slideOut('t', {
31980             callback : function(){
31981                this.animating = false;
31982                Roo.callback(callback);
31983             },
31984             scope: this,
31985             duration: this.node.ownerTree.duration || .25
31986         });
31987     },
31988
31989     getContainer : function(){
31990         return this.ctNode;
31991     },
31992
31993     getEl : function(){
31994         return this.wrap;
31995     },
31996
31997     appendDDGhost : function(ghostNode){
31998         ghostNode.appendChild(this.elNode.cloneNode(true));
31999     },
32000
32001     getDDRepairXY : function(){
32002         return Roo.lib.Dom.getXY(this.iconNode);
32003     },
32004
32005     onRender : function(){
32006         this.render();
32007     },
32008
32009     render : function(bulkRender){
32010         var n = this.node, a = n.attributes;
32011         var targetNode = n.parentNode ?
32012               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32013
32014         if(!this.rendered){
32015             this.rendered = true;
32016
32017             this.renderElements(n, a, targetNode, bulkRender);
32018
32019             if(a.qtip){
32020                if(this.textNode.setAttributeNS){
32021                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32022                    if(a.qtipTitle){
32023                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32024                    }
32025                }else{
32026                    this.textNode.setAttribute("ext:qtip", a.qtip);
32027                    if(a.qtipTitle){
32028                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32029                    }
32030                }
32031             }else if(a.qtipCfg){
32032                 a.qtipCfg.target = Roo.id(this.textNode);
32033                 Roo.QuickTips.register(a.qtipCfg);
32034             }
32035             this.initEvents();
32036             if(!this.node.expanded){
32037                 this.updateExpandIcon();
32038             }
32039         }else{
32040             if(bulkRender === true) {
32041                 targetNode.appendChild(this.wrap);
32042             }
32043         }
32044     },
32045
32046     renderElements : function(n, a, targetNode, bulkRender){
32047         // add some indent caching, this helps performance when rendering a large tree
32048         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32049         var t = n.getOwnerTree();
32050         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32051         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32052         var cb = typeof a.checked == 'boolean';
32053         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32054         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32055             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32056             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32057             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32058             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32059             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32060              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32061                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32062             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32063             "</li>"];
32064
32065         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32066             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32067                                 n.nextSibling.ui.getEl(), buf.join(""));
32068         }else{
32069             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32070         }
32071
32072         this.elNode = this.wrap.childNodes[0];
32073         this.ctNode = this.wrap.childNodes[1];
32074         var cs = this.elNode.childNodes;
32075         this.indentNode = cs[0];
32076         this.ecNode = cs[1];
32077         this.iconNode = cs[2];
32078         var index = 3;
32079         if(cb){
32080             this.checkbox = cs[3];
32081             index++;
32082         }
32083         this.anchor = cs[index];
32084         this.textNode = cs[index].firstChild;
32085     },
32086
32087     getAnchor : function(){
32088         return this.anchor;
32089     },
32090
32091     getTextEl : function(){
32092         return this.textNode;
32093     },
32094
32095     getIconEl : function(){
32096         return this.iconNode;
32097     },
32098
32099     isChecked : function(){
32100         return this.checkbox ? this.checkbox.checked : false;
32101     },
32102
32103     updateExpandIcon : function(){
32104         if(this.rendered){
32105             var n = this.node, c1, c2;
32106             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32107             var hasChild = n.hasChildNodes();
32108             if(hasChild){
32109                 if(n.expanded){
32110                     cls += "-minus";
32111                     c1 = "x-tree-node-collapsed";
32112                     c2 = "x-tree-node-expanded";
32113                 }else{
32114                     cls += "-plus";
32115                     c1 = "x-tree-node-expanded";
32116                     c2 = "x-tree-node-collapsed";
32117                 }
32118                 if(this.wasLeaf){
32119                     this.removeClass("x-tree-node-leaf");
32120                     this.wasLeaf = false;
32121                 }
32122                 if(this.c1 != c1 || this.c2 != c2){
32123                     Roo.fly(this.elNode).replaceClass(c1, c2);
32124                     this.c1 = c1; this.c2 = c2;
32125                 }
32126             }else{
32127                 if(!this.wasLeaf){
32128                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32129                     delete this.c1;
32130                     delete this.c2;
32131                     this.wasLeaf = true;
32132                 }
32133             }
32134             var ecc = "x-tree-ec-icon "+cls;
32135             if(this.ecc != ecc){
32136                 this.ecNode.className = ecc;
32137                 this.ecc = ecc;
32138             }
32139         }
32140     },
32141
32142     getChildIndent : function(){
32143         if(!this.childIndent){
32144             var buf = [];
32145             var p = this.node;
32146             while(p){
32147                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32148                     if(!p.isLast()) {
32149                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32150                     } else {
32151                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32152                     }
32153                 }
32154                 p = p.parentNode;
32155             }
32156             this.childIndent = buf.join("");
32157         }
32158         return this.childIndent;
32159     },
32160
32161     renderIndent : function(){
32162         if(this.rendered){
32163             var indent = "";
32164             var p = this.node.parentNode;
32165             if(p){
32166                 indent = p.ui.getChildIndent();
32167             }
32168             if(this.indentMarkup != indent){ // don't rerender if not required
32169                 this.indentNode.innerHTML = indent;
32170                 this.indentMarkup = indent;
32171             }
32172             this.updateExpandIcon();
32173         }
32174     }
32175 };
32176
32177 Roo.tree.RootTreeNodeUI = function(){
32178     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32179 };
32180 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32181     render : function(){
32182         if(!this.rendered){
32183             var targetNode = this.node.ownerTree.innerCt.dom;
32184             this.node.expanded = true;
32185             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32186             this.wrap = this.ctNode = targetNode.firstChild;
32187         }
32188     },
32189     collapse : function(){
32190     },
32191     expand : function(){
32192     }
32193 });/*
32194  * Based on:
32195  * Ext JS Library 1.1.1
32196  * Copyright(c) 2006-2007, Ext JS, LLC.
32197  *
32198  * Originally Released Under LGPL - original licence link has changed is not relivant.
32199  *
32200  * Fork - LGPL
32201  * <script type="text/javascript">
32202  */
32203 /**
32204  * @class Roo.tree.TreeLoader
32205  * @extends Roo.util.Observable
32206  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32207  * nodes from a specified URL. The response must be a javascript Array definition
32208  * who's elements are node definition objects. eg:
32209  * <pre><code>
32210    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32211     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32212 </code></pre>
32213  * <br><br>
32214  * A server request is sent, and child nodes are loaded only when a node is expanded.
32215  * The loading node's id is passed to the server under the parameter name "node" to
32216  * enable the server to produce the correct child nodes.
32217  * <br><br>
32218  * To pass extra parameters, an event handler may be attached to the "beforeload"
32219  * event, and the parameters specified in the TreeLoader's baseParams property:
32220  * <pre><code>
32221     myTreeLoader.on("beforeload", function(treeLoader, node) {
32222         this.baseParams.category = node.attributes.category;
32223     }, this);
32224 </code></pre><
32225  * This would pass an HTTP parameter called "category" to the server containing
32226  * the value of the Node's "category" attribute.
32227  * @constructor
32228  * Creates a new Treeloader.
32229  * @param {Object} config A config object containing config properties.
32230  */
32231 Roo.tree.TreeLoader = function(config){
32232     this.baseParams = {};
32233     this.requestMethod = "POST";
32234     Roo.apply(this, config);
32235
32236     this.addEvents({
32237     
32238         /**
32239          * @event beforeload
32240          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32241          * @param {Object} This TreeLoader object.
32242          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32243          * @param {Object} callback The callback function specified in the {@link #load} call.
32244          */
32245         beforeload : true,
32246         /**
32247          * @event load
32248          * Fires when the node has been successfuly loaded.
32249          * @param {Object} This TreeLoader object.
32250          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32251          * @param {Object} response The response object containing the data from the server.
32252          */
32253         load : true,
32254         /**
32255          * @event loadexception
32256          * Fires if the network request failed.
32257          * @param {Object} This TreeLoader object.
32258          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32259          * @param {Object} response The response object containing the data from the server.
32260          */
32261         loadexception : true,
32262         /**
32263          * @event create
32264          * Fires before a node is created, enabling you to return custom Node types 
32265          * @param {Object} This TreeLoader object.
32266          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32267          */
32268         create : true
32269     });
32270
32271     Roo.tree.TreeLoader.superclass.constructor.call(this);
32272 };
32273
32274 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32275     /**
32276     * @cfg {String} dataUrl The URL from which to request a Json string which
32277     * specifies an array of node definition object representing the child nodes
32278     * to be loaded.
32279     */
32280     /**
32281     * @cfg {Object} baseParams (optional) An object containing properties which
32282     * specify HTTP parameters to be passed to each request for child nodes.
32283     */
32284     /**
32285     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32286     * created by this loader. If the attributes sent by the server have an attribute in this object,
32287     * they take priority.
32288     */
32289     /**
32290     * @cfg {Object} uiProviders (optional) An object containing properties which
32291     * 
32292     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32293     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32294     * <i>uiProvider</i> attribute of a returned child node is a string rather
32295     * than a reference to a TreeNodeUI implementation, this that string value
32296     * is used as a property name in the uiProviders object. You can define the provider named
32297     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32298     */
32299     uiProviders : {},
32300
32301     /**
32302     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32303     * child nodes before loading.
32304     */
32305     clearOnLoad : true,
32306
32307     /**
32308     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32309     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32310     * Grid query { data : [ .....] }
32311     */
32312     
32313     root : false,
32314      /**
32315     * @cfg {String} queryParam (optional) 
32316     * Name of the query as it will be passed on the querystring (defaults to 'node')
32317     * eg. the request will be ?node=[id]
32318     */
32319     
32320     
32321     queryParam: false,
32322     
32323     /**
32324      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32325      * This is called automatically when a node is expanded, but may be used to reload
32326      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32327      * @param {Roo.tree.TreeNode} node
32328      * @param {Function} callback
32329      */
32330     load : function(node, callback){
32331         if(this.clearOnLoad){
32332             while(node.firstChild){
32333                 node.removeChild(node.firstChild);
32334             }
32335         }
32336         if(node.attributes.children){ // preloaded json children
32337             var cs = node.attributes.children;
32338             for(var i = 0, len = cs.length; i < len; i++){
32339                 node.appendChild(this.createNode(cs[i]));
32340             }
32341             if(typeof callback == "function"){
32342                 callback();
32343             }
32344         }else if(this.dataUrl){
32345             this.requestData(node, callback);
32346         }
32347     },
32348
32349     getParams: function(node){
32350         var buf = [], bp = this.baseParams;
32351         for(var key in bp){
32352             if(typeof bp[key] != "function"){
32353                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32354             }
32355         }
32356         var n = this.queryParam === false ? 'node' : this.queryParam;
32357         buf.push(n + "=", encodeURIComponent(node.id));
32358         return buf.join("");
32359     },
32360
32361     requestData : function(node, callback){
32362         if(this.fireEvent("beforeload", this, node, callback) !== false){
32363             this.transId = Roo.Ajax.request({
32364                 method:this.requestMethod,
32365                 url: this.dataUrl||this.url,
32366                 success: this.handleResponse,
32367                 failure: this.handleFailure,
32368                 scope: this,
32369                 argument: {callback: callback, node: node},
32370                 params: this.getParams(node)
32371             });
32372         }else{
32373             // if the load is cancelled, make sure we notify
32374             // the node that we are done
32375             if(typeof callback == "function"){
32376                 callback();
32377             }
32378         }
32379     },
32380
32381     isLoading : function(){
32382         return this.transId ? true : false;
32383     },
32384
32385     abort : function(){
32386         if(this.isLoading()){
32387             Roo.Ajax.abort(this.transId);
32388         }
32389     },
32390
32391     // private
32392     createNode : function(attr){
32393         // apply baseAttrs, nice idea Corey!
32394         if(this.baseAttrs){
32395             Roo.applyIf(attr, this.baseAttrs);
32396         }
32397         if(this.applyLoader !== false){
32398             attr.loader = this;
32399         }
32400         // uiProvider = depreciated..
32401         
32402         if(typeof(attr.uiProvider) == 'string'){
32403            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32404                 /**  eval:var:attr */ eval(attr.uiProvider);
32405         }
32406         if(typeof(this.uiProviders['default']) != 'undefined') {
32407             attr.uiProvider = this.uiProviders['default'];
32408         }
32409         
32410         this.fireEvent('create', this, attr);
32411         
32412         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32413         return(attr.leaf ?
32414                         new Roo.tree.TreeNode(attr) :
32415                         new Roo.tree.AsyncTreeNode(attr));
32416     },
32417
32418     processResponse : function(response, node, callback){
32419         var json = response.responseText;
32420         try {
32421             
32422             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32423             if (this.root !== false) {
32424                 o = o[this.root];
32425             }
32426             
32427             for(var i = 0, len = o.length; i < len; i++){
32428                 var n = this.createNode(o[i]);
32429                 if(n){
32430                     node.appendChild(n);
32431                 }
32432             }
32433             if(typeof callback == "function"){
32434                 callback(this, node);
32435             }
32436         }catch(e){
32437             this.handleFailure(response);
32438         }
32439     },
32440
32441     handleResponse : function(response){
32442         this.transId = false;
32443         var a = response.argument;
32444         this.processResponse(response, a.node, a.callback);
32445         this.fireEvent("load", this, a.node, response);
32446     },
32447
32448     handleFailure : function(response){
32449         this.transId = false;
32450         var a = response.argument;
32451         this.fireEvent("loadexception", this, a.node, response);
32452         if(typeof a.callback == "function"){
32453             a.callback(this, a.node);
32454         }
32455     }
32456 });/*
32457  * Based on:
32458  * Ext JS Library 1.1.1
32459  * Copyright(c) 2006-2007, Ext JS, LLC.
32460  *
32461  * Originally Released Under LGPL - original licence link has changed is not relivant.
32462  *
32463  * Fork - LGPL
32464  * <script type="text/javascript">
32465  */
32466
32467 /**
32468 * @class Roo.tree.TreeFilter
32469 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32470 * @param {TreePanel} tree
32471 * @param {Object} config (optional)
32472  */
32473 Roo.tree.TreeFilter = function(tree, config){
32474     this.tree = tree;
32475     this.filtered = {};
32476     Roo.apply(this, config);
32477 };
32478
32479 Roo.tree.TreeFilter.prototype = {
32480     clearBlank:false,
32481     reverse:false,
32482     autoClear:false,
32483     remove:false,
32484
32485      /**
32486      * Filter the data by a specific attribute.
32487      * @param {String/RegExp} value Either string that the attribute value
32488      * should start with or a RegExp to test against the attribute
32489      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32490      * @param {TreeNode} startNode (optional) The node to start the filter at.
32491      */
32492     filter : function(value, attr, startNode){
32493         attr = attr || "text";
32494         var f;
32495         if(typeof value == "string"){
32496             var vlen = value.length;
32497             // auto clear empty filter
32498             if(vlen == 0 && this.clearBlank){
32499                 this.clear();
32500                 return;
32501             }
32502             value = value.toLowerCase();
32503             f = function(n){
32504                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32505             };
32506         }else if(value.exec){ // regex?
32507             f = function(n){
32508                 return value.test(n.attributes[attr]);
32509             };
32510         }else{
32511             throw 'Illegal filter type, must be string or regex';
32512         }
32513         this.filterBy(f, null, startNode);
32514         },
32515
32516     /**
32517      * Filter by a function. The passed function will be called with each
32518      * node in the tree (or from the startNode). If the function returns true, the node is kept
32519      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32520      * @param {Function} fn The filter function
32521      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32522      */
32523     filterBy : function(fn, scope, startNode){
32524         startNode = startNode || this.tree.root;
32525         if(this.autoClear){
32526             this.clear();
32527         }
32528         var af = this.filtered, rv = this.reverse;
32529         var f = function(n){
32530             if(n == startNode){
32531                 return true;
32532             }
32533             if(af[n.id]){
32534                 return false;
32535             }
32536             var m = fn.call(scope || n, n);
32537             if(!m || rv){
32538                 af[n.id] = n;
32539                 n.ui.hide();
32540                 return false;
32541             }
32542             return true;
32543         };
32544         startNode.cascade(f);
32545         if(this.remove){
32546            for(var id in af){
32547                if(typeof id != "function"){
32548                    var n = af[id];
32549                    if(n && n.parentNode){
32550                        n.parentNode.removeChild(n);
32551                    }
32552                }
32553            }
32554         }
32555     },
32556
32557     /**
32558      * Clears the current filter. Note: with the "remove" option
32559      * set a filter cannot be cleared.
32560      */
32561     clear : function(){
32562         var t = this.tree;
32563         var af = this.filtered;
32564         for(var id in af){
32565             if(typeof id != "function"){
32566                 var n = af[id];
32567                 if(n){
32568                     n.ui.show();
32569                 }
32570             }
32571         }
32572         this.filtered = {};
32573     }
32574 };
32575 /*
32576  * Based on:
32577  * Ext JS Library 1.1.1
32578  * Copyright(c) 2006-2007, Ext JS, LLC.
32579  *
32580  * Originally Released Under LGPL - original licence link has changed is not relivant.
32581  *
32582  * Fork - LGPL
32583  * <script type="text/javascript">
32584  */
32585  
32586
32587 /**
32588  * @class Roo.tree.TreeSorter
32589  * Provides sorting of nodes in a TreePanel
32590  * 
32591  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32592  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32593  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32594  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32595  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32596  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32597  * @constructor
32598  * @param {TreePanel} tree
32599  * @param {Object} config
32600  */
32601 Roo.tree.TreeSorter = function(tree, config){
32602     Roo.apply(this, config);
32603     tree.on("beforechildrenrendered", this.doSort, this);
32604     tree.on("append", this.updateSort, this);
32605     tree.on("insert", this.updateSort, this);
32606     
32607     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32608     var p = this.property || "text";
32609     var sortType = this.sortType;
32610     var fs = this.folderSort;
32611     var cs = this.caseSensitive === true;
32612     var leafAttr = this.leafAttr || 'leaf';
32613
32614     this.sortFn = function(n1, n2){
32615         if(fs){
32616             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32617                 return 1;
32618             }
32619             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32620                 return -1;
32621             }
32622         }
32623         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32624         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32625         if(v1 < v2){
32626                         return dsc ? +1 : -1;
32627                 }else if(v1 > v2){
32628                         return dsc ? -1 : +1;
32629         }else{
32630                 return 0;
32631         }
32632     };
32633 };
32634
32635 Roo.tree.TreeSorter.prototype = {
32636     doSort : function(node){
32637         node.sort(this.sortFn);
32638     },
32639     
32640     compareNodes : function(n1, n2){
32641         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32642     },
32643     
32644     updateSort : function(tree, node){
32645         if(node.childrenRendered){
32646             this.doSort.defer(1, this, [node]);
32647         }
32648     }
32649 };/*
32650  * Based on:
32651  * Ext JS Library 1.1.1
32652  * Copyright(c) 2006-2007, Ext JS, LLC.
32653  *
32654  * Originally Released Under LGPL - original licence link has changed is not relivant.
32655  *
32656  * Fork - LGPL
32657  * <script type="text/javascript">
32658  */
32659
32660 if(Roo.dd.DropZone){
32661     
32662 Roo.tree.TreeDropZone = function(tree, config){
32663     this.allowParentInsert = false;
32664     this.allowContainerDrop = false;
32665     this.appendOnly = false;
32666     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32667     this.tree = tree;
32668     this.lastInsertClass = "x-tree-no-status";
32669     this.dragOverData = {};
32670 };
32671
32672 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32673     ddGroup : "TreeDD",
32674     
32675     expandDelay : 1000,
32676     
32677     expandNode : function(node){
32678         if(node.hasChildNodes() && !node.isExpanded()){
32679             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32680         }
32681     },
32682     
32683     queueExpand : function(node){
32684         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32685     },
32686     
32687     cancelExpand : function(){
32688         if(this.expandProcId){
32689             clearTimeout(this.expandProcId);
32690             this.expandProcId = false;
32691         }
32692     },
32693     
32694     isValidDropPoint : function(n, pt, dd, e, data){
32695         if(!n || !data){ return false; }
32696         var targetNode = n.node;
32697         var dropNode = data.node;
32698         // default drop rules
32699         if(!(targetNode && targetNode.isTarget && pt)){
32700             return false;
32701         }
32702         if(pt == "append" && targetNode.allowChildren === false){
32703             return false;
32704         }
32705         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32706             return false;
32707         }
32708         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32709             return false;
32710         }
32711         // reuse the object
32712         var overEvent = this.dragOverData;
32713         overEvent.tree = this.tree;
32714         overEvent.target = targetNode;
32715         overEvent.data = data;
32716         overEvent.point = pt;
32717         overEvent.source = dd;
32718         overEvent.rawEvent = e;
32719         overEvent.dropNode = dropNode;
32720         overEvent.cancel = false;  
32721         var result = this.tree.fireEvent("nodedragover", overEvent);
32722         return overEvent.cancel === false && result !== false;
32723     },
32724     
32725     getDropPoint : function(e, n, dd){
32726         var tn = n.node;
32727         if(tn.isRoot){
32728             return tn.allowChildren !== false ? "append" : false; // always append for root
32729         }
32730         var dragEl = n.ddel;
32731         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32732         var y = Roo.lib.Event.getPageY(e);
32733         var noAppend = tn.allowChildren === false || tn.isLeaf();
32734         if(this.appendOnly || tn.parentNode.allowChildren === false){
32735             return noAppend ? false : "append";
32736         }
32737         var noBelow = false;
32738         if(!this.allowParentInsert){
32739             noBelow = tn.hasChildNodes() && tn.isExpanded();
32740         }
32741         var q = (b - t) / (noAppend ? 2 : 3);
32742         if(y >= t && y < (t + q)){
32743             return "above";
32744         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32745             return "below";
32746         }else{
32747             return "append";
32748         }
32749     },
32750     
32751     onNodeEnter : function(n, dd, e, data){
32752         this.cancelExpand();
32753     },
32754     
32755     onNodeOver : function(n, dd, e, data){
32756         var pt = this.getDropPoint(e, n, dd);
32757         var node = n.node;
32758         
32759         // auto node expand check
32760         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32761             this.queueExpand(node);
32762         }else if(pt != "append"){
32763             this.cancelExpand();
32764         }
32765         
32766         // set the insert point style on the target node
32767         var returnCls = this.dropNotAllowed;
32768         if(this.isValidDropPoint(n, pt, dd, e, data)){
32769            if(pt){
32770                var el = n.ddel;
32771                var cls;
32772                if(pt == "above"){
32773                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32774                    cls = "x-tree-drag-insert-above";
32775                }else if(pt == "below"){
32776                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32777                    cls = "x-tree-drag-insert-below";
32778                }else{
32779                    returnCls = "x-tree-drop-ok-append";
32780                    cls = "x-tree-drag-append";
32781                }
32782                if(this.lastInsertClass != cls){
32783                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32784                    this.lastInsertClass = cls;
32785                }
32786            }
32787        }
32788        return returnCls;
32789     },
32790     
32791     onNodeOut : function(n, dd, e, data){
32792         this.cancelExpand();
32793         this.removeDropIndicators(n);
32794     },
32795     
32796     onNodeDrop : function(n, dd, e, data){
32797         var point = this.getDropPoint(e, n, dd);
32798         var targetNode = n.node;
32799         targetNode.ui.startDrop();
32800         if(!this.isValidDropPoint(n, point, dd, e, data)){
32801             targetNode.ui.endDrop();
32802             return false;
32803         }
32804         // first try to find the drop node
32805         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32806         var dropEvent = {
32807             tree : this.tree,
32808             target: targetNode,
32809             data: data,
32810             point: point,
32811             source: dd,
32812             rawEvent: e,
32813             dropNode: dropNode,
32814             cancel: !dropNode   
32815         };
32816         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32817         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32818             targetNode.ui.endDrop();
32819             return false;
32820         }
32821         // allow target changing
32822         targetNode = dropEvent.target;
32823         if(point == "append" && !targetNode.isExpanded()){
32824             targetNode.expand(false, null, function(){
32825                 this.completeDrop(dropEvent);
32826             }.createDelegate(this));
32827         }else{
32828             this.completeDrop(dropEvent);
32829         }
32830         return true;
32831     },
32832     
32833     completeDrop : function(de){
32834         var ns = de.dropNode, p = de.point, t = de.target;
32835         if(!(ns instanceof Array)){
32836             ns = [ns];
32837         }
32838         var n;
32839         for(var i = 0, len = ns.length; i < len; i++){
32840             n = ns[i];
32841             if(p == "above"){
32842                 t.parentNode.insertBefore(n, t);
32843             }else if(p == "below"){
32844                 t.parentNode.insertBefore(n, t.nextSibling);
32845             }else{
32846                 t.appendChild(n);
32847             }
32848         }
32849         n.ui.focus();
32850         if(this.tree.hlDrop){
32851             n.ui.highlight();
32852         }
32853         t.ui.endDrop();
32854         this.tree.fireEvent("nodedrop", de);
32855     },
32856     
32857     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
32858         if(this.tree.hlDrop){
32859             dropNode.ui.focus();
32860             dropNode.ui.highlight();
32861         }
32862         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
32863     },
32864     
32865     getTree : function(){
32866         return this.tree;
32867     },
32868     
32869     removeDropIndicators : function(n){
32870         if(n && n.ddel){
32871             var el = n.ddel;
32872             Roo.fly(el).removeClass([
32873                     "x-tree-drag-insert-above",
32874                     "x-tree-drag-insert-below",
32875                     "x-tree-drag-append"]);
32876             this.lastInsertClass = "_noclass";
32877         }
32878     },
32879     
32880     beforeDragDrop : function(target, e, id){
32881         this.cancelExpand();
32882         return true;
32883     },
32884     
32885     afterRepair : function(data){
32886         if(data && Roo.enableFx){
32887             data.node.ui.highlight();
32888         }
32889         this.hideProxy();
32890     }    
32891 });
32892
32893 }/*
32894  * Based on:
32895  * Ext JS Library 1.1.1
32896  * Copyright(c) 2006-2007, Ext JS, LLC.
32897  *
32898  * Originally Released Under LGPL - original licence link has changed is not relivant.
32899  *
32900  * Fork - LGPL
32901  * <script type="text/javascript">
32902  */
32903  
32904
32905 if(Roo.dd.DragZone){
32906 Roo.tree.TreeDragZone = function(tree, config){
32907     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
32908     this.tree = tree;
32909 };
32910
32911 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
32912     ddGroup : "TreeDD",
32913     
32914     onBeforeDrag : function(data, e){
32915         var n = data.node;
32916         return n && n.draggable && !n.disabled;
32917     },
32918     
32919     onInitDrag : function(e){
32920         var data = this.dragData;
32921         this.tree.getSelectionModel().select(data.node);
32922         this.proxy.update("");
32923         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
32924         this.tree.fireEvent("startdrag", this.tree, data.node, e);
32925     },
32926     
32927     getRepairXY : function(e, data){
32928         return data.node.ui.getDDRepairXY();
32929     },
32930     
32931     onEndDrag : function(data, e){
32932         this.tree.fireEvent("enddrag", this.tree, data.node, e);
32933     },
32934     
32935     onValidDrop : function(dd, e, id){
32936         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
32937         this.hideProxy();
32938     },
32939     
32940     beforeInvalidDrop : function(e, id){
32941         // this scrolls the original position back into view
32942         var sm = this.tree.getSelectionModel();
32943         sm.clearSelections();
32944         sm.select(this.dragData.node);
32945     }
32946 });
32947 }/*
32948  * Based on:
32949  * Ext JS Library 1.1.1
32950  * Copyright(c) 2006-2007, Ext JS, LLC.
32951  *
32952  * Originally Released Under LGPL - original licence link has changed is not relivant.
32953  *
32954  * Fork - LGPL
32955  * <script type="text/javascript">
32956  */
32957 /**
32958  * @class Roo.tree.TreeEditor
32959  * @extends Roo.Editor
32960  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
32961  * as the editor field.
32962  * @constructor
32963  * @param {TreePanel} tree
32964  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
32965  */
32966 Roo.tree.TreeEditor = function(tree, config){
32967     config = config || {};
32968     var field = config.events ? config : new Roo.form.TextField(config);
32969     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
32970
32971     this.tree = tree;
32972
32973     tree.on('beforeclick', this.beforeNodeClick, this);
32974     tree.getTreeEl().on('mousedown', this.hide, this);
32975     this.on('complete', this.updateNode, this);
32976     this.on('beforestartedit', this.fitToTree, this);
32977     this.on('startedit', this.bindScroll, this, {delay:10});
32978     this.on('specialkey', this.onSpecialKey, this);
32979 };
32980
32981 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
32982     /**
32983      * @cfg {String} alignment
32984      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
32985      */
32986     alignment: "l-l",
32987     // inherit
32988     autoSize: false,
32989     /**
32990      * @cfg {Boolean} hideEl
32991      * True to hide the bound element while the editor is displayed (defaults to false)
32992      */
32993     hideEl : false,
32994     /**
32995      * @cfg {String} cls
32996      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
32997      */
32998     cls: "x-small-editor x-tree-editor",
32999     /**
33000      * @cfg {Boolean} shim
33001      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33002      */
33003     shim:false,
33004     // inherit
33005     shadow:"frame",
33006     /**
33007      * @cfg {Number} maxWidth
33008      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33009      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33010      * scroll and client offsets into account prior to each edit.
33011      */
33012     maxWidth: 250,
33013
33014     editDelay : 350,
33015
33016     // private
33017     fitToTree : function(ed, el){
33018         var td = this.tree.getTreeEl().dom, nd = el.dom;
33019         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33020             td.scrollLeft = nd.offsetLeft;
33021         }
33022         var w = Math.min(
33023                 this.maxWidth,
33024                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33025         this.setSize(w, '');
33026     },
33027
33028     // private
33029     triggerEdit : function(node){
33030         this.completeEdit();
33031         this.editNode = node;
33032         this.startEdit(node.ui.textNode, node.text);
33033     },
33034
33035     // private
33036     bindScroll : function(){
33037         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33038     },
33039
33040     // private
33041     beforeNodeClick : function(node, e){
33042         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33043         this.lastClick = new Date();
33044         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33045             e.stopEvent();
33046             this.triggerEdit(node);
33047             return false;
33048         }
33049     },
33050
33051     // private
33052     updateNode : function(ed, value){
33053         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33054         this.editNode.setText(value);
33055     },
33056
33057     // private
33058     onHide : function(){
33059         Roo.tree.TreeEditor.superclass.onHide.call(this);
33060         if(this.editNode){
33061             this.editNode.ui.focus();
33062         }
33063     },
33064
33065     // private
33066     onSpecialKey : function(field, e){
33067         var k = e.getKey();
33068         if(k == e.ESC){
33069             e.stopEvent();
33070             this.cancelEdit();
33071         }else if(k == e.ENTER && !e.hasModifier()){
33072             e.stopEvent();
33073             this.completeEdit();
33074         }
33075     }
33076 });//<Script type="text/javascript">
33077 /*
33078  * Based on:
33079  * Ext JS Library 1.1.1
33080  * Copyright(c) 2006-2007, Ext JS, LLC.
33081  *
33082  * Originally Released Under LGPL - original licence link has changed is not relivant.
33083  *
33084  * Fork - LGPL
33085  * <script type="text/javascript">
33086  */
33087  
33088 /**
33089  * Not documented??? - probably should be...
33090  */
33091
33092 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33093     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33094     
33095     renderElements : function(n, a, targetNode, bulkRender){
33096         //consel.log("renderElements?");
33097         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33098
33099         var t = n.getOwnerTree();
33100         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33101         
33102         var cols = t.columns;
33103         var bw = t.borderWidth;
33104         var c = cols[0];
33105         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33106          var cb = typeof a.checked == "boolean";
33107         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33108         var colcls = 'x-t-' + tid + '-c0';
33109         var buf = [
33110             '<li class="x-tree-node">',
33111             
33112                 
33113                 '<div class="x-tree-node-el ', a.cls,'">',
33114                     // extran...
33115                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33116                 
33117                 
33118                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33119                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33120                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33121                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33122                            (a.iconCls ? ' '+a.iconCls : ''),
33123                            '" unselectable="on" />',
33124                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33125                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33126                              
33127                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33128                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33129                             '<span unselectable="on" qtip="' + tx + '">',
33130                              tx,
33131                              '</span></a>' ,
33132                     '</div>',
33133                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33134                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33135                  ];
33136         
33137         for(var i = 1, len = cols.length; i < len; i++){
33138             c = cols[i];
33139             colcls = 'x-t-' + tid + '-c' +i;
33140             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33141             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33142                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33143                       "</div>");
33144          }
33145          
33146          buf.push(
33147             '</a>',
33148             '<div class="x-clear"></div></div>',
33149             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33150             "</li>");
33151         
33152         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33153             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33154                                 n.nextSibling.ui.getEl(), buf.join(""));
33155         }else{
33156             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33157         }
33158         var el = this.wrap.firstChild;
33159         this.elRow = el;
33160         this.elNode = el.firstChild;
33161         this.ranchor = el.childNodes[1];
33162         this.ctNode = this.wrap.childNodes[1];
33163         var cs = el.firstChild.childNodes;
33164         this.indentNode = cs[0];
33165         this.ecNode = cs[1];
33166         this.iconNode = cs[2];
33167         var index = 3;
33168         if(cb){
33169             this.checkbox = cs[3];
33170             index++;
33171         }
33172         this.anchor = cs[index];
33173         
33174         this.textNode = cs[index].firstChild;
33175         
33176         //el.on("click", this.onClick, this);
33177         //el.on("dblclick", this.onDblClick, this);
33178         
33179         
33180        // console.log(this);
33181     },
33182     initEvents : function(){
33183         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33184         
33185             
33186         var a = this.ranchor;
33187
33188         var el = Roo.get(a);
33189
33190         if(Roo.isOpera){ // opera render bug ignores the CSS
33191             el.setStyle("text-decoration", "none");
33192         }
33193
33194         el.on("click", this.onClick, this);
33195         el.on("dblclick", this.onDblClick, this);
33196         el.on("contextmenu", this.onContextMenu, this);
33197         
33198     },
33199     
33200     /*onSelectedChange : function(state){
33201         if(state){
33202             this.focus();
33203             this.addClass("x-tree-selected");
33204         }else{
33205             //this.blur();
33206             this.removeClass("x-tree-selected");
33207         }
33208     },*/
33209     addClass : function(cls){
33210         if(this.elRow){
33211             Roo.fly(this.elRow).addClass(cls);
33212         }
33213         
33214     },
33215     
33216     
33217     removeClass : function(cls){
33218         if(this.elRow){
33219             Roo.fly(this.elRow).removeClass(cls);
33220         }
33221     }
33222
33223     
33224     
33225 });//<Script type="text/javascript">
33226
33227 /*
33228  * Based on:
33229  * Ext JS Library 1.1.1
33230  * Copyright(c) 2006-2007, Ext JS, LLC.
33231  *
33232  * Originally Released Under LGPL - original licence link has changed is not relivant.
33233  *
33234  * Fork - LGPL
33235  * <script type="text/javascript">
33236  */
33237  
33238
33239 /**
33240  * @class Roo.tree.ColumnTree
33241  * @extends Roo.data.TreePanel
33242  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33243  * @cfg {int} borderWidth  compined right/left border allowance
33244  * @constructor
33245  * @param {String/HTMLElement/Element} el The container element
33246  * @param {Object} config
33247  */
33248 Roo.tree.ColumnTree =  function(el, config)
33249 {
33250    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33251    this.addEvents({
33252         /**
33253         * @event resize
33254         * Fire this event on a container when it resizes
33255         * @param {int} w Width
33256         * @param {int} h Height
33257         */
33258        "resize" : true
33259     });
33260     this.on('resize', this.onResize, this);
33261 };
33262
33263 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33264     //lines:false,
33265     
33266     
33267     borderWidth: Roo.isBorderBox ? 0 : 2, 
33268     headEls : false,
33269     
33270     render : function(){
33271         // add the header.....
33272        
33273         Roo.tree.ColumnTree.superclass.render.apply(this);
33274         
33275         this.el.addClass('x-column-tree');
33276         
33277         this.headers = this.el.createChild(
33278             {cls:'x-tree-headers'},this.innerCt.dom);
33279    
33280         var cols = this.columns, c;
33281         var totalWidth = 0;
33282         this.headEls = [];
33283         var  len = cols.length;
33284         for(var i = 0; i < len; i++){
33285              c = cols[i];
33286              totalWidth += c.width;
33287             this.headEls.push(this.headers.createChild({
33288                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33289                  cn: {
33290                      cls:'x-tree-hd-text',
33291                      html: c.header
33292                  },
33293                  style:'width:'+(c.width-this.borderWidth)+'px;'
33294              }));
33295         }
33296         this.headers.createChild({cls:'x-clear'});
33297         // prevent floats from wrapping when clipped
33298         this.headers.setWidth(totalWidth);
33299         //this.innerCt.setWidth(totalWidth);
33300         this.innerCt.setStyle({ overflow: 'auto' });
33301         this.onResize(this.width, this.height);
33302              
33303         
33304     },
33305     onResize : function(w,h)
33306     {
33307         this.height = h;
33308         this.width = w;
33309         // resize cols..
33310         this.innerCt.setWidth(this.width);
33311         this.innerCt.setHeight(this.height-20);
33312         
33313         // headers...
33314         var cols = this.columns, c;
33315         var totalWidth = 0;
33316         var expEl = false;
33317         var len = cols.length;
33318         for(var i = 0; i < len; i++){
33319             c = cols[i];
33320             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33321                 // it's the expander..
33322                 expEl  = this.headEls[i];
33323                 continue;
33324             }
33325             totalWidth += c.width;
33326             
33327         }
33328         if (expEl) {
33329             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33330         }
33331         this.headers.setWidth(w-20);
33332
33333         
33334         
33335         
33336     }
33337 });
33338 /*
33339  * Based on:
33340  * Ext JS Library 1.1.1
33341  * Copyright(c) 2006-2007, Ext JS, LLC.
33342  *
33343  * Originally Released Under LGPL - original licence link has changed is not relivant.
33344  *
33345  * Fork - LGPL
33346  * <script type="text/javascript">
33347  */
33348  
33349 /**
33350  * @class Roo.menu.Menu
33351  * @extends Roo.util.Observable
33352  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33353  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33354  * @constructor
33355  * Creates a new Menu
33356  * @param {Object} config Configuration options
33357  */
33358 Roo.menu.Menu = function(config){
33359     Roo.apply(this, config);
33360     this.id = this.id || Roo.id();
33361     this.addEvents({
33362         /**
33363          * @event beforeshow
33364          * Fires before this menu is displayed
33365          * @param {Roo.menu.Menu} this
33366          */
33367         beforeshow : true,
33368         /**
33369          * @event beforehide
33370          * Fires before this menu is hidden
33371          * @param {Roo.menu.Menu} this
33372          */
33373         beforehide : true,
33374         /**
33375          * @event show
33376          * Fires after this menu is displayed
33377          * @param {Roo.menu.Menu} this
33378          */
33379         show : true,
33380         /**
33381          * @event hide
33382          * Fires after this menu is hidden
33383          * @param {Roo.menu.Menu} this
33384          */
33385         hide : true,
33386         /**
33387          * @event click
33388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33389          * @param {Roo.menu.Menu} this
33390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33391          * @param {Roo.EventObject} e
33392          */
33393         click : true,
33394         /**
33395          * @event mouseover
33396          * Fires when the mouse is hovering over this menu
33397          * @param {Roo.menu.Menu} this
33398          * @param {Roo.EventObject} e
33399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33400          */
33401         mouseover : true,
33402         /**
33403          * @event mouseout
33404          * Fires when the mouse exits this menu
33405          * @param {Roo.menu.Menu} this
33406          * @param {Roo.EventObject} e
33407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33408          */
33409         mouseout : true,
33410         /**
33411          * @event itemclick
33412          * Fires when a menu item contained in this menu is clicked
33413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33414          * @param {Roo.EventObject} e
33415          */
33416         itemclick: true
33417     });
33418     if (this.registerMenu) {
33419         Roo.menu.MenuMgr.register(this);
33420     }
33421     
33422     var mis = this.items;
33423     this.items = new Roo.util.MixedCollection();
33424     if(mis){
33425         this.add.apply(this, mis);
33426     }
33427 };
33428
33429 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33430     /**
33431      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33432      */
33433     minWidth : 120,
33434     /**
33435      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33436      * for bottom-right shadow (defaults to "sides")
33437      */
33438     shadow : "sides",
33439     /**
33440      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33441      * this menu (defaults to "tl-tr?")
33442      */
33443     subMenuAlign : "tl-tr?",
33444     /**
33445      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33446      * relative to its element of origin (defaults to "tl-bl?")
33447      */
33448     defaultAlign : "tl-bl?",
33449     /**
33450      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33451      */
33452     allowOtherMenus : false,
33453     /**
33454      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33455      */
33456     registerMenu : true,
33457
33458     hidden:true,
33459
33460     // private
33461     render : function(){
33462         if(this.el){
33463             return;
33464         }
33465         var el = this.el = new Roo.Layer({
33466             cls: "x-menu",
33467             shadow:this.shadow,
33468             constrain: false,
33469             parentEl: this.parentEl || document.body,
33470             zindex:15000
33471         });
33472
33473         this.keyNav = new Roo.menu.MenuNav(this);
33474
33475         if(this.plain){
33476             el.addClass("x-menu-plain");
33477         }
33478         if(this.cls){
33479             el.addClass(this.cls);
33480         }
33481         // generic focus element
33482         this.focusEl = el.createChild({
33483             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33484         });
33485         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33486         ul.on("click", this.onClick, this);
33487         ul.on("mouseover", this.onMouseOver, this);
33488         ul.on("mouseout", this.onMouseOut, this);
33489         this.items.each(function(item){
33490             var li = document.createElement("li");
33491             li.className = "x-menu-list-item";
33492             ul.dom.appendChild(li);
33493             item.render(li, this);
33494         }, this);
33495         this.ul = ul;
33496         this.autoWidth();
33497     },
33498
33499     // private
33500     autoWidth : function(){
33501         var el = this.el, ul = this.ul;
33502         if(!el){
33503             return;
33504         }
33505         var w = this.width;
33506         if(w){
33507             el.setWidth(w);
33508         }else if(Roo.isIE){
33509             el.setWidth(this.minWidth);
33510             var t = el.dom.offsetWidth; // force recalc
33511             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33512         }
33513     },
33514
33515     // private
33516     delayAutoWidth : function(){
33517         if(this.rendered){
33518             if(!this.awTask){
33519                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33520             }
33521             this.awTask.delay(20);
33522         }
33523     },
33524
33525     // private
33526     findTargetItem : function(e){
33527         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33528         if(t && t.menuItemId){
33529             return this.items.get(t.menuItemId);
33530         }
33531     },
33532
33533     // private
33534     onClick : function(e){
33535         var t;
33536         if(t = this.findTargetItem(e)){
33537             t.onClick(e);
33538             this.fireEvent("click", this, t, e);
33539         }
33540     },
33541
33542     // private
33543     setActiveItem : function(item, autoExpand){
33544         if(item != this.activeItem){
33545             if(this.activeItem){
33546                 this.activeItem.deactivate();
33547             }
33548             this.activeItem = item;
33549             item.activate(autoExpand);
33550         }else if(autoExpand){
33551             item.expandMenu();
33552         }
33553     },
33554
33555     // private
33556     tryActivate : function(start, step){
33557         var items = this.items;
33558         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33559             var item = items.get(i);
33560             if(!item.disabled && item.canActivate){
33561                 this.setActiveItem(item, false);
33562                 return item;
33563             }
33564         }
33565         return false;
33566     },
33567
33568     // private
33569     onMouseOver : function(e){
33570         var t;
33571         if(t = this.findTargetItem(e)){
33572             if(t.canActivate && !t.disabled){
33573                 this.setActiveItem(t, true);
33574             }
33575         }
33576         this.fireEvent("mouseover", this, e, t);
33577     },
33578
33579     // private
33580     onMouseOut : function(e){
33581         var t;
33582         if(t = this.findTargetItem(e)){
33583             if(t == this.activeItem && t.shouldDeactivate(e)){
33584                 this.activeItem.deactivate();
33585                 delete this.activeItem;
33586             }
33587         }
33588         this.fireEvent("mouseout", this, e, t);
33589     },
33590
33591     /**
33592      * Read-only.  Returns true if the menu is currently displayed, else false.
33593      * @type Boolean
33594      */
33595     isVisible : function(){
33596         return this.el && !this.hidden;
33597     },
33598
33599     /**
33600      * Displays this menu relative to another element
33601      * @param {String/HTMLElement/Roo.Element} element The element to align to
33602      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33603      * the element (defaults to this.defaultAlign)
33604      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33605      */
33606     show : function(el, pos, parentMenu){
33607         this.parentMenu = parentMenu;
33608         if(!this.el){
33609             this.render();
33610         }
33611         this.fireEvent("beforeshow", this);
33612         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33613     },
33614
33615     /**
33616      * Displays this menu at a specific xy position
33617      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33618      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33619      */
33620     showAt : function(xy, parentMenu, /* private: */_e){
33621         this.parentMenu = parentMenu;
33622         if(!this.el){
33623             this.render();
33624         }
33625         if(_e !== false){
33626             this.fireEvent("beforeshow", this);
33627             xy = this.el.adjustForConstraints(xy);
33628         }
33629         this.el.setXY(xy);
33630         this.el.show();
33631         this.hidden = false;
33632         this.focus();
33633         this.fireEvent("show", this);
33634     },
33635
33636     focus : function(){
33637         if(!this.hidden){
33638             this.doFocus.defer(50, this);
33639         }
33640     },
33641
33642     doFocus : function(){
33643         if(!this.hidden){
33644             this.focusEl.focus();
33645         }
33646     },
33647
33648     /**
33649      * Hides this menu and optionally all parent menus
33650      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33651      */
33652     hide : function(deep){
33653         if(this.el && this.isVisible()){
33654             this.fireEvent("beforehide", this);
33655             if(this.activeItem){
33656                 this.activeItem.deactivate();
33657                 this.activeItem = null;
33658             }
33659             this.el.hide();
33660             this.hidden = true;
33661             this.fireEvent("hide", this);
33662         }
33663         if(deep === true && this.parentMenu){
33664             this.parentMenu.hide(true);
33665         }
33666     },
33667
33668     /**
33669      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33670      * Any of the following are valid:
33671      * <ul>
33672      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33673      * <li>An HTMLElement object which will be converted to a menu item</li>
33674      * <li>A menu item config object that will be created as a new menu item</li>
33675      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33676      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33677      * </ul>
33678      * Usage:
33679      * <pre><code>
33680 // Create the menu
33681 var menu = new Roo.menu.Menu();
33682
33683 // Create a menu item to add by reference
33684 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33685
33686 // Add a bunch of items at once using different methods.
33687 // Only the last item added will be returned.
33688 var item = menu.add(
33689     menuItem,                // add existing item by ref
33690     'Dynamic Item',          // new TextItem
33691     '-',                     // new separator
33692     { text: 'Config Item' }  // new item by config
33693 );
33694 </code></pre>
33695      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33696      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33697      */
33698     add : function(){
33699         var a = arguments, l = a.length, item;
33700         for(var i = 0; i < l; i++){
33701             var el = a[i];
33702             if(el.render){ // some kind of Item
33703                 item = this.addItem(el);
33704             }else if(typeof el == "string"){ // string
33705                 if(el == "separator" || el == "-"){
33706                     item = this.addSeparator();
33707                 }else{
33708                     item = this.addText(el);
33709                 }
33710             }else if(el.tagName || el.el){ // element
33711                 item = this.addElement(el);
33712             }else if(typeof el == "object"){ // must be menu item config?
33713                 item = this.addMenuItem(el);
33714             }
33715         }
33716         return item;
33717     },
33718
33719     /**
33720      * Returns this menu's underlying {@link Roo.Element} object
33721      * @return {Roo.Element} The element
33722      */
33723     getEl : function(){
33724         if(!this.el){
33725             this.render();
33726         }
33727         return this.el;
33728     },
33729
33730     /**
33731      * Adds a separator bar to the menu
33732      * @return {Roo.menu.Item} The menu item that was added
33733      */
33734     addSeparator : function(){
33735         return this.addItem(new Roo.menu.Separator());
33736     },
33737
33738     /**
33739      * Adds an {@link Roo.Element} object to the menu
33740      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33741      * @return {Roo.menu.Item} The menu item that was added
33742      */
33743     addElement : function(el){
33744         return this.addItem(new Roo.menu.BaseItem(el));
33745     },
33746
33747     /**
33748      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33749      * @param {Roo.menu.Item} item The menu item to add
33750      * @return {Roo.menu.Item} The menu item that was added
33751      */
33752     addItem : function(item){
33753         this.items.add(item);
33754         if(this.ul){
33755             var li = document.createElement("li");
33756             li.className = "x-menu-list-item";
33757             this.ul.dom.appendChild(li);
33758             item.render(li, this);
33759             this.delayAutoWidth();
33760         }
33761         return item;
33762     },
33763
33764     /**
33765      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33766      * @param {Object} config A MenuItem config object
33767      * @return {Roo.menu.Item} The menu item that was added
33768      */
33769     addMenuItem : function(config){
33770         if(!(config instanceof Roo.menu.Item)){
33771             if(typeof config.checked == "boolean"){ // must be check menu item config?
33772                 config = new Roo.menu.CheckItem(config);
33773             }else{
33774                 config = new Roo.menu.Item(config);
33775             }
33776         }
33777         return this.addItem(config);
33778     },
33779
33780     /**
33781      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33782      * @param {String} text The text to display in the menu item
33783      * @return {Roo.menu.Item} The menu item that was added
33784      */
33785     addText : function(text){
33786         return this.addItem(new Roo.menu.TextItem(text));
33787     },
33788
33789     /**
33790      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33791      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33792      * @param {Roo.menu.Item} item The menu item to add
33793      * @return {Roo.menu.Item} The menu item that was added
33794      */
33795     insert : function(index, item){
33796         this.items.insert(index, item);
33797         if(this.ul){
33798             var li = document.createElement("li");
33799             li.className = "x-menu-list-item";
33800             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33801             item.render(li, this);
33802             this.delayAutoWidth();
33803         }
33804         return item;
33805     },
33806
33807     /**
33808      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33809      * @param {Roo.menu.Item} item The menu item to remove
33810      */
33811     remove : function(item){
33812         this.items.removeKey(item.id);
33813         item.destroy();
33814     },
33815
33816     /**
33817      * Removes and destroys all items in the menu
33818      */
33819     removeAll : function(){
33820         var f;
33821         while(f = this.items.first()){
33822             this.remove(f);
33823         }
33824     }
33825 });
33826
33827 // MenuNav is a private utility class used internally by the Menu
33828 Roo.menu.MenuNav = function(menu){
33829     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33830     this.scope = this.menu = menu;
33831 };
33832
33833 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33834     doRelay : function(e, h){
33835         var k = e.getKey();
33836         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33837             this.menu.tryActivate(0, 1);
33838             return false;
33839         }
33840         return h.call(this.scope || this, e, this.menu);
33841     },
33842
33843     up : function(e, m){
33844         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
33845             m.tryActivate(m.items.length-1, -1);
33846         }
33847     },
33848
33849     down : function(e, m){
33850         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
33851             m.tryActivate(0, 1);
33852         }
33853     },
33854
33855     right : function(e, m){
33856         if(m.activeItem){
33857             m.activeItem.expandMenu(true);
33858         }
33859     },
33860
33861     left : function(e, m){
33862         m.hide();
33863         if(m.parentMenu && m.parentMenu.activeItem){
33864             m.parentMenu.activeItem.activate();
33865         }
33866     },
33867
33868     enter : function(e, m){
33869         if(m.activeItem){
33870             e.stopPropagation();
33871             m.activeItem.onClick(e);
33872             m.fireEvent("click", this, m.activeItem);
33873             return true;
33874         }
33875     }
33876 });/*
33877  * Based on:
33878  * Ext JS Library 1.1.1
33879  * Copyright(c) 2006-2007, Ext JS, LLC.
33880  *
33881  * Originally Released Under LGPL - original licence link has changed is not relivant.
33882  *
33883  * Fork - LGPL
33884  * <script type="text/javascript">
33885  */
33886  
33887 /**
33888  * @class Roo.menu.MenuMgr
33889  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
33890  * @singleton
33891  */
33892 Roo.menu.MenuMgr = function(){
33893    var menus, active, groups = {}, attached = false, lastShow = new Date();
33894
33895    // private - called when first menu is created
33896    function init(){
33897        menus = {};
33898        active = new Roo.util.MixedCollection();
33899        Roo.get(document).addKeyListener(27, function(){
33900            if(active.length > 0){
33901                hideAll();
33902            }
33903        });
33904    }
33905
33906    // private
33907    function hideAll(){
33908        if(active && active.length > 0){
33909            var c = active.clone();
33910            c.each(function(m){
33911                m.hide();
33912            });
33913        }
33914    }
33915
33916    // private
33917    function onHide(m){
33918        active.remove(m);
33919        if(active.length < 1){
33920            Roo.get(document).un("mousedown", onMouseDown);
33921            attached = false;
33922        }
33923    }
33924
33925    // private
33926    function onShow(m){
33927        var last = active.last();
33928        lastShow = new Date();
33929        active.add(m);
33930        if(!attached){
33931            Roo.get(document).on("mousedown", onMouseDown);
33932            attached = true;
33933        }
33934        if(m.parentMenu){
33935           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
33936           m.parentMenu.activeChild = m;
33937        }else if(last && last.isVisible()){
33938           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
33939        }
33940    }
33941
33942    // private
33943    function onBeforeHide(m){
33944        if(m.activeChild){
33945            m.activeChild.hide();
33946        }
33947        if(m.autoHideTimer){
33948            clearTimeout(m.autoHideTimer);
33949            delete m.autoHideTimer;
33950        }
33951    }
33952
33953    // private
33954    function onBeforeShow(m){
33955        var pm = m.parentMenu;
33956        if(!pm && !m.allowOtherMenus){
33957            hideAll();
33958        }else if(pm && pm.activeChild && active != m){
33959            pm.activeChild.hide();
33960        }
33961    }
33962
33963    // private
33964    function onMouseDown(e){
33965        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
33966            hideAll();
33967        }
33968    }
33969
33970    // private
33971    function onBeforeCheck(mi, state){
33972        if(state){
33973            var g = groups[mi.group];
33974            for(var i = 0, l = g.length; i < l; i++){
33975                if(g[i] != mi){
33976                    g[i].setChecked(false);
33977                }
33978            }
33979        }
33980    }
33981
33982    return {
33983
33984        /**
33985         * Hides all menus that are currently visible
33986         */
33987        hideAll : function(){
33988             hideAll();  
33989        },
33990
33991        // private
33992        register : function(menu){
33993            if(!menus){
33994                init();
33995            }
33996            menus[menu.id] = menu;
33997            menu.on("beforehide", onBeforeHide);
33998            menu.on("hide", onHide);
33999            menu.on("beforeshow", onBeforeShow);
34000            menu.on("show", onShow);
34001            var g = menu.group;
34002            if(g && menu.events["checkchange"]){
34003                if(!groups[g]){
34004                    groups[g] = [];
34005                }
34006                groups[g].push(menu);
34007                menu.on("checkchange", onCheck);
34008            }
34009        },
34010
34011         /**
34012          * Returns a {@link Roo.menu.Menu} object
34013          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34014          * be used to generate and return a new Menu instance.
34015          */
34016        get : function(menu){
34017            if(typeof menu == "string"){ // menu id
34018                return menus[menu];
34019            }else if(menu.events){  // menu instance
34020                return menu;
34021            }else if(typeof menu.length == 'number'){ // array of menu items?
34022                return new Roo.menu.Menu({items:menu});
34023            }else{ // otherwise, must be a config
34024                return new Roo.menu.Menu(menu);
34025            }
34026        },
34027
34028        // private
34029        unregister : function(menu){
34030            delete menus[menu.id];
34031            menu.un("beforehide", onBeforeHide);
34032            menu.un("hide", onHide);
34033            menu.un("beforeshow", onBeforeShow);
34034            menu.un("show", onShow);
34035            var g = menu.group;
34036            if(g && menu.events["checkchange"]){
34037                groups[g].remove(menu);
34038                menu.un("checkchange", onCheck);
34039            }
34040        },
34041
34042        // private
34043        registerCheckable : function(menuItem){
34044            var g = menuItem.group;
34045            if(g){
34046                if(!groups[g]){
34047                    groups[g] = [];
34048                }
34049                groups[g].push(menuItem);
34050                menuItem.on("beforecheckchange", onBeforeCheck);
34051            }
34052        },
34053
34054        // private
34055        unregisterCheckable : function(menuItem){
34056            var g = menuItem.group;
34057            if(g){
34058                groups[g].remove(menuItem);
34059                menuItem.un("beforecheckchange", onBeforeCheck);
34060            }
34061        }
34062    };
34063 }();/*
34064  * Based on:
34065  * Ext JS Library 1.1.1
34066  * Copyright(c) 2006-2007, Ext JS, LLC.
34067  *
34068  * Originally Released Under LGPL - original licence link has changed is not relivant.
34069  *
34070  * Fork - LGPL
34071  * <script type="text/javascript">
34072  */
34073  
34074
34075 /**
34076  * @class Roo.menu.BaseItem
34077  * @extends Roo.Component
34078  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34079  * management and base configuration options shared by all menu components.
34080  * @constructor
34081  * Creates a new BaseItem
34082  * @param {Object} config Configuration options
34083  */
34084 Roo.menu.BaseItem = function(config){
34085     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34086
34087     this.addEvents({
34088         /**
34089          * @event click
34090          * Fires when this item is clicked
34091          * @param {Roo.menu.BaseItem} this
34092          * @param {Roo.EventObject} e
34093          */
34094         click: true,
34095         /**
34096          * @event activate
34097          * Fires when this item is activated
34098          * @param {Roo.menu.BaseItem} this
34099          */
34100         activate : true,
34101         /**
34102          * @event deactivate
34103          * Fires when this item is deactivated
34104          * @param {Roo.menu.BaseItem} this
34105          */
34106         deactivate : true
34107     });
34108
34109     if(this.handler){
34110         this.on("click", this.handler, this.scope, true);
34111     }
34112 };
34113
34114 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34115     /**
34116      * @cfg {Function} handler
34117      * A function that will handle the click event of this menu item (defaults to undefined)
34118      */
34119     /**
34120      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34121      */
34122     canActivate : false,
34123     /**
34124      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34125      */
34126     activeClass : "x-menu-item-active",
34127     /**
34128      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34129      */
34130     hideOnClick : true,
34131     /**
34132      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34133      */
34134     hideDelay : 100,
34135
34136     // private
34137     ctype: "Roo.menu.BaseItem",
34138
34139     // private
34140     actionMode : "container",
34141
34142     // private
34143     render : function(container, parentMenu){
34144         this.parentMenu = parentMenu;
34145         Roo.menu.BaseItem.superclass.render.call(this, container);
34146         this.container.menuItemId = this.id;
34147     },
34148
34149     // private
34150     onRender : function(container, position){
34151         this.el = Roo.get(this.el);
34152         container.dom.appendChild(this.el.dom);
34153     },
34154
34155     // private
34156     onClick : function(e){
34157         if(!this.disabled && this.fireEvent("click", this, e) !== false
34158                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34159             this.handleClick(e);
34160         }else{
34161             e.stopEvent();
34162         }
34163     },
34164
34165     // private
34166     activate : function(){
34167         if(this.disabled){
34168             return false;
34169         }
34170         var li = this.container;
34171         li.addClass(this.activeClass);
34172         this.region = li.getRegion().adjust(2, 2, -2, -2);
34173         this.fireEvent("activate", this);
34174         return true;
34175     },
34176
34177     // private
34178     deactivate : function(){
34179         this.container.removeClass(this.activeClass);
34180         this.fireEvent("deactivate", this);
34181     },
34182
34183     // private
34184     shouldDeactivate : function(e){
34185         return !this.region || !this.region.contains(e.getPoint());
34186     },
34187
34188     // private
34189     handleClick : function(e){
34190         if(this.hideOnClick){
34191             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34192         }
34193     },
34194
34195     // private
34196     expandMenu : function(autoActivate){
34197         // do nothing
34198     },
34199
34200     // private
34201     hideMenu : function(){
34202         // do nothing
34203     }
34204 });/*
34205  * Based on:
34206  * Ext JS Library 1.1.1
34207  * Copyright(c) 2006-2007, Ext JS, LLC.
34208  *
34209  * Originally Released Under LGPL - original licence link has changed is not relivant.
34210  *
34211  * Fork - LGPL
34212  * <script type="text/javascript">
34213  */
34214  
34215 /**
34216  * @class Roo.menu.Adapter
34217  * @extends Roo.menu.BaseItem
34218  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34219  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34220  * @constructor
34221  * Creates a new Adapter
34222  * @param {Object} config Configuration options
34223  */
34224 Roo.menu.Adapter = function(component, config){
34225     Roo.menu.Adapter.superclass.constructor.call(this, config);
34226     this.component = component;
34227 };
34228 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34229     // private
34230     canActivate : true,
34231
34232     // private
34233     onRender : function(container, position){
34234         this.component.render(container);
34235         this.el = this.component.getEl();
34236     },
34237
34238     // private
34239     activate : function(){
34240         if(this.disabled){
34241             return false;
34242         }
34243         this.component.focus();
34244         this.fireEvent("activate", this);
34245         return true;
34246     },
34247
34248     // private
34249     deactivate : function(){
34250         this.fireEvent("deactivate", this);
34251     },
34252
34253     // private
34254     disable : function(){
34255         this.component.disable();
34256         Roo.menu.Adapter.superclass.disable.call(this);
34257     },
34258
34259     // private
34260     enable : function(){
34261         this.component.enable();
34262         Roo.menu.Adapter.superclass.enable.call(this);
34263     }
34264 });/*
34265  * Based on:
34266  * Ext JS Library 1.1.1
34267  * Copyright(c) 2006-2007, Ext JS, LLC.
34268  *
34269  * Originally Released Under LGPL - original licence link has changed is not relivant.
34270  *
34271  * Fork - LGPL
34272  * <script type="text/javascript">
34273  */
34274
34275 /**
34276  * @class Roo.menu.TextItem
34277  * @extends Roo.menu.BaseItem
34278  * Adds a static text string to a menu, usually used as either a heading or group separator.
34279  * @constructor
34280  * Creates a new TextItem
34281  * @param {String} text The text to display
34282  */
34283 Roo.menu.TextItem = function(text){
34284     this.text = text;
34285     Roo.menu.TextItem.superclass.constructor.call(this);
34286 };
34287
34288 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34289     /**
34290      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34291      */
34292     hideOnClick : false,
34293     /**
34294      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34295      */
34296     itemCls : "x-menu-text",
34297
34298     // private
34299     onRender : function(){
34300         var s = document.createElement("span");
34301         s.className = this.itemCls;
34302         s.innerHTML = this.text;
34303         this.el = s;
34304         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34305     }
34306 });/*
34307  * Based on:
34308  * Ext JS Library 1.1.1
34309  * Copyright(c) 2006-2007, Ext JS, LLC.
34310  *
34311  * Originally Released Under LGPL - original licence link has changed is not relivant.
34312  *
34313  * Fork - LGPL
34314  * <script type="text/javascript">
34315  */
34316
34317 /**
34318  * @class Roo.menu.Separator
34319  * @extends Roo.menu.BaseItem
34320  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34321  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34322  * @constructor
34323  * @param {Object} config Configuration options
34324  */
34325 Roo.menu.Separator = function(config){
34326     Roo.menu.Separator.superclass.constructor.call(this, config);
34327 };
34328
34329 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34330     /**
34331      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34332      */
34333     itemCls : "x-menu-sep",
34334     /**
34335      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34336      */
34337     hideOnClick : false,
34338
34339     // private
34340     onRender : function(li){
34341         var s = document.createElement("span");
34342         s.className = this.itemCls;
34343         s.innerHTML = "&#160;";
34344         this.el = s;
34345         li.addClass("x-menu-sep-li");
34346         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34347     }
34348 });/*
34349  * Based on:
34350  * Ext JS Library 1.1.1
34351  * Copyright(c) 2006-2007, Ext JS, LLC.
34352  *
34353  * Originally Released Under LGPL - original licence link has changed is not relivant.
34354  *
34355  * Fork - LGPL
34356  * <script type="text/javascript">
34357  */
34358 /**
34359  * @class Roo.menu.Item
34360  * @extends Roo.menu.BaseItem
34361  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34362  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34363  * activation and click handling.
34364  * @constructor
34365  * Creates a new Item
34366  * @param {Object} config Configuration options
34367  */
34368 Roo.menu.Item = function(config){
34369     Roo.menu.Item.superclass.constructor.call(this, config);
34370     if(this.menu){
34371         this.menu = Roo.menu.MenuMgr.get(this.menu);
34372     }
34373 };
34374 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34375     /**
34376      * @cfg {String} icon
34377      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34378      */
34379     /**
34380      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34381      */
34382     itemCls : "x-menu-item",
34383     /**
34384      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34385      */
34386     canActivate : true,
34387     /**
34388      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34389      */
34390     showDelay: 200,
34391     // doc'd in BaseItem
34392     hideDelay: 200,
34393
34394     // private
34395     ctype: "Roo.menu.Item",
34396     
34397     // private
34398     onRender : function(container, position){
34399         var el = document.createElement("a");
34400         el.hideFocus = true;
34401         el.unselectable = "on";
34402         el.href = this.href || "#";
34403         if(this.hrefTarget){
34404             el.target = this.hrefTarget;
34405         }
34406         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34407         el.innerHTML = String.format(
34408                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34409                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34410         this.el = el;
34411         Roo.menu.Item.superclass.onRender.call(this, container, position);
34412     },
34413
34414     /**
34415      * Sets the text to display in this menu item
34416      * @param {String} text The text to display
34417      */
34418     setText : function(text){
34419         this.text = text;
34420         if(this.rendered){
34421             this.el.update(String.format(
34422                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34423                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34424             this.parentMenu.autoWidth();
34425         }
34426     },
34427
34428     // private
34429     handleClick : function(e){
34430         if(!this.href){ // if no link defined, stop the event automatically
34431             e.stopEvent();
34432         }
34433         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34434     },
34435
34436     // private
34437     activate : function(autoExpand){
34438         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34439             this.focus();
34440             if(autoExpand){
34441                 this.expandMenu();
34442             }
34443         }
34444         return true;
34445     },
34446
34447     // private
34448     shouldDeactivate : function(e){
34449         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34450             if(this.menu && this.menu.isVisible()){
34451                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34452             }
34453             return true;
34454         }
34455         return false;
34456     },
34457
34458     // private
34459     deactivate : function(){
34460         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34461         this.hideMenu();
34462     },
34463
34464     // private
34465     expandMenu : function(autoActivate){
34466         if(!this.disabled && this.menu){
34467             clearTimeout(this.hideTimer);
34468             delete this.hideTimer;
34469             if(!this.menu.isVisible() && !this.showTimer){
34470                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34471             }else if (this.menu.isVisible() && autoActivate){
34472                 this.menu.tryActivate(0, 1);
34473             }
34474         }
34475     },
34476
34477     // private
34478     deferExpand : function(autoActivate){
34479         delete this.showTimer;
34480         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34481         if(autoActivate){
34482             this.menu.tryActivate(0, 1);
34483         }
34484     },
34485
34486     // private
34487     hideMenu : function(){
34488         clearTimeout(this.showTimer);
34489         delete this.showTimer;
34490         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34491             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34492         }
34493     },
34494
34495     // private
34496     deferHide : function(){
34497         delete this.hideTimer;
34498         this.menu.hide();
34499     }
34500 });/*
34501  * Based on:
34502  * Ext JS Library 1.1.1
34503  * Copyright(c) 2006-2007, Ext JS, LLC.
34504  *
34505  * Originally Released Under LGPL - original licence link has changed is not relivant.
34506  *
34507  * Fork - LGPL
34508  * <script type="text/javascript">
34509  */
34510  
34511 /**
34512  * @class Roo.menu.CheckItem
34513  * @extends Roo.menu.Item
34514  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34515  * @constructor
34516  * Creates a new CheckItem
34517  * @param {Object} config Configuration options
34518  */
34519 Roo.menu.CheckItem = function(config){
34520     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34521     this.addEvents({
34522         /**
34523          * @event beforecheckchange
34524          * Fires before the checked value is set, providing an opportunity to cancel if needed
34525          * @param {Roo.menu.CheckItem} this
34526          * @param {Boolean} checked The new checked value that will be set
34527          */
34528         "beforecheckchange" : true,
34529         /**
34530          * @event checkchange
34531          * Fires after the checked value has been set
34532          * @param {Roo.menu.CheckItem} this
34533          * @param {Boolean} checked The checked value that was set
34534          */
34535         "checkchange" : true
34536     });
34537     if(this.checkHandler){
34538         this.on('checkchange', this.checkHandler, this.scope);
34539     }
34540 };
34541 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34542     /**
34543      * @cfg {String} group
34544      * All check items with the same group name will automatically be grouped into a single-select
34545      * radio button group (defaults to '')
34546      */
34547     /**
34548      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34549      */
34550     itemCls : "x-menu-item x-menu-check-item",
34551     /**
34552      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34553      */
34554     groupClass : "x-menu-group-item",
34555
34556     /**
34557      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34558      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34559      * initialized with checked = true will be rendered as checked.
34560      */
34561     checked: false,
34562
34563     // private
34564     ctype: "Roo.menu.CheckItem",
34565
34566     // private
34567     onRender : function(c){
34568         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34569         if(this.group){
34570             this.el.addClass(this.groupClass);
34571         }
34572         Roo.menu.MenuMgr.registerCheckable(this);
34573         if(this.checked){
34574             this.checked = false;
34575             this.setChecked(true, true);
34576         }
34577     },
34578
34579     // private
34580     destroy : function(){
34581         if(this.rendered){
34582             Roo.menu.MenuMgr.unregisterCheckable(this);
34583         }
34584         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34585     },
34586
34587     /**
34588      * Set the checked state of this item
34589      * @param {Boolean} checked The new checked value
34590      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34591      */
34592     setChecked : function(state, suppressEvent){
34593         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34594             if(this.container){
34595                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34596             }
34597             this.checked = state;
34598             if(suppressEvent !== true){
34599                 this.fireEvent("checkchange", this, state);
34600             }
34601         }
34602     },
34603
34604     // private
34605     handleClick : function(e){
34606        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34607            this.setChecked(!this.checked);
34608        }
34609        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34610     }
34611 });/*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621  
34622 /**
34623  * @class Roo.menu.DateItem
34624  * @extends Roo.menu.Adapter
34625  * A menu item that wraps the {@link Roo.DatPicker} component.
34626  * @constructor
34627  * Creates a new DateItem
34628  * @param {Object} config Configuration options
34629  */
34630 Roo.menu.DateItem = function(config){
34631     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34632     /** The Roo.DatePicker object @type Roo.DatePicker */
34633     this.picker = this.component;
34634     this.addEvents({select: true});
34635     
34636     this.picker.on("render", function(picker){
34637         picker.getEl().swallowEvent("click");
34638         picker.container.addClass("x-menu-date-item");
34639     });
34640
34641     this.picker.on("select", this.onSelect, this);
34642 };
34643
34644 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34645     // private
34646     onSelect : function(picker, date){
34647         this.fireEvent("select", this, date, picker);
34648         Roo.menu.DateItem.superclass.handleClick.call(this);
34649     }
34650 });/*
34651  * Based on:
34652  * Ext JS Library 1.1.1
34653  * Copyright(c) 2006-2007, Ext JS, LLC.
34654  *
34655  * Originally Released Under LGPL - original licence link has changed is not relivant.
34656  *
34657  * Fork - LGPL
34658  * <script type="text/javascript">
34659  */
34660  
34661 /**
34662  * @class Roo.menu.ColorItem
34663  * @extends Roo.menu.Adapter
34664  * A menu item that wraps the {@link Roo.ColorPalette} component.
34665  * @constructor
34666  * Creates a new ColorItem
34667  * @param {Object} config Configuration options
34668  */
34669 Roo.menu.ColorItem = function(config){
34670     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34671     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34672     this.palette = this.component;
34673     this.relayEvents(this.palette, ["select"]);
34674     if(this.selectHandler){
34675         this.on('select', this.selectHandler, this.scope);
34676     }
34677 };
34678 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34679  * Based on:
34680  * Ext JS Library 1.1.1
34681  * Copyright(c) 2006-2007, Ext JS, LLC.
34682  *
34683  * Originally Released Under LGPL - original licence link has changed is not relivant.
34684  *
34685  * Fork - LGPL
34686  * <script type="text/javascript">
34687  */
34688  
34689
34690 /**
34691  * @class Roo.menu.DateMenu
34692  * @extends Roo.menu.Menu
34693  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34694  * @constructor
34695  * Creates a new DateMenu
34696  * @param {Object} config Configuration options
34697  */
34698 Roo.menu.DateMenu = function(config){
34699     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34700     this.plain = true;
34701     var di = new Roo.menu.DateItem(config);
34702     this.add(di);
34703     /**
34704      * The {@link Roo.DatePicker} instance for this DateMenu
34705      * @type DatePicker
34706      */
34707     this.picker = di.picker;
34708     /**
34709      * @event select
34710      * @param {DatePicker} picker
34711      * @param {Date} date
34712      */
34713     this.relayEvents(di, ["select"]);
34714
34715     this.on('beforeshow', function(){
34716         if(this.picker){
34717             this.picker.hideMonthPicker(true);
34718         }
34719     }, this);
34720 };
34721 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34722     cls:'x-date-menu'
34723 });/*
34724  * Based on:
34725  * Ext JS Library 1.1.1
34726  * Copyright(c) 2006-2007, Ext JS, LLC.
34727  *
34728  * Originally Released Under LGPL - original licence link has changed is not relivant.
34729  *
34730  * Fork - LGPL
34731  * <script type="text/javascript">
34732  */
34733  
34734
34735 /**
34736  * @class Roo.menu.ColorMenu
34737  * @extends Roo.menu.Menu
34738  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34739  * @constructor
34740  * Creates a new ColorMenu
34741  * @param {Object} config Configuration options
34742  */
34743 Roo.menu.ColorMenu = function(config){
34744     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34745     this.plain = true;
34746     var ci = new Roo.menu.ColorItem(config);
34747     this.add(ci);
34748     /**
34749      * The {@link Roo.ColorPalette} instance for this ColorMenu
34750      * @type ColorPalette
34751      */
34752     this.palette = ci.palette;
34753     /**
34754      * @event select
34755      * @param {ColorPalette} palette
34756      * @param {String} color
34757      */
34758     this.relayEvents(ci, ["select"]);
34759 };
34760 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34761  * Based on:
34762  * Ext JS Library 1.1.1
34763  * Copyright(c) 2006-2007, Ext JS, LLC.
34764  *
34765  * Originally Released Under LGPL - original licence link has changed is not relivant.
34766  *
34767  * Fork - LGPL
34768  * <script type="text/javascript">
34769  */
34770  
34771 /**
34772  * @class Roo.form.Field
34773  * @extends Roo.BoxComponent
34774  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34775  * @constructor
34776  * Creates a new Field
34777  * @param {Object} config Configuration options
34778  */
34779 Roo.form.Field = function(config){
34780     Roo.form.Field.superclass.constructor.call(this, config);
34781 };
34782
34783 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34784     /**
34785      * @cfg {String} fieldLabel Label to use when rendering a form.
34786      */
34787        /**
34788      * @cfg {String} qtip Mouse over tip
34789      */
34790      
34791     /**
34792      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34793      */
34794     invalidClass : "x-form-invalid",
34795     /**
34796      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
34797      */
34798     invalidText : "The value in this field is invalid",
34799     /**
34800      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34801      */
34802     focusClass : "x-form-focus",
34803     /**
34804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34805       automatic validation (defaults to "keyup").
34806      */
34807     validationEvent : "keyup",
34808     /**
34809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34810      */
34811     validateOnBlur : true,
34812     /**
34813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34814      */
34815     validationDelay : 250,
34816     /**
34817      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34818      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34819      */
34820     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34821     /**
34822      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34823      */
34824     fieldClass : "x-form-field",
34825     /**
34826      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
34827      *<pre>
34828 Value         Description
34829 -----------   ----------------------------------------------------------------------
34830 qtip          Display a quick tip when the user hovers over the field
34831 title         Display a default browser title attribute popup
34832 under         Add a block div beneath the field containing the error text
34833 side          Add an error icon to the right of the field with a popup on hover
34834 [element id]  Add the error text directly to the innerHTML of the specified element
34835 </pre>
34836      */
34837     msgTarget : 'qtip',
34838     /**
34839      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
34840      */
34841     msgFx : 'normal',
34842
34843     /**
34844      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
34845      */
34846     readOnly : false,
34847
34848     /**
34849      * @cfg {Boolean} disabled True to disable the field (defaults to false).
34850      */
34851     disabled : false,
34852
34853     /**
34854      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
34855      */
34856     inputType : undefined,
34857     
34858     /**
34859      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
34860          */
34861         tabIndex : undefined,
34862         
34863     // private
34864     isFormField : true,
34865
34866     // private
34867     hasFocus : false,
34868     /**
34869      * @property {Roo.Element} fieldEl
34870      * Element Containing the rendered Field (with label etc.)
34871      */
34872     /**
34873      * @cfg {Mixed} value A value to initialize this field with.
34874      */
34875     value : undefined,
34876
34877     /**
34878      * @cfg {String} name The field's HTML name attribute.
34879      */
34880     /**
34881      * @cfg {String} cls A CSS class to apply to the field's underlying element.
34882      */
34883
34884         // private ??
34885         initComponent : function(){
34886         Roo.form.Field.superclass.initComponent.call(this);
34887         this.addEvents({
34888             /**
34889              * @event focus
34890              * Fires when this field receives input focus.
34891              * @param {Roo.form.Field} this
34892              */
34893             focus : true,
34894             /**
34895              * @event blur
34896              * Fires when this field loses input focus.
34897              * @param {Roo.form.Field} this
34898              */
34899             blur : true,
34900             /**
34901              * @event specialkey
34902              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
34903              * {@link Roo.EventObject#getKey} to determine which key was pressed.
34904              * @param {Roo.form.Field} this
34905              * @param {Roo.EventObject} e The event object
34906              */
34907             specialkey : true,
34908             /**
34909              * @event change
34910              * Fires just before the field blurs if the field value has changed.
34911              * @param {Roo.form.Field} this
34912              * @param {Mixed} newValue The new value
34913              * @param {Mixed} oldValue The original value
34914              */
34915             change : true,
34916             /**
34917              * @event invalid
34918              * Fires after the field has been marked as invalid.
34919              * @param {Roo.form.Field} this
34920              * @param {String} msg The validation message
34921              */
34922             invalid : true,
34923             /**
34924              * @event valid
34925              * Fires after the field has been validated with no errors.
34926              * @param {Roo.form.Field} this
34927              */
34928             valid : true
34929         });
34930     },
34931
34932     /**
34933      * Returns the name attribute of the field if available
34934      * @return {String} name The field name
34935      */
34936     getName: function(){
34937          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
34938     },
34939
34940     // private
34941     onRender : function(ct, position){
34942         Roo.form.Field.superclass.onRender.call(this, ct, position);
34943         if(!this.el){
34944             var cfg = this.getAutoCreate();
34945             if(!cfg.name){
34946                 cfg.name = this.name || this.id;
34947             }
34948             if(this.inputType){
34949                 cfg.type = this.inputType;
34950             }
34951             this.el = ct.createChild(cfg, position);
34952         }
34953         var type = this.el.dom.type;
34954         if(type){
34955             if(type == 'password'){
34956                 type = 'text';
34957             }
34958             this.el.addClass('x-form-'+type);
34959         }
34960         if(this.readOnly){
34961             this.el.dom.readOnly = true;
34962         }
34963         if(this.tabIndex !== undefined){
34964             this.el.dom.setAttribute('tabIndex', this.tabIndex);
34965         }
34966
34967         this.el.addClass([this.fieldClass, this.cls]);
34968         this.initValue();
34969     },
34970
34971     /**
34972      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
34973      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
34974      * @return {Roo.form.Field} this
34975      */
34976     applyTo : function(target){
34977         this.allowDomMove = false;
34978         this.el = Roo.get(target);
34979         this.render(this.el.dom.parentNode);
34980         return this;
34981     },
34982
34983     // private
34984     initValue : function(){
34985         if(this.value !== undefined){
34986             this.setValue(this.value);
34987         }else if(this.el.dom.value.length > 0){
34988             this.setValue(this.el.dom.value);
34989         }
34990     },
34991
34992     /**
34993      * Returns true if this field has been changed since it was originally loaded and is not disabled.
34994      */
34995     isDirty : function() {
34996         if(this.disabled) {
34997             return false;
34998         }
34999         return String(this.getValue()) !== String(this.originalValue);
35000     },
35001
35002     // private
35003     afterRender : function(){
35004         Roo.form.Field.superclass.afterRender.call(this);
35005         this.initEvents();
35006     },
35007
35008     // private
35009     fireKey : function(e){
35010         if(e.isNavKeyPress()){
35011             this.fireEvent("specialkey", this, e);
35012         }
35013     },
35014
35015     /**
35016      * Resets the current field value to the originally loaded value and clears any validation messages
35017      */
35018     reset : function(){
35019         this.setValue(this.originalValue);
35020         this.clearInvalid();
35021     },
35022
35023     // private
35024     initEvents : function(){
35025         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35026         this.el.on("focus", this.onFocus,  this);
35027         this.el.on("blur", this.onBlur,  this);
35028
35029         // reference to original value for reset
35030         this.originalValue = this.getValue();
35031     },
35032
35033     // private
35034     onFocus : function(){
35035         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35036             this.el.addClass(this.focusClass);
35037         }
35038         if(!this.hasFocus){
35039             this.hasFocus = true;
35040             this.startValue = this.getValue();
35041             this.fireEvent("focus", this);
35042         }
35043     },
35044
35045     beforeBlur : Roo.emptyFn,
35046
35047     // private
35048     onBlur : function(){
35049         this.beforeBlur();
35050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35051             this.el.removeClass(this.focusClass);
35052         }
35053         this.hasFocus = false;
35054         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35055             this.validate();
35056         }
35057         var v = this.getValue();
35058         if(String(v) !== String(this.startValue)){
35059             this.fireEvent('change', this, v, this.startValue);
35060         }
35061         this.fireEvent("blur", this);
35062     },
35063
35064     /**
35065      * Returns whether or not the field value is currently valid
35066      * @param {Boolean} preventMark True to disable marking the field invalid
35067      * @return {Boolean} True if the value is valid, else false
35068      */
35069     isValid : function(preventMark){
35070         if(this.disabled){
35071             return true;
35072         }
35073         var restore = this.preventMark;
35074         this.preventMark = preventMark === true;
35075         var v = this.validateValue(this.processValue(this.getRawValue()));
35076         this.preventMark = restore;
35077         return v;
35078     },
35079
35080     /**
35081      * Validates the field value
35082      * @return {Boolean} True if the value is valid, else false
35083      */
35084     validate : function(){
35085         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35086             this.clearInvalid();
35087             return true;
35088         }
35089         return false;
35090     },
35091
35092     processValue : function(value){
35093         return value;
35094     },
35095
35096     // private
35097     // Subclasses should provide the validation implementation by overriding this
35098     validateValue : function(value){
35099         return true;
35100     },
35101
35102     /**
35103      * Mark this field as invalid
35104      * @param {String} msg The validation message
35105      */
35106     markInvalid : function(msg){
35107         if(!this.rendered || this.preventMark){ // not rendered
35108             return;
35109         }
35110         this.el.addClass(this.invalidClass);
35111         msg = msg || this.invalidText;
35112         switch(this.msgTarget){
35113             case 'qtip':
35114                 this.el.dom.qtip = msg;
35115                 this.el.dom.qclass = 'x-form-invalid-tip';
35116                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35117                     Roo.QuickTips.enable();
35118                 }
35119                 break;
35120             case 'title':
35121                 this.el.dom.title = msg;
35122                 break;
35123             case 'under':
35124                 if(!this.errorEl){
35125                     var elp = this.el.findParent('.x-form-element', 5, true);
35126                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35127                     this.errorEl.setWidth(elp.getWidth(true)-20);
35128                 }
35129                 this.errorEl.update(msg);
35130                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35131                 break;
35132             case 'side':
35133                 if(!this.errorIcon){
35134                     var elp = this.el.findParent('.x-form-element', 5, true);
35135                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35136                 }
35137                 this.alignErrorIcon();
35138                 this.errorIcon.dom.qtip = msg;
35139                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35140                 this.errorIcon.show();
35141                 this.on('resize', this.alignErrorIcon, this);
35142                 break;
35143             default:
35144                 var t = Roo.getDom(this.msgTarget);
35145                 t.innerHTML = msg;
35146                 t.style.display = this.msgDisplay;
35147                 break;
35148         }
35149         this.fireEvent('invalid', this, msg);
35150     },
35151
35152     // private
35153     alignErrorIcon : function(){
35154         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35155     },
35156
35157     /**
35158      * Clear any invalid styles/messages for this field
35159      */
35160     clearInvalid : function(){
35161         if(!this.rendered || this.preventMark){ // not rendered
35162             return;
35163         }
35164         this.el.removeClass(this.invalidClass);
35165         switch(this.msgTarget){
35166             case 'qtip':
35167                 this.el.dom.qtip = '';
35168                 break;
35169             case 'title':
35170                 this.el.dom.title = '';
35171                 break;
35172             case 'under':
35173                 if(this.errorEl){
35174                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35175                 }
35176                 break;
35177             case 'side':
35178                 if(this.errorIcon){
35179                     this.errorIcon.dom.qtip = '';
35180                     this.errorIcon.hide();
35181                     this.un('resize', this.alignErrorIcon, this);
35182                 }
35183                 break;
35184             default:
35185                 var t = Roo.getDom(this.msgTarget);
35186                 t.innerHTML = '';
35187                 t.style.display = 'none';
35188                 break;
35189         }
35190         this.fireEvent('valid', this);
35191     },
35192
35193     /**
35194      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35195      * @return {Mixed} value The field value
35196      */
35197     getRawValue : function(){
35198         var v = this.el.getValue();
35199         if(v === this.emptyText){
35200             v = '';
35201         }
35202         return v;
35203     },
35204
35205     /**
35206      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35207      * @return {Mixed} value The field value
35208      */
35209     getValue : function(){
35210         var v = this.el.getValue();
35211         if(v === this.emptyText || v === undefined){
35212             v = '';
35213         }
35214         return v;
35215     },
35216
35217     /**
35218      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35219      * @param {Mixed} value The value to set
35220      */
35221     setRawValue : function(v){
35222         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35223     },
35224
35225     /**
35226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35227      * @param {Mixed} value The value to set
35228      */
35229     setValue : function(v){
35230         this.value = v;
35231         if(this.rendered){
35232             this.el.dom.value = (v === null || v === undefined ? '' : v);
35233             this.validate();
35234         }
35235     },
35236
35237     adjustSize : function(w, h){
35238         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35239         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35240         return s;
35241     },
35242
35243     adjustWidth : function(tag, w){
35244         tag = tag.toLowerCase();
35245         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35246             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35247                 if(tag == 'input'){
35248                     return w + 2;
35249                 }
35250                 if(tag = 'textarea'){
35251                     return w-2;
35252                 }
35253             }else if(Roo.isOpera){
35254                 if(tag == 'input'){
35255                     return w + 2;
35256                 }
35257                 if(tag = 'textarea'){
35258                     return w-2;
35259                 }
35260             }
35261         }
35262         return w;
35263     }
35264 });
35265
35266
35267 // anything other than normal should be considered experimental
35268 Roo.form.Field.msgFx = {
35269     normal : {
35270         show: function(msgEl, f){
35271             msgEl.setDisplayed('block');
35272         },
35273
35274         hide : function(msgEl, f){
35275             msgEl.setDisplayed(false).update('');
35276         }
35277     },
35278
35279     slide : {
35280         show: function(msgEl, f){
35281             msgEl.slideIn('t', {stopFx:true});
35282         },
35283
35284         hide : function(msgEl, f){
35285             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35286         }
35287     },
35288
35289     slideRight : {
35290         show: function(msgEl, f){
35291             msgEl.fixDisplay();
35292             msgEl.alignTo(f.el, 'tl-tr');
35293             msgEl.slideIn('l', {stopFx:true});
35294         },
35295
35296         hide : function(msgEl, f){
35297             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35298         }
35299     }
35300 };/*
35301  * Based on:
35302  * Ext JS Library 1.1.1
35303  * Copyright(c) 2006-2007, Ext JS, LLC.
35304  *
35305  * Originally Released Under LGPL - original licence link has changed is not relivant.
35306  *
35307  * Fork - LGPL
35308  * <script type="text/javascript">
35309  */
35310  
35311
35312 /**
35313  * @class Roo.form.TextField
35314  * @extends Roo.form.Field
35315  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35316  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35317  * @constructor
35318  * Creates a new TextField
35319  * @param {Object} config Configuration options
35320  */
35321 Roo.form.TextField = function(config){
35322     Roo.form.TextField.superclass.constructor.call(this, config);
35323     this.addEvents({
35324         /**
35325          * @event autosize
35326          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35327          * according to the default logic, but this event provides a hook for the developer to apply additional
35328          * logic at runtime to resize the field if needed.
35329              * @param {Roo.form.Field} this This text field
35330              * @param {Number} width The new field width
35331              */
35332         autosize : true
35333     });
35334 };
35335
35336 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35337     /**
35338      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35339      */
35340     grow : false,
35341     /**
35342      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35343      */
35344     growMin : 30,
35345     /**
35346      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35347      */
35348     growMax : 800,
35349     /**
35350      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35351      */
35352     vtype : null,
35353     /**
35354      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35355      */
35356     maskRe : null,
35357     /**
35358      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35359      */
35360     disableKeyFilter : false,
35361     /**
35362      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35363      */
35364     allowBlank : true,
35365     /**
35366      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35367      */
35368     minLength : 0,
35369     /**
35370      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35371      */
35372     maxLength : Number.MAX_VALUE,
35373     /**
35374      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35375      */
35376     minLengthText : "The minimum length for this field is {0}",
35377     /**
35378      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35379      */
35380     maxLengthText : "The maximum length for this field is {0}",
35381     /**
35382      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35383      */
35384     selectOnFocus : false,
35385     /**
35386      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35387      */
35388     blankText : "This field is required",
35389     /**
35390      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35391      * If available, this function will be called only after the basic validators all return true, and will be passed the
35392      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35393      */
35394     validator : null,
35395     /**
35396      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35397      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35398      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35399      */
35400     regex : null,
35401     /**
35402      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35403      */
35404     regexText : "",
35405     /**
35406      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35407      */
35408     emptyText : null,
35409     /**
35410      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35411      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35412      */
35413     emptyClass : 'x-form-empty-field',
35414
35415     // private
35416     initEvents : function(){
35417         Roo.form.TextField.superclass.initEvents.call(this);
35418         if(this.validationEvent == 'keyup'){
35419             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35420             this.el.on('keyup', this.filterValidation, this);
35421         }
35422         else if(this.validationEvent !== false){
35423             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35424         }
35425         if(this.selectOnFocus || this.emptyText){
35426             this.on("focus", this.preFocus, this);
35427             if(this.emptyText){
35428                 this.on('blur', this.postBlur, this);
35429                 this.applyEmptyText();
35430             }
35431         }
35432         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35433             this.el.on("keypress", this.filterKeys, this);
35434         }
35435         if(this.grow){
35436             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35437             this.el.on("click", this.autoSize,  this);
35438         }
35439     },
35440
35441     processValue : function(value){
35442         if(this.stripCharsRe){
35443             var newValue = value.replace(this.stripCharsRe, '');
35444             if(newValue !== value){
35445                 this.setRawValue(newValue);
35446                 return newValue;
35447             }
35448         }
35449         return value;
35450     },
35451
35452     filterValidation : function(e){
35453         if(!e.isNavKeyPress()){
35454             this.validationTask.delay(this.validationDelay);
35455         }
35456     },
35457
35458     // private
35459     onKeyUp : function(e){
35460         if(!e.isNavKeyPress()){
35461             this.autoSize();
35462         }
35463     },
35464
35465     /**
35466      * Resets the current field value to the originally-loaded value and clears any validation messages.
35467      * Also adds emptyText and emptyClass if the original value was blank.
35468      */
35469     reset : function(){
35470         Roo.form.TextField.superclass.reset.call(this);
35471         this.applyEmptyText();
35472     },
35473
35474     applyEmptyText : function(){
35475         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35476             this.setRawValue(this.emptyText);
35477             this.el.addClass(this.emptyClass);
35478         }
35479     },
35480
35481     // private
35482     preFocus : function(){
35483         if(this.emptyText){
35484             if(this.el.dom.value == this.emptyText){
35485                 this.setRawValue('');
35486             }
35487             this.el.removeClass(this.emptyClass);
35488         }
35489         if(this.selectOnFocus){
35490             this.el.dom.select();
35491         }
35492     },
35493
35494     // private
35495     postBlur : function(){
35496         this.applyEmptyText();
35497     },
35498
35499     // private
35500     filterKeys : function(e){
35501         var k = e.getKey();
35502         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35503             return;
35504         }
35505         var c = e.getCharCode(), cc = String.fromCharCode(c);
35506         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35507             return;
35508         }
35509         if(!this.maskRe.test(cc)){
35510             e.stopEvent();
35511         }
35512     },
35513
35514     setValue : function(v){
35515         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35516             this.el.removeClass(this.emptyClass);
35517         }
35518         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35519         this.applyEmptyText();
35520         this.autoSize();
35521     },
35522
35523     /**
35524      * Validates a value according to the field's validation rules and marks the field as invalid
35525      * if the validation fails
35526      * @param {Mixed} value The value to validate
35527      * @return {Boolean} True if the value is valid, else false
35528      */
35529     validateValue : function(value){
35530         if(value.length < 1 || value === this.emptyText){ // if it's blank
35531              if(this.allowBlank){
35532                 this.clearInvalid();
35533                 return true;
35534              }else{
35535                 this.markInvalid(this.blankText);
35536                 return false;
35537              }
35538         }
35539         if(value.length < this.minLength){
35540             this.markInvalid(String.format(this.minLengthText, this.minLength));
35541             return false;
35542         }
35543         if(value.length > this.maxLength){
35544             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35545             return false;
35546         }
35547         if(this.vtype){
35548             var vt = Roo.form.VTypes;
35549             if(!vt[this.vtype](value, this)){
35550                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35551                 return false;
35552             }
35553         }
35554         if(typeof this.validator == "function"){
35555             var msg = this.validator(value);
35556             if(msg !== true){
35557                 this.markInvalid(msg);
35558                 return false;
35559             }
35560         }
35561         if(this.regex && !this.regex.test(value)){
35562             this.markInvalid(this.regexText);
35563             return false;
35564         }
35565         return true;
35566     },
35567
35568     /**
35569      * Selects text in this field
35570      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35571      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35572      */
35573     selectText : function(start, end){
35574         var v = this.getRawValue();
35575         if(v.length > 0){
35576             start = start === undefined ? 0 : start;
35577             end = end === undefined ? v.length : end;
35578             var d = this.el.dom;
35579             if(d.setSelectionRange){
35580                 d.setSelectionRange(start, end);
35581             }else if(d.createTextRange){
35582                 var range = d.createTextRange();
35583                 range.moveStart("character", start);
35584                 range.moveEnd("character", v.length-end);
35585                 range.select();
35586             }
35587         }
35588     },
35589
35590     /**
35591      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35592      * This only takes effect if grow = true, and fires the autosize event.
35593      */
35594     autoSize : function(){
35595         if(!this.grow || !this.rendered){
35596             return;
35597         }
35598         if(!this.metrics){
35599             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35600         }
35601         var el = this.el;
35602         var v = el.dom.value;
35603         var d = document.createElement('div');
35604         d.appendChild(document.createTextNode(v));
35605         v = d.innerHTML;
35606         d = null;
35607         v += "&#160;";
35608         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35609         this.el.setWidth(w);
35610         this.fireEvent("autosize", this, w);
35611     }
35612 });/*
35613  * Based on:
35614  * Ext JS Library 1.1.1
35615  * Copyright(c) 2006-2007, Ext JS, LLC.
35616  *
35617  * Originally Released Under LGPL - original licence link has changed is not relivant.
35618  *
35619  * Fork - LGPL
35620  * <script type="text/javascript">
35621  */
35622  
35623 /**
35624  * @class Roo.form.Hidden
35625  * @extends Roo.form.TextField
35626  * Simple Hidden element used on forms 
35627  * 
35628  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35629  * 
35630  * @constructor
35631  * Creates a new Hidden form element.
35632  * @param {Object} config Configuration options
35633  */
35634
35635
35636
35637 // easy hidden field...
35638 Roo.form.Hidden = function(config){
35639     Roo.form.Hidden.superclass.constructor.call(this, config);
35640 };
35641   
35642 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35643     fieldLabel:      '',
35644     inputType:      'hidden',
35645     width:          50,
35646     allowBlank:     true,
35647     labelSeparator: '',
35648     hidden:         true,
35649     itemCls :       'x-form-item-display-none'
35650
35651
35652 });
35653
35654
35655 /*
35656  * Based on:
35657  * Ext JS Library 1.1.1
35658  * Copyright(c) 2006-2007, Ext JS, LLC.
35659  *
35660  * Originally Released Under LGPL - original licence link has changed is not relivant.
35661  *
35662  * Fork - LGPL
35663  * <script type="text/javascript">
35664  */
35665  
35666 /**
35667  * @class Roo.form.TriggerField
35668  * @extends Roo.form.TextField
35669  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35670  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35671  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35672  * for which you can provide a custom implementation.  For example:
35673  * <pre><code>
35674 var trigger = new Roo.form.TriggerField();
35675 trigger.onTriggerClick = myTriggerFn;
35676 trigger.applyTo('my-field');
35677 </code></pre>
35678  *
35679  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35680  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35681  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35682  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35683  * @constructor
35684  * Create a new TriggerField.
35685  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35686  * to the base TextField)
35687  */
35688 Roo.form.TriggerField = function(config){
35689     this.mimicing = false;
35690     Roo.form.TriggerField.superclass.constructor.call(this, config);
35691 };
35692
35693 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35694     /**
35695      * @cfg {String} triggerClass A CSS class to apply to the trigger
35696      */
35697     /**
35698      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35699      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35700      */
35701     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35702     /**
35703      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35704      */
35705     hideTrigger:false,
35706
35707     /** @cfg {Boolean} grow @hide */
35708     /** @cfg {Number} growMin @hide */
35709     /** @cfg {Number} growMax @hide */
35710
35711     /**
35712      * @hide 
35713      * @method
35714      */
35715     autoSize: Roo.emptyFn,
35716     // private
35717     monitorTab : true,
35718     // private
35719     deferHeight : true,
35720
35721     
35722     actionMode : 'wrap',
35723     // private
35724     onResize : function(w, h){
35725         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35726         if(typeof w == 'number'){
35727             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35728         }
35729     },
35730
35731     // private
35732     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35733
35734     // private
35735     getResizeEl : function(){
35736         return this.wrap;
35737     },
35738
35739     // private
35740     getPositionEl : function(){
35741         return this.wrap;
35742     },
35743
35744     // private
35745     alignErrorIcon : function(){
35746         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35747     },
35748
35749     // private
35750     onRender : function(ct, position){
35751         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35752         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35753         this.trigger = this.wrap.createChild(this.triggerConfig ||
35754                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35755         if(this.hideTrigger){
35756             this.trigger.setDisplayed(false);
35757         }
35758         this.initTrigger();
35759         if(!this.width){
35760             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35761         }
35762     },
35763
35764     // private
35765     initTrigger : function(){
35766         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35767         this.trigger.addClassOnOver('x-form-trigger-over');
35768         this.trigger.addClassOnClick('x-form-trigger-click');
35769     },
35770
35771     // private
35772     onDestroy : function(){
35773         if(this.trigger){
35774             this.trigger.removeAllListeners();
35775             this.trigger.remove();
35776         }
35777         if(this.wrap){
35778             this.wrap.remove();
35779         }
35780         Roo.form.TriggerField.superclass.onDestroy.call(this);
35781     },
35782
35783     // private
35784     onFocus : function(){
35785         Roo.form.TriggerField.superclass.onFocus.call(this);
35786         if(!this.mimicing){
35787             this.wrap.addClass('x-trigger-wrap-focus');
35788             this.mimicing = true;
35789             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35790             if(this.monitorTab){
35791                 this.el.on("keydown", this.checkTab, this);
35792             }
35793         }
35794     },
35795
35796     // private
35797     checkTab : function(e){
35798         if(e.getKey() == e.TAB){
35799             this.triggerBlur();
35800         }
35801     },
35802
35803     // private
35804     onBlur : function(){
35805         // do nothing
35806     },
35807
35808     // private
35809     mimicBlur : function(e, t){
35810         if(!this.wrap.contains(t) && this.validateBlur()){
35811             this.triggerBlur();
35812         }
35813     },
35814
35815     // private
35816     triggerBlur : function(){
35817         this.mimicing = false;
35818         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35819         if(this.monitorTab){
35820             this.el.un("keydown", this.checkTab, this);
35821         }
35822         this.wrap.removeClass('x-trigger-wrap-focus');
35823         Roo.form.TriggerField.superclass.onBlur.call(this);
35824     },
35825
35826     // private
35827     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
35828     validateBlur : function(e, t){
35829         return true;
35830     },
35831
35832     // private
35833     onDisable : function(){
35834         Roo.form.TriggerField.superclass.onDisable.call(this);
35835         if(this.wrap){
35836             this.wrap.addClass('x-item-disabled');
35837         }
35838     },
35839
35840     // private
35841     onEnable : function(){
35842         Roo.form.TriggerField.superclass.onEnable.call(this);
35843         if(this.wrap){
35844             this.wrap.removeClass('x-item-disabled');
35845         }
35846     },
35847
35848     // private
35849     onShow : function(){
35850         var ae = this.getActionEl();
35851         
35852         if(ae){
35853             ae.dom.style.display = '';
35854             ae.dom.style.visibility = 'visible';
35855         }
35856     },
35857
35858     // private
35859     
35860     onHide : function(){
35861         var ae = this.getActionEl();
35862         ae.dom.style.display = 'none';
35863     },
35864
35865     /**
35866      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
35867      * by an implementing function.
35868      * @method
35869      * @param {EventObject} e
35870      */
35871     onTriggerClick : Roo.emptyFn
35872 });
35873
35874 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
35875 // to be extended by an implementing class.  For an example of implementing this class, see the custom
35876 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
35877 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
35878     initComponent : function(){
35879         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
35880
35881         this.triggerConfig = {
35882             tag:'span', cls:'x-form-twin-triggers', cn:[
35883             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
35884             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
35885         ]};
35886     },
35887
35888     getTrigger : function(index){
35889         return this.triggers[index];
35890     },
35891
35892     initTrigger : function(){
35893         var ts = this.trigger.select('.x-form-trigger', true);
35894         this.wrap.setStyle('overflow', 'hidden');
35895         var triggerField = this;
35896         ts.each(function(t, all, index){
35897             t.hide = function(){
35898                 var w = triggerField.wrap.getWidth();
35899                 this.dom.style.display = 'none';
35900                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35901             };
35902             t.show = function(){
35903                 var w = triggerField.wrap.getWidth();
35904                 this.dom.style.display = '';
35905                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
35906             };
35907             var triggerIndex = 'Trigger'+(index+1);
35908
35909             if(this['hide'+triggerIndex]){
35910                 t.dom.style.display = 'none';
35911             }
35912             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
35913             t.addClassOnOver('x-form-trigger-over');
35914             t.addClassOnClick('x-form-trigger-click');
35915         }, this);
35916         this.triggers = ts.elements;
35917     },
35918
35919     onTrigger1Click : Roo.emptyFn,
35920     onTrigger2Click : Roo.emptyFn
35921 });/*
35922  * Based on:
35923  * Ext JS Library 1.1.1
35924  * Copyright(c) 2006-2007, Ext JS, LLC.
35925  *
35926  * Originally Released Under LGPL - original licence link has changed is not relivant.
35927  *
35928  * Fork - LGPL
35929  * <script type="text/javascript">
35930  */
35931  
35932 /**
35933  * @class Roo.form.TextArea
35934  * @extends Roo.form.TextField
35935  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
35936  * support for auto-sizing.
35937  * @constructor
35938  * Creates a new TextArea
35939  * @param {Object} config Configuration options
35940  */
35941 Roo.form.TextArea = function(config){
35942     Roo.form.TextArea.superclass.constructor.call(this, config);
35943     // these are provided exchanges for backwards compat
35944     // minHeight/maxHeight were replaced by growMin/growMax to be
35945     // compatible with TextField growing config values
35946     if(this.minHeight !== undefined){
35947         this.growMin = this.minHeight;
35948     }
35949     if(this.maxHeight !== undefined){
35950         this.growMax = this.maxHeight;
35951     }
35952 };
35953
35954 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
35955     /**
35956      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
35957      */
35958     growMin : 60,
35959     /**
35960      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
35961      */
35962     growMax: 1000,
35963     /**
35964      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
35965      * in the field (equivalent to setting overflow: hidden, defaults to false)
35966      */
35967     preventScrollbars: false,
35968     /**
35969      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35970      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
35971      */
35972
35973     // private
35974     onRender : function(ct, position){
35975         if(!this.el){
35976             this.defaultAutoCreate = {
35977                 tag: "textarea",
35978                 style:"width:300px;height:60px;",
35979                 autocomplete: "off"
35980             };
35981         }
35982         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
35983         if(this.grow){
35984             this.textSizeEl = Roo.DomHelper.append(document.body, {
35985                 tag: "pre", cls: "x-form-grow-sizer"
35986             });
35987             if(this.preventScrollbars){
35988                 this.el.setStyle("overflow", "hidden");
35989             }
35990             this.el.setHeight(this.growMin);
35991         }
35992     },
35993
35994     onDestroy : function(){
35995         if(this.textSizeEl){
35996             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
35997         }
35998         Roo.form.TextArea.superclass.onDestroy.call(this);
35999     },
36000
36001     // private
36002     onKeyUp : function(e){
36003         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36004             this.autoSize();
36005         }
36006     },
36007
36008     /**
36009      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36010      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36011      */
36012     autoSize : function(){
36013         if(!this.grow || !this.textSizeEl){
36014             return;
36015         }
36016         var el = this.el;
36017         var v = el.dom.value;
36018         var ts = this.textSizeEl;
36019
36020         ts.innerHTML = '';
36021         ts.appendChild(document.createTextNode(v));
36022         v = ts.innerHTML;
36023
36024         Roo.fly(ts).setWidth(this.el.getWidth());
36025         if(v.length < 1){
36026             v = "&#160;&#160;";
36027         }else{
36028             if(Roo.isIE){
36029                 v = v.replace(/\n/g, '<p>&#160;</p>');
36030             }
36031             v += "&#160;\n&#160;";
36032         }
36033         ts.innerHTML = v;
36034         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36035         if(h != this.lastHeight){
36036             this.lastHeight = h;
36037             this.el.setHeight(h);
36038             this.fireEvent("autosize", this, h);
36039         }
36040     }
36041 });/*
36042  * Based on:
36043  * Ext JS Library 1.1.1
36044  * Copyright(c) 2006-2007, Ext JS, LLC.
36045  *
36046  * Originally Released Under LGPL - original licence link has changed is not relivant.
36047  *
36048  * Fork - LGPL
36049  * <script type="text/javascript">
36050  */
36051  
36052
36053 /**
36054  * @class Roo.form.NumberField
36055  * @extends Roo.form.TextField
36056  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36057  * @constructor
36058  * Creates a new NumberField
36059  * @param {Object} config Configuration options
36060  */
36061 Roo.form.NumberField = function(config){
36062     Roo.form.NumberField.superclass.constructor.call(this, config);
36063 };
36064
36065 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36066     /**
36067      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36068      */
36069     fieldClass: "x-form-field x-form-num-field",
36070     /**
36071      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36072      */
36073     allowDecimals : true,
36074     /**
36075      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36076      */
36077     decimalSeparator : ".",
36078     /**
36079      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36080      */
36081     decimalPrecision : 2,
36082     /**
36083      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36084      */
36085     allowNegative : true,
36086     /**
36087      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36088      */
36089     minValue : Number.NEGATIVE_INFINITY,
36090     /**
36091      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36092      */
36093     maxValue : Number.MAX_VALUE,
36094     /**
36095      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36096      */
36097     minText : "The minimum value for this field is {0}",
36098     /**
36099      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36100      */
36101     maxText : "The maximum value for this field is {0}",
36102     /**
36103      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36104      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36105      */
36106     nanText : "{0} is not a valid number",
36107
36108     // private
36109     initEvents : function(){
36110         Roo.form.NumberField.superclass.initEvents.call(this);
36111         var allowed = "0123456789";
36112         if(this.allowDecimals){
36113             allowed += this.decimalSeparator;
36114         }
36115         if(this.allowNegative){
36116             allowed += "-";
36117         }
36118         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36119         var keyPress = function(e){
36120             var k = e.getKey();
36121             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36122                 return;
36123             }
36124             var c = e.getCharCode();
36125             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36126                 e.stopEvent();
36127             }
36128         };
36129         this.el.on("keypress", keyPress, this);
36130     },
36131
36132     // private
36133     validateValue : function(value){
36134         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36135             return false;
36136         }
36137         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36138              return true;
36139         }
36140         var num = this.parseValue(value);
36141         if(isNaN(num)){
36142             this.markInvalid(String.format(this.nanText, value));
36143             return false;
36144         }
36145         if(num < this.minValue){
36146             this.markInvalid(String.format(this.minText, this.minValue));
36147             return false;
36148         }
36149         if(num > this.maxValue){
36150             this.markInvalid(String.format(this.maxText, this.maxValue));
36151             return false;
36152         }
36153         return true;
36154     },
36155
36156     getValue : function(){
36157         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36158     },
36159
36160     // private
36161     parseValue : function(value){
36162         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36163         return isNaN(value) ? '' : value;
36164     },
36165
36166     // private
36167     fixPrecision : function(value){
36168         var nan = isNaN(value);
36169         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36170             return nan ? '' : value;
36171         }
36172         return parseFloat(value).toFixed(this.decimalPrecision);
36173     },
36174
36175     setValue : function(v){
36176         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36177     },
36178
36179     // private
36180     decimalPrecisionFcn : function(v){
36181         return Math.floor(v);
36182     },
36183
36184     beforeBlur : function(){
36185         var v = this.parseValue(this.getRawValue());
36186         if(v){
36187             this.setValue(this.fixPrecision(v));
36188         }
36189     }
36190 });/*
36191  * Based on:
36192  * Ext JS Library 1.1.1
36193  * Copyright(c) 2006-2007, Ext JS, LLC.
36194  *
36195  * Originally Released Under LGPL - original licence link has changed is not relivant.
36196  *
36197  * Fork - LGPL
36198  * <script type="text/javascript">
36199  */
36200  
36201 /**
36202  * @class Roo.form.DateField
36203  * @extends Roo.form.TriggerField
36204  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36205 * @constructor
36206 * Create a new DateField
36207 * @param {Object} config
36208  */
36209 Roo.form.DateField = function(config){
36210     Roo.form.DateField.superclass.constructor.call(this, config);
36211     
36212       this.addEvents({
36213          
36214         /**
36215          * @event select
36216          * Fires when a date is selected
36217              * @param {Roo.form.DateField} combo This combo box
36218              * @param {Date} date The date selected
36219              */
36220         'select' : true
36221          
36222     });
36223     
36224     
36225     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36226     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36227     this.ddMatch = null;
36228     if(this.disabledDates){
36229         var dd = this.disabledDates;
36230         var re = "(?:";
36231         for(var i = 0; i < dd.length; i++){
36232             re += dd[i];
36233             if(i != dd.length-1) re += "|";
36234         }
36235         this.ddMatch = new RegExp(re + ")");
36236     }
36237 };
36238
36239 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36240     /**
36241      * @cfg {String} format
36242      * The default date format string which can be overriden for localization support.  The format must be
36243      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36244      */
36245     format : "m/d/y",
36246     /**
36247      * @cfg {String} altFormats
36248      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36249      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36250      */
36251     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36252     /**
36253      * @cfg {Array} disabledDays
36254      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36255      */
36256     disabledDays : null,
36257     /**
36258      * @cfg {String} disabledDaysText
36259      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36260      */
36261     disabledDaysText : "Disabled",
36262     /**
36263      * @cfg {Array} disabledDates
36264      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36265      * expression so they are very powerful. Some examples:
36266      * <ul>
36267      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36268      * <li>["03/08", "09/16"] would disable those days for every year</li>
36269      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36270      * <li>["03/../2006"] would disable every day in March 2006</li>
36271      * <li>["^03"] would disable every day in every March</li>
36272      * </ul>
36273      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36274      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36275      */
36276     disabledDates : null,
36277     /**
36278      * @cfg {String} disabledDatesText
36279      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36280      */
36281     disabledDatesText : "Disabled",
36282     /**
36283      * @cfg {Date/String} minValue
36284      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36285      * valid format (defaults to null).
36286      */
36287     minValue : null,
36288     /**
36289      * @cfg {Date/String} maxValue
36290      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36291      * valid format (defaults to null).
36292      */
36293     maxValue : null,
36294     /**
36295      * @cfg {String} minText
36296      * The error text to display when the date in the cell is before minValue (defaults to
36297      * 'The date in this field must be after {minValue}').
36298      */
36299     minText : "The date in this field must be equal to or after {0}",
36300     /**
36301      * @cfg {String} maxText
36302      * The error text to display when the date in the cell is after maxValue (defaults to
36303      * 'The date in this field must be before {maxValue}').
36304      */
36305     maxText : "The date in this field must be equal to or before {0}",
36306     /**
36307      * @cfg {String} invalidText
36308      * The error text to display when the date in the field is invalid (defaults to
36309      * '{value} is not a valid date - it must be in the format {format}').
36310      */
36311     invalidText : "{0} is not a valid date - it must be in the format {1}",
36312     /**
36313      * @cfg {String} triggerClass
36314      * An additional CSS class used to style the trigger button.  The trigger will always get the
36315      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36316      * which displays a calendar icon).
36317      */
36318     triggerClass : 'x-form-date-trigger',
36319     
36320
36321     /**
36322      * @cfg {bool} useIso
36323      * if enabled, then the date field will use a hidden field to store the 
36324      * real value as iso formated date. default (false)
36325      */ 
36326     useIso : false,
36327     /**
36328      * @cfg {String/Object} autoCreate
36329      * A DomHelper element spec, or true for a default element spec (defaults to
36330      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36331      */ 
36332     // private
36333     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36334     
36335     // private
36336     hiddenField: false,
36337     
36338     onRender : function(ct, position)
36339     {
36340         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36341         if (this.useIso) {
36342             this.el.dom.removeAttribute('name'); 
36343             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36344                     'before', true);
36345             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36346             // prevent input submission
36347             this.hiddenName = this.name;
36348         }
36349             
36350             
36351     },
36352     
36353     // private
36354     validateValue : function(value)
36355     {
36356         value = this.formatDate(value);
36357         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36358             return false;
36359         }
36360         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36361              return true;
36362         }
36363         var svalue = value;
36364         value = this.parseDate(value);
36365         if(!value){
36366             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36367             return false;
36368         }
36369         var time = value.getTime();
36370         if(this.minValue && time < this.minValue.getTime()){
36371             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36372             return false;
36373         }
36374         if(this.maxValue && time > this.maxValue.getTime()){
36375             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36376             return false;
36377         }
36378         if(this.disabledDays){
36379             var day = value.getDay();
36380             for(var i = 0; i < this.disabledDays.length; i++) {
36381                 if(day === this.disabledDays[i]){
36382                     this.markInvalid(this.disabledDaysText);
36383                     return false;
36384                 }
36385             }
36386         }
36387         var fvalue = this.formatDate(value);
36388         if(this.ddMatch && this.ddMatch.test(fvalue)){
36389             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36390             return false;
36391         }
36392         return true;
36393     },
36394
36395     // private
36396     // Provides logic to override the default TriggerField.validateBlur which just returns true
36397     validateBlur : function(){
36398         return !this.menu || !this.menu.isVisible();
36399     },
36400
36401     /**
36402      * Returns the current date value of the date field.
36403      * @return {Date} The date value
36404      */
36405     getValue : function(){
36406         
36407         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36408     },
36409
36410     /**
36411      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36412      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36413      * (the default format used is "m/d/y").
36414      * <br />Usage:
36415      * <pre><code>
36416 //All of these calls set the same date value (May 4, 2006)
36417
36418 //Pass a date object:
36419 var dt = new Date('5/4/06');
36420 dateField.setValue(dt);
36421
36422 //Pass a date string (default format):
36423 dateField.setValue('5/4/06');
36424
36425 //Pass a date string (custom format):
36426 dateField.format = 'Y-m-d';
36427 dateField.setValue('2006-5-4');
36428 </code></pre>
36429      * @param {String/Date} date The date or valid date string
36430      */
36431     setValue : function(date){
36432         if (this.hiddenField) {
36433             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36434         }
36435         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36436     },
36437
36438     // private
36439     parseDate : function(value){
36440         if(!value || value instanceof Date){
36441             return value;
36442         }
36443         var v = Date.parseDate(value, this.format);
36444         if(!v && this.altFormats){
36445             if(!this.altFormatsArray){
36446                 this.altFormatsArray = this.altFormats.split("|");
36447             }
36448             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36449                 v = Date.parseDate(value, this.altFormatsArray[i]);
36450             }
36451         }
36452         return v;
36453     },
36454
36455     // private
36456     formatDate : function(date, fmt){
36457         return (!date || !(date instanceof Date)) ?
36458                date : date.dateFormat(fmt || this.format);
36459     },
36460
36461     // private
36462     menuListeners : {
36463         select: function(m, d){
36464             this.setValue(d);
36465             this.fireEvent('select', this, d);
36466         },
36467         show : function(){ // retain focus styling
36468             this.onFocus();
36469         },
36470         hide : function(){
36471             this.focus.defer(10, this);
36472             var ml = this.menuListeners;
36473             this.menu.un("select", ml.select,  this);
36474             this.menu.un("show", ml.show,  this);
36475             this.menu.un("hide", ml.hide,  this);
36476         }
36477     },
36478
36479     // private
36480     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36481     onTriggerClick : function(){
36482         if(this.disabled){
36483             return;
36484         }
36485         if(this.menu == null){
36486             this.menu = new Roo.menu.DateMenu();
36487         }
36488         Roo.apply(this.menu.picker,  {
36489             showClear: this.allowBlank,
36490             minDate : this.minValue,
36491             maxDate : this.maxValue,
36492             disabledDatesRE : this.ddMatch,
36493             disabledDatesText : this.disabledDatesText,
36494             disabledDays : this.disabledDays,
36495             disabledDaysText : this.disabledDaysText,
36496             format : this.format,
36497             minText : String.format(this.minText, this.formatDate(this.minValue)),
36498             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36499         });
36500         this.menu.on(Roo.apply({}, this.menuListeners, {
36501             scope:this
36502         }));
36503         this.menu.picker.setValue(this.getValue() || new Date());
36504         this.menu.show(this.el, "tl-bl?");
36505     },
36506
36507     beforeBlur : function(){
36508         var v = this.parseDate(this.getRawValue());
36509         if(v){
36510             this.setValue(v);
36511         }
36512     }
36513
36514     /** @cfg {Boolean} grow @hide */
36515     /** @cfg {Number} growMin @hide */
36516     /** @cfg {Number} growMax @hide */
36517     /**
36518      * @hide
36519      * @method autoSize
36520      */
36521 });/*
36522  * Based on:
36523  * Ext JS Library 1.1.1
36524  * Copyright(c) 2006-2007, Ext JS, LLC.
36525  *
36526  * Originally Released Under LGPL - original licence link has changed is not relivant.
36527  *
36528  * Fork - LGPL
36529  * <script type="text/javascript">
36530  */
36531  
36532
36533 /**
36534  * @class Roo.form.ComboBox
36535  * @extends Roo.form.TriggerField
36536  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36537  * @constructor
36538  * Create a new ComboBox.
36539  * @param {Object} config Configuration options
36540  */
36541 Roo.form.ComboBox = function(config){
36542     Roo.form.ComboBox.superclass.constructor.call(this, config);
36543     this.addEvents({
36544         /**
36545          * @event expand
36546          * Fires when the dropdown list is expanded
36547              * @param {Roo.form.ComboBox} combo This combo box
36548              */
36549         'expand' : true,
36550         /**
36551          * @event collapse
36552          * Fires when the dropdown list is collapsed
36553              * @param {Roo.form.ComboBox} combo This combo box
36554              */
36555         'collapse' : true,
36556         /**
36557          * @event beforeselect
36558          * Fires before a list item is selected. Return false to cancel the selection.
36559              * @param {Roo.form.ComboBox} combo This combo box
36560              * @param {Roo.data.Record} record The data record returned from the underlying store
36561              * @param {Number} index The index of the selected item in the dropdown list
36562              */
36563         'beforeselect' : true,
36564         /**
36565          * @event select
36566          * Fires when a list item is selected
36567              * @param {Roo.form.ComboBox} combo This combo box
36568              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36569              * @param {Number} index The index of the selected item in the dropdown list
36570              */
36571         'select' : true,
36572         /**
36573          * @event beforequery
36574          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36575          * The event object passed has these properties:
36576              * @param {Roo.form.ComboBox} combo This combo box
36577              * @param {String} query The query
36578              * @param {Boolean} forceAll true to force "all" query
36579              * @param {Boolean} cancel true to cancel the query
36580              * @param {Object} e The query event object
36581              */
36582         'beforequery': true
36583     });
36584     if(this.transform){
36585         this.allowDomMove = false;
36586         var s = Roo.getDom(this.transform);
36587         if(!this.hiddenName){
36588             this.hiddenName = s.name;
36589         }
36590         if(!this.store){
36591             this.mode = 'local';
36592             var d = [], opts = s.options;
36593             for(var i = 0, len = opts.length;i < len; i++){
36594                 var o = opts[i];
36595                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36596                 if(o.selected) {
36597                     this.value = value;
36598                 }
36599                 d.push([value, o.text]);
36600             }
36601             this.store = new Roo.data.SimpleStore({
36602                 'id': 0,
36603                 fields: ['value', 'text'],
36604                 data : d
36605             });
36606             this.valueField = 'value';
36607             this.displayField = 'text';
36608         }
36609         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36610         if(!this.lazyRender){
36611             this.target = true;
36612             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36613             s.parentNode.removeChild(s); // remove it
36614             this.render(this.el.parentNode);
36615         }else{
36616             s.parentNode.removeChild(s); // remove it
36617         }
36618
36619     }
36620     if (this.store) {
36621         this.store = Roo.factory(this.store, Roo.data);
36622     }
36623     
36624     this.selectedIndex = -1;
36625     if(this.mode == 'local'){
36626         if(config.queryDelay === undefined){
36627             this.queryDelay = 10;
36628         }
36629         if(config.minChars === undefined){
36630             this.minChars = 0;
36631         }
36632     }
36633 };
36634
36635 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36636     /**
36637      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36638      */
36639     /**
36640      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36641      * rendering into an Roo.Editor, defaults to false)
36642      */
36643     /**
36644      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36645      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36646      */
36647     /**
36648      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36649      */
36650     /**
36651      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36652      * the dropdown list (defaults to undefined, with no header element)
36653      */
36654
36655      /**
36656      * @cfg {String/Roo.Template} tpl The template to use to render the output
36657      */
36658      
36659     // private
36660     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36661     /**
36662      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36663      */
36664     listWidth: undefined,
36665     /**
36666      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36667      * mode = 'remote' or 'text' if mode = 'local')
36668      */
36669     displayField: undefined,
36670     /**
36671      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36672      * mode = 'remote' or 'value' if mode = 'local'). 
36673      * Note: use of a valueField requires the user make a selection
36674      * in order for a value to be mapped.
36675      */
36676     valueField: undefined,
36677     /**
36678      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36679      * field's data value (defaults to the underlying DOM element's name)
36680      */
36681     hiddenName: undefined,
36682     /**
36683      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36684      */
36685     listClass: '',
36686     /**
36687      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36688      */
36689     selectedClass: 'x-combo-selected',
36690     /**
36691      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36692      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36693      * which displays a downward arrow icon).
36694      */
36695     triggerClass : 'x-form-arrow-trigger',
36696     /**
36697      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36698      */
36699     shadow:'sides',
36700     /**
36701      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36702      * anchor positions (defaults to 'tl-bl')
36703      */
36704     listAlign: 'tl-bl?',
36705     /**
36706      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36707      */
36708     maxHeight: 300,
36709     /**
36710      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36711      * query specified by the allQuery config option (defaults to 'query')
36712      */
36713     triggerAction: 'query',
36714     /**
36715      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36716      * (defaults to 4, does not apply if editable = false)
36717      */
36718     minChars : 4,
36719     /**
36720      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36721      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36722      */
36723     typeAhead: false,
36724     /**
36725      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36726      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36727      */
36728     queryDelay: 500,
36729     /**
36730      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36731      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36732      */
36733     pageSize: 0,
36734     /**
36735      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36736      * when editable = true (defaults to false)
36737      */
36738     selectOnFocus:false,
36739     /**
36740      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36741      */
36742     queryParam: 'query',
36743     /**
36744      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36745      * when mode = 'remote' (defaults to 'Loading...')
36746      */
36747     loadingText: 'Loading...',
36748     /**
36749      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36750      */
36751     resizable: false,
36752     /**
36753      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36754      */
36755     handleHeight : 8,
36756     /**
36757      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36758      * traditional select (defaults to true)
36759      */
36760     editable: true,
36761     /**
36762      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36763      */
36764     allQuery: '',
36765     /**
36766      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36767      */
36768     mode: 'remote',
36769     /**
36770      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36771      * listWidth has a higher value)
36772      */
36773     minListWidth : 70,
36774     /**
36775      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36776      * allow the user to set arbitrary text into the field (defaults to false)
36777      */
36778     forceSelection:false,
36779     /**
36780      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36781      * if typeAhead = true (defaults to 250)
36782      */
36783     typeAheadDelay : 250,
36784     /**
36785      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36786      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36787      */
36788     valueNotFoundText : undefined,
36789     /**
36790      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36791      */
36792     blockFocus : false,
36793     
36794     /**
36795      * @cfg {bool} disableClear Disable showing of clear button.
36796      */
36797     disableClear : false,
36798     
36799     // private
36800     onRender : function(ct, position){
36801         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36802         if(this.hiddenName){
36803             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36804                     'before', true);
36805             this.hiddenField.value =
36806                 this.hiddenValue !== undefined ? this.hiddenValue :
36807                 this.value !== undefined ? this.value : '';
36808
36809             // prevent input submission
36810             this.el.dom.removeAttribute('name');
36811         }
36812         if(Roo.isGecko){
36813             this.el.dom.setAttribute('autocomplete', 'off');
36814         }
36815
36816         var cls = 'x-combo-list';
36817
36818         this.list = new Roo.Layer({
36819             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36820         });
36821
36822         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36823         this.list.setWidth(lw);
36824         this.list.swallowEvent('mousewheel');
36825         this.assetHeight = 0;
36826
36827         if(this.title){
36828             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
36829             this.assetHeight += this.header.getHeight();
36830         }
36831
36832         this.innerList = this.list.createChild({cls:cls+'-inner'});
36833         this.innerList.on('mouseover', this.onViewOver, this);
36834         this.innerList.on('mousemove', this.onViewMove, this);
36835         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36836         
36837         if(this.allowBlank && !this.pageSize && !this.disableClear){
36838             this.footer = this.list.createChild({cls:cls+'-ft'});
36839             this.pageTb = new Roo.Toolbar(this.footer);
36840            
36841         }
36842         if(this.pageSize){
36843             this.footer = this.list.createChild({cls:cls+'-ft'});
36844             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
36845                     {pageSize: this.pageSize});
36846             
36847         }
36848         
36849         if (this.pageTb && this.allowBlank && !this.disableClear) {
36850             var _this = this;
36851             this.pageTb.add(new Roo.Toolbar.Fill(), {
36852                 cls: 'x-btn-icon x-btn-clear',
36853                 text: '&#160;',
36854                 handler: function()
36855                 {
36856                     _this.collapse();
36857                     _this.clearValue();
36858                     _this.onSelect(false, -1);
36859                 }
36860             });
36861         }
36862         if (this.footer) {
36863             this.assetHeight += this.footer.getHeight();
36864         }
36865         
36866
36867         if(!this.tpl){
36868             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
36869         }
36870
36871         this.view = new Roo.View(this.innerList, this.tpl, {
36872             singleSelect:true, store: this.store, selectedClass: this.selectedClass
36873         });
36874
36875         this.view.on('click', this.onViewClick, this);
36876
36877         this.store.on('beforeload', this.onBeforeLoad, this);
36878         this.store.on('load', this.onLoad, this);
36879         this.store.on('loadexception', this.collapse, this);
36880
36881         if(this.resizable){
36882             this.resizer = new Roo.Resizable(this.list,  {
36883                pinned:true, handles:'se'
36884             });
36885             this.resizer.on('resize', function(r, w, h){
36886                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
36887                 this.listWidth = w;
36888                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
36889                 this.restrictHeight();
36890             }, this);
36891             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
36892         }
36893         if(!this.editable){
36894             this.editable = true;
36895             this.setEditable(false);
36896         }
36897     },
36898
36899     // private
36900     initEvents : function(){
36901         Roo.form.ComboBox.superclass.initEvents.call(this);
36902
36903         this.keyNav = new Roo.KeyNav(this.el, {
36904             "up" : function(e){
36905                 this.inKeyMode = true;
36906                 this.selectPrev();
36907             },
36908
36909             "down" : function(e){
36910                 if(!this.isExpanded()){
36911                     this.onTriggerClick();
36912                 }else{
36913                     this.inKeyMode = true;
36914                     this.selectNext();
36915                 }
36916             },
36917
36918             "enter" : function(e){
36919                 this.onViewClick();
36920                 //return true;
36921             },
36922
36923             "esc" : function(e){
36924                 this.collapse();
36925             },
36926
36927             "tab" : function(e){
36928                 this.onViewClick(false);
36929                 return true;
36930             },
36931
36932             scope : this,
36933
36934             doRelay : function(foo, bar, hname){
36935                 if(hname == 'down' || this.scope.isExpanded()){
36936                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
36937                 }
36938                 return true;
36939             },
36940
36941             forceKeyDown: true
36942         });
36943         this.queryDelay = Math.max(this.queryDelay || 10,
36944                 this.mode == 'local' ? 10 : 250);
36945         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
36946         if(this.typeAhead){
36947             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
36948         }
36949         if(this.editable !== false){
36950             this.el.on("keyup", this.onKeyUp, this);
36951         }
36952         if(this.forceSelection){
36953             this.on('blur', this.doForce, this);
36954         }
36955     },
36956
36957     onDestroy : function(){
36958         if(this.view){
36959             this.view.setStore(null);
36960             this.view.el.removeAllListeners();
36961             this.view.el.remove();
36962             this.view.purgeListeners();
36963         }
36964         if(this.list){
36965             this.list.destroy();
36966         }
36967         if(this.store){
36968             this.store.un('beforeload', this.onBeforeLoad, this);
36969             this.store.un('load', this.onLoad, this);
36970             this.store.un('loadexception', this.collapse, this);
36971         }
36972         Roo.form.ComboBox.superclass.onDestroy.call(this);
36973     },
36974
36975     // private
36976     fireKey : function(e){
36977         if(e.isNavKeyPress() && !this.list.isVisible()){
36978             this.fireEvent("specialkey", this, e);
36979         }
36980     },
36981
36982     // private
36983     onResize: function(w, h){
36984         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
36985         if(this.list && this.listWidth === undefined){
36986             var lw = Math.max(w, this.minListWidth);
36987             this.list.setWidth(lw);
36988             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
36989         }
36990     },
36991
36992     /**
36993      * Allow or prevent the user from directly editing the field text.  If false is passed,
36994      * the user will only be able to select from the items defined in the dropdown list.  This method
36995      * is the runtime equivalent of setting the 'editable' config option at config time.
36996      * @param {Boolean} value True to allow the user to directly edit the field text
36997      */
36998     setEditable : function(value){
36999         if(value == this.editable){
37000             return;
37001         }
37002         this.editable = value;
37003         if(!value){
37004             this.el.dom.setAttribute('readOnly', true);
37005             this.el.on('mousedown', this.onTriggerClick,  this);
37006             this.el.addClass('x-combo-noedit');
37007         }else{
37008             this.el.dom.setAttribute('readOnly', false);
37009             this.el.un('mousedown', this.onTriggerClick,  this);
37010             this.el.removeClass('x-combo-noedit');
37011         }
37012     },
37013
37014     // private
37015     onBeforeLoad : function(){
37016         if(!this.hasFocus){
37017             return;
37018         }
37019         this.innerList.update(this.loadingText ?
37020                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37021         this.restrictHeight();
37022         this.selectedIndex = -1;
37023     },
37024
37025     // private
37026     onLoad : function(){
37027         if(!this.hasFocus){
37028             return;
37029         }
37030         if(this.store.getCount() > 0){
37031             this.expand();
37032             this.restrictHeight();
37033             if(this.lastQuery == this.allQuery){
37034                 if(this.editable){
37035                     this.el.dom.select();
37036                 }
37037                 if(!this.selectByValue(this.value, true)){
37038                     this.select(0, true);
37039                 }
37040             }else{
37041                 this.selectNext();
37042                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37043                     this.taTask.delay(this.typeAheadDelay);
37044                 }
37045             }
37046         }else{
37047             this.onEmptyResults();
37048         }
37049         //this.el.focus();
37050     },
37051
37052     // private
37053     onTypeAhead : function(){
37054         if(this.store.getCount() > 0){
37055             var r = this.store.getAt(0);
37056             var newValue = r.data[this.displayField];
37057             var len = newValue.length;
37058             var selStart = this.getRawValue().length;
37059             if(selStart != len){
37060                 this.setRawValue(newValue);
37061                 this.selectText(selStart, newValue.length);
37062             }
37063         }
37064     },
37065
37066     // private
37067     onSelect : function(record, index){
37068         if(this.fireEvent('beforeselect', this, record, index) !== false){
37069             this.setFromData(index > -1 ? record.data : false);
37070             this.collapse();
37071             this.fireEvent('select', this, record, index);
37072         }
37073     },
37074
37075     /**
37076      * Returns the currently selected field value or empty string if no value is set.
37077      * @return {String} value The selected value
37078      */
37079     getValue : function(){
37080         if(this.valueField){
37081             return typeof this.value != 'undefined' ? this.value : '';
37082         }else{
37083             return Roo.form.ComboBox.superclass.getValue.call(this);
37084         }
37085     },
37086
37087     /**
37088      * Clears any text/value currently set in the field
37089      */
37090     clearValue : function(){
37091         if(this.hiddenField){
37092             this.hiddenField.value = '';
37093         }
37094         this.value = '';
37095         this.setRawValue('');
37096         this.lastSelectionText = '';
37097         this.applyEmptyText();
37098     },
37099
37100     /**
37101      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37102      * will be displayed in the field.  If the value does not match the data value of an existing item,
37103      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37104      * Otherwise the field will be blank (although the value will still be set).
37105      * @param {String} value The value to match
37106      */
37107     setValue : function(v){
37108         var text = v;
37109         if(this.valueField){
37110             var r = this.findRecord(this.valueField, v);
37111             if(r){
37112                 text = r.data[this.displayField];
37113             }else if(this.valueNotFoundText !== undefined){
37114                 text = this.valueNotFoundText;
37115             }
37116         }
37117         this.lastSelectionText = text;
37118         if(this.hiddenField){
37119             this.hiddenField.value = v;
37120         }
37121         Roo.form.ComboBox.superclass.setValue.call(this, text);
37122         this.value = v;
37123     },
37124     /**
37125      * @property {Object} the last set data for the element
37126      */
37127     
37128     lastData : false,
37129     /**
37130      * Sets the value of the field based on a object which is related to the record format for the store.
37131      * @param {Object} value the value to set as. or false on reset?
37132      */
37133     setFromData : function(o){
37134         var dv = ''; // display value
37135         var vv = ''; // value value..
37136         this.lastData = o;
37137         if (this.displayField) {
37138             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37139         } else {
37140             // this is an error condition!!!
37141             console.log('no value field set for '+ this.name);
37142         }
37143         
37144         if(this.valueField){
37145             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37146         }
37147         if(this.hiddenField){
37148             this.hiddenField.value = vv;
37149             
37150             this.lastSelectionText = dv;
37151             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37152             this.value = vv;
37153             return;
37154         }
37155         // no hidden field.. - we store the value in 'value', but still display
37156         // display field!!!!
37157         this.lastSelectionText = dv;
37158         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37159         this.value = vv;
37160         
37161         
37162     },
37163     // private
37164     reset : function(){
37165         // overridden so that last data is reset..
37166         this.setValue(this.originalValue);
37167         this.clearInvalid();
37168         this.lastData = false;
37169     },
37170     // private
37171     findRecord : function(prop, value){
37172         var record;
37173         if(this.store.getCount() > 0){
37174             this.store.each(function(r){
37175                 if(r.data[prop] == value){
37176                     record = r;
37177                     return false;
37178                 }
37179             });
37180         }
37181         return record;
37182     },
37183
37184     // private
37185     onViewMove : function(e, t){
37186         this.inKeyMode = false;
37187     },
37188
37189     // private
37190     onViewOver : function(e, t){
37191         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37192             return;
37193         }
37194         var item = this.view.findItemFromChild(t);
37195         if(item){
37196             var index = this.view.indexOf(item);
37197             this.select(index, false);
37198         }
37199     },
37200
37201     // private
37202     onViewClick : function(doFocus){
37203         var index = this.view.getSelectedIndexes()[0];
37204         var r = this.store.getAt(index);
37205         if(r){
37206             this.onSelect(r, index);
37207         }
37208         if(doFocus !== false && !this.blockFocus){
37209             this.el.focus();
37210         }
37211     },
37212
37213     // private
37214     restrictHeight : function(){
37215         this.innerList.dom.style.height = '';
37216         var inner = this.innerList.dom;
37217         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37218         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37219         this.list.beginUpdate();
37220         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37221         this.list.alignTo(this.el, this.listAlign);
37222         this.list.endUpdate();
37223     },
37224
37225     // private
37226     onEmptyResults : function(){
37227         this.collapse();
37228     },
37229
37230     /**
37231      * Returns true if the dropdown list is expanded, else false.
37232      */
37233     isExpanded : function(){
37234         return this.list.isVisible();
37235     },
37236
37237     /**
37238      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37239      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37240      * @param {String} value The data value of the item to select
37241      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37242      * selected item if it is not currently in view (defaults to true)
37243      * @return {Boolean} True if the value matched an item in the list, else false
37244      */
37245     selectByValue : function(v, scrollIntoView){
37246         if(v !== undefined && v !== null){
37247             var r = this.findRecord(this.valueField || this.displayField, v);
37248             if(r){
37249                 this.select(this.store.indexOf(r), scrollIntoView);
37250                 return true;
37251             }
37252         }
37253         return false;
37254     },
37255
37256     /**
37257      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37258      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37259      * @param {Number} index The zero-based index of the list item to select
37260      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37261      * selected item if it is not currently in view (defaults to true)
37262      */
37263     select : function(index, scrollIntoView){
37264         this.selectedIndex = index;
37265         this.view.select(index);
37266         if(scrollIntoView !== false){
37267             var el = this.view.getNode(index);
37268             if(el){
37269                 this.innerList.scrollChildIntoView(el, false);
37270             }
37271         }
37272     },
37273
37274     // private
37275     selectNext : function(){
37276         var ct = this.store.getCount();
37277         if(ct > 0){
37278             if(this.selectedIndex == -1){
37279                 this.select(0);
37280             }else if(this.selectedIndex < ct-1){
37281                 this.select(this.selectedIndex+1);
37282             }
37283         }
37284     },
37285
37286     // private
37287     selectPrev : function(){
37288         var ct = this.store.getCount();
37289         if(ct > 0){
37290             if(this.selectedIndex == -1){
37291                 this.select(0);
37292             }else if(this.selectedIndex != 0){
37293                 this.select(this.selectedIndex-1);
37294             }
37295         }
37296     },
37297
37298     // private
37299     onKeyUp : function(e){
37300         if(this.editable !== false && !e.isSpecialKey()){
37301             this.lastKey = e.getKey();
37302             this.dqTask.delay(this.queryDelay);
37303         }
37304     },
37305
37306     // private
37307     validateBlur : function(){
37308         return !this.list || !this.list.isVisible();   
37309     },
37310
37311     // private
37312     initQuery : function(){
37313         this.doQuery(this.getRawValue());
37314     },
37315
37316     // private
37317     doForce : function(){
37318         if(this.el.dom.value.length > 0){
37319             this.el.dom.value =
37320                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37321             this.applyEmptyText();
37322         }
37323     },
37324
37325     /**
37326      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37327      * query allowing the query action to be canceled if needed.
37328      * @param {String} query The SQL query to execute
37329      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37330      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37331      * saved in the current store (defaults to false)
37332      */
37333     doQuery : function(q, forceAll){
37334         if(q === undefined || q === null){
37335             q = '';
37336         }
37337         var qe = {
37338             query: q,
37339             forceAll: forceAll,
37340             combo: this,
37341             cancel:false
37342         };
37343         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37344             return false;
37345         }
37346         q = qe.query;
37347         forceAll = qe.forceAll;
37348         if(forceAll === true || (q.length >= this.minChars)){
37349             if(this.lastQuery != q){
37350                 this.lastQuery = q;
37351                 if(this.mode == 'local'){
37352                     this.selectedIndex = -1;
37353                     if(forceAll){
37354                         this.store.clearFilter();
37355                     }else{
37356                         this.store.filter(this.displayField, q);
37357                     }
37358                     this.onLoad();
37359                 }else{
37360                     this.store.baseParams[this.queryParam] = q;
37361                     this.store.load({
37362                         params: this.getParams(q)
37363                     });
37364                     this.expand();
37365                 }
37366             }else{
37367                 this.selectedIndex = -1;
37368                 this.onLoad();   
37369             }
37370         }
37371     },
37372
37373     // private
37374     getParams : function(q){
37375         var p = {};
37376         //p[this.queryParam] = q;
37377         if(this.pageSize){
37378             p.start = 0;
37379             p.limit = this.pageSize;
37380         }
37381         return p;
37382     },
37383
37384     /**
37385      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37386      */
37387     collapse : function(){
37388         if(!this.isExpanded()){
37389             return;
37390         }
37391         this.list.hide();
37392         Roo.get(document).un('mousedown', this.collapseIf, this);
37393         Roo.get(document).un('mousewheel', this.collapseIf, this);
37394         this.fireEvent('collapse', this);
37395     },
37396
37397     // private
37398     collapseIf : function(e){
37399         if(!e.within(this.wrap) && !e.within(this.list)){
37400             this.collapse();
37401         }
37402     },
37403
37404     /**
37405      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37406      */
37407     expand : function(){
37408         if(this.isExpanded() || !this.hasFocus){
37409             return;
37410         }
37411         this.list.alignTo(this.el, this.listAlign);
37412         this.list.show();
37413         Roo.get(document).on('mousedown', this.collapseIf, this);
37414         Roo.get(document).on('mousewheel', this.collapseIf, this);
37415         this.fireEvent('expand', this);
37416     },
37417
37418     // private
37419     // Implements the default empty TriggerField.onTriggerClick function
37420     onTriggerClick : function(){
37421         if(this.disabled){
37422             return;
37423         }
37424         if(this.isExpanded()){
37425             this.collapse();
37426             if (!this.blockFocus) {
37427                 this.el.focus();
37428             }
37429             
37430         }else {
37431             this.hasFocus = true;
37432             if(this.triggerAction == 'all') {
37433                 this.doQuery(this.allQuery, true);
37434             } else {
37435                 this.doQuery(this.getRawValue());
37436             }
37437             if (!this.blockFocus) {
37438                 this.el.focus();
37439             }
37440         }
37441     }
37442
37443     /** 
37444     * @cfg {Boolean} grow 
37445     * @hide 
37446     */
37447     /** 
37448     * @cfg {Number} growMin 
37449     * @hide 
37450     */
37451     /** 
37452     * @cfg {Number} growMax 
37453     * @hide 
37454     */
37455     /**
37456      * @hide
37457      * @method autoSize
37458      */
37459 });/*
37460  * Based on:
37461  * Ext JS Library 1.1.1
37462  * Copyright(c) 2006-2007, Ext JS, LLC.
37463  *
37464  * Originally Released Under LGPL - original licence link has changed is not relivant.
37465  *
37466  * Fork - LGPL
37467  * <script type="text/javascript">
37468  */
37469 /**
37470  * @class Roo.form.Checkbox
37471  * @extends Roo.form.Field
37472  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37473  * @constructor
37474  * Creates a new Checkbox
37475  * @param {Object} config Configuration options
37476  */
37477 Roo.form.Checkbox = function(config){
37478     Roo.form.Checkbox.superclass.constructor.call(this, config);
37479     this.addEvents({
37480         /**
37481          * @event check
37482          * Fires when the checkbox is checked or unchecked.
37483              * @param {Roo.form.Checkbox} this This checkbox
37484              * @param {Boolean} checked The new checked value
37485              */
37486         check : true
37487     });
37488 };
37489
37490 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37491     /**
37492      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37493      */
37494     focusClass : undefined,
37495     /**
37496      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37497      */
37498     fieldClass: "x-form-field",
37499     /**
37500      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37501      */
37502     checked: false,
37503     /**
37504      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37505      * {tag: "input", type: "checkbox", autocomplete: "off"})
37506      */
37507     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37508     /**
37509      * @cfg {String} boxLabel The text that appears beside the checkbox
37510      */
37511     boxLabel : "",
37512     /**
37513      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37514      */  
37515     inputValue : '1',
37516     /**
37517      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37518      */
37519      valueOff: '0', // value when not checked..
37520
37521     actionMode : 'viewEl', 
37522     //
37523     // private
37524     itemCls : 'x-menu-check-item x-form-item',
37525     groupClass : 'x-menu-group-item',
37526     inputType : 'hidden',
37527     
37528     
37529     inSetChecked: false, // check that we are not calling self...
37530     
37531     inputElement: false, // real input element?
37532     basedOn: false, // ????
37533     
37534     isFormField: true, // not sure where this is needed!!!!
37535
37536     onResize : function(){
37537         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37538         if(!this.boxLabel){
37539             this.el.alignTo(this.wrap, 'c-c');
37540         }
37541     },
37542
37543     initEvents : function(){
37544         Roo.form.Checkbox.superclass.initEvents.call(this);
37545         this.el.on("click", this.onClick,  this);
37546         this.el.on("change", this.onClick,  this);
37547     },
37548
37549
37550     getResizeEl : function(){
37551         return this.wrap;
37552     },
37553
37554     getPositionEl : function(){
37555         return this.wrap;
37556     },
37557
37558     // private
37559     onRender : function(ct, position){
37560         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37561         /*
37562         if(this.inputValue !== undefined){
37563             this.el.dom.value = this.inputValue;
37564         }
37565         */
37566         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37567         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37568         var viewEl = this.wrap.createChild({ 
37569             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37570         this.viewEl = viewEl;   
37571         this.wrap.on('click', this.onClick,  this); 
37572         
37573         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37574         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37575         
37576         
37577         
37578         if(this.boxLabel){
37579             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37580         //    viewEl.on('click', this.onClick,  this); 
37581         }
37582         //if(this.checked){
37583             this.setChecked(this.checked);
37584         //}else{
37585             //this.checked = this.el.dom;
37586         //}
37587
37588     },
37589
37590     // private
37591     initValue : Roo.emptyFn,
37592
37593     /**
37594      * Returns the checked state of the checkbox.
37595      * @return {Boolean} True if checked, else false
37596      */
37597     getValue : function(){
37598         if(this.el){
37599             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37600         }
37601         return this.valueOff;
37602         
37603     },
37604
37605         // private
37606     onClick : function(){ 
37607         this.setChecked(!this.checked);
37608
37609         //if(this.el.dom.checked != this.checked){
37610         //    this.setValue(this.el.dom.checked);
37611        // }
37612     },
37613
37614     /**
37615      * Sets the checked state of the checkbox.
37616      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
37617      */
37618     setValue : function(v,suppressEvent){
37619         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37620         //if(this.el && this.el.dom){
37621         //    this.el.dom.checked = this.checked;
37622         //    this.el.dom.defaultChecked = this.checked;
37623         //}
37624         this.setChecked(v === this.inputValue);
37625         //this.fireEvent("check", this, this.checked);
37626     },
37627     // private..
37628     setChecked : function(state,suppressEvent)
37629     {
37630         if (this.inSetChecked) {
37631             this.checked = state;
37632             return;
37633         }
37634         
37635     
37636         if(this.wrap){
37637             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37638         }
37639         this.checked = state;
37640         if(suppressEvent !== true){
37641             this.fireEvent('checkchange', this, state);
37642         }
37643         this.inSetChecked = true;
37644         this.el.dom.value = state ? this.inputValue : this.valueOff;
37645         this.inSetChecked = false;
37646         
37647     },
37648     // handle setting of hidden value by some other method!!?!?
37649     setFromHidden: function()
37650     {
37651         if(!this.el){
37652             return;
37653         }
37654         //console.log("SET FROM HIDDEN");
37655         //alert('setFrom hidden');
37656         this.setValue(this.el.dom.value);
37657     },
37658     
37659     onDestroy : function()
37660     {
37661         if(this.viewEl){
37662             Roo.get(this.viewEl).remove();
37663         }
37664          
37665         Roo.form.Checkbox.superclass.onDestroy.call(this);
37666     }
37667
37668 });/*
37669  * Based on:
37670  * Ext JS Library 1.1.1
37671  * Copyright(c) 2006-2007, Ext JS, LLC.
37672  *
37673  * Originally Released Under LGPL - original licence link has changed is not relivant.
37674  *
37675  * Fork - LGPL
37676  * <script type="text/javascript">
37677  */
37678  
37679 /**
37680  * @class Roo.form.Radio
37681  * @extends Roo.form.Checkbox
37682  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37683  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37684  * @constructor
37685  * Creates a new Radio
37686  * @param {Object} config Configuration options
37687  */
37688 Roo.form.Radio = function(){
37689     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37690 };
37691 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37692     inputType: 'radio',
37693
37694     /**
37695      * If this radio is part of a group, it will return the selected value
37696      * @return {String}
37697      */
37698     getGroupValue : function(){
37699         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37700     }
37701 });//<script type="text/javascript">
37702
37703 /*
37704  * Ext JS Library 1.1.1
37705  * Copyright(c) 2006-2007, Ext JS, LLC.
37706  * licensing@extjs.com
37707  * 
37708  * http://www.extjs.com/license
37709  */
37710  
37711  /*
37712   * 
37713   * Known bugs:
37714   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37715   * - IE ? - no idea how much works there.
37716   * 
37717   * 
37718   * 
37719   */
37720  
37721
37722 /**
37723  * @class Ext.form.HtmlEditor
37724  * @extends Ext.form.Field
37725  * Provides a lightweight HTML Editor component.
37726  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37727  * 
37728  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37729  * supported by this editor.</b><br/><br/>
37730  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37731  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37732  */
37733 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37734       /**
37735      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37736      */
37737     toolbars : false,
37738     /**
37739      * @cfg {String} createLinkText The default text for the create link prompt
37740      */
37741     createLinkText : 'Please enter the URL for the link:',
37742     /**
37743      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37744      */
37745     defaultLinkValue : 'http:/'+'/',
37746    
37747     
37748     // id of frame..
37749     frameId: false,
37750     
37751     // private properties
37752     validationEvent : false,
37753     deferHeight: true,
37754     initialized : false,
37755     activated : false,
37756     sourceEditMode : false,
37757     onFocus : Roo.emptyFn,
37758     iframePad:3,
37759     hideMode:'offsets',
37760     defaultAutoCreate : {
37761         tag: "textarea",
37762         style:"width:500px;height:300px;",
37763         autocomplete: "off"
37764     },
37765
37766     // private
37767     initComponent : function(){
37768         this.addEvents({
37769             /**
37770              * @event initialize
37771              * Fires when the editor is fully initialized (including the iframe)
37772              * @param {HtmlEditor} this
37773              */
37774             initialize: true,
37775             /**
37776              * @event activate
37777              * Fires when the editor is first receives the focus. Any insertion must wait
37778              * until after this event.
37779              * @param {HtmlEditor} this
37780              */
37781             activate: true,
37782              /**
37783              * @event beforesync
37784              * Fires before the textarea is updated with content from the editor iframe. Return false
37785              * to cancel the sync.
37786              * @param {HtmlEditor} this
37787              * @param {String} html
37788              */
37789             beforesync: true,
37790              /**
37791              * @event beforepush
37792              * Fires before the iframe editor is updated with content from the textarea. Return false
37793              * to cancel the push.
37794              * @param {HtmlEditor} this
37795              * @param {String} html
37796              */
37797             beforepush: true,
37798              /**
37799              * @event sync
37800              * Fires when the textarea is updated with content from the editor iframe.
37801              * @param {HtmlEditor} this
37802              * @param {String} html
37803              */
37804             sync: true,
37805              /**
37806              * @event push
37807              * Fires when the iframe editor is updated with content from the textarea.
37808              * @param {HtmlEditor} this
37809              * @param {String} html
37810              */
37811             push: true,
37812              /**
37813              * @event editmodechange
37814              * Fires when the editor switches edit modes
37815              * @param {HtmlEditor} this
37816              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37817              */
37818             editmodechange: true,
37819             /**
37820              * @event editorevent
37821              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
37822              * @param {HtmlEditor} this
37823              */
37824             editorevent: true
37825         })
37826     },
37827
37828     /**
37829      * Protected method that will not generally be called directly. It
37830      * is called when the editor creates its toolbar. Override this method if you need to
37831      * add custom toolbar buttons.
37832      * @param {HtmlEditor} editor
37833      */
37834     createToolbar : function(editor){
37835         if (!editor.toolbars || !editor.toolbars.length) {
37836             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
37837         }
37838         
37839         for (var i =0 ; i < editor.toolbars.length;i++) {
37840             editor.toolbars[i].init(editor);
37841         }
37842          
37843         
37844     },
37845
37846     /**
37847      * Protected method that will not generally be called directly. It
37848      * is called when the editor initializes the iframe with HTML contents. Override this method if you
37849      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
37850      */
37851     getDocMarkup : function(){
37852         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
37853     },
37854
37855     // private
37856     onRender : function(ct, position){
37857         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
37858         this.el.dom.style.border = '0 none';
37859         this.el.dom.setAttribute('tabIndex', -1);
37860         this.el.addClass('x-hidden');
37861         if(Roo.isIE){ // fix IE 1px bogus margin
37862             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
37863         }
37864         this.wrap = this.el.wrap({
37865             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
37866         });
37867
37868         this.frameId = Roo.id();
37869         this.createToolbar(this);
37870         
37871         
37872         
37873         
37874       
37875         
37876         var iframe = this.wrap.createChild({
37877             tag: 'iframe',
37878             id: this.frameId,
37879             name: this.frameId,
37880             frameBorder : 'no',
37881             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
37882         });
37883         
37884        // console.log(iframe);
37885         //this.wrap.dom.appendChild(iframe);
37886
37887         this.iframe = iframe.dom;
37888
37889          this.assignDocWin();
37890         
37891         this.doc.designMode = 'on';
37892        
37893         this.doc.open();
37894         this.doc.write(this.getDocMarkup());
37895         this.doc.close();
37896
37897         
37898         var task = { // must defer to wait for browser to be ready
37899             run : function(){
37900                 //console.log("run task?" + this.doc.readyState);
37901                 this.assignDocWin();
37902                 if(this.doc.body || this.doc.readyState == 'complete'){
37903                     try {
37904                         
37905                        
37906                         this.doc.designMode="on";
37907                     } catch (e) {
37908                         return;
37909                     }
37910                     Roo.TaskMgr.stop(task);
37911                     this.initEditor.defer(10, this);
37912                 }
37913             },
37914             interval : 10,
37915             duration:10000,
37916             scope: this
37917         };
37918         Roo.TaskMgr.start(task);
37919
37920         if(!this.width){
37921             this.setSize(this.el.getSize());
37922         }
37923     },
37924
37925     // private
37926     onResize : function(w, h){
37927         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
37928         if(this.el && this.iframe){
37929             if(typeof w == 'number'){
37930                 var aw = w - this.wrap.getFrameWidth('lr');
37931                 this.el.setWidth(this.adjustWidth('textarea', aw));
37932                 this.iframe.style.width = aw + 'px';
37933             }
37934             if(typeof h == 'number'){
37935                 var tbh = 0;
37936                 for (var i =0; i < this.toolbars.length;i++) {
37937                     // fixme - ask toolbars for heights?
37938                     tbh += this.toolbars[i].tb.el.getHeight();
37939                 }
37940                 
37941                 
37942                 
37943                 
37944                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
37945                 this.el.setHeight(this.adjustWidth('textarea', ah));
37946                 this.iframe.style.height = ah + 'px';
37947                 if(this.doc){
37948                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
37949                 }
37950             }
37951         }
37952     },
37953
37954     /**
37955      * Toggles the editor between standard and source edit mode.
37956      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
37957      */
37958     toggleSourceEdit : function(sourceEditMode){
37959         
37960         this.sourceEditMode = sourceEditMode === true;
37961         
37962         if(this.sourceEditMode){
37963           
37964             this.syncValue();
37965             this.iframe.className = 'x-hidden';
37966             this.el.removeClass('x-hidden');
37967             this.el.dom.removeAttribute('tabIndex');
37968             this.el.focus();
37969         }else{
37970              
37971             this.pushValue();
37972             this.iframe.className = '';
37973             this.el.addClass('x-hidden');
37974             this.el.dom.setAttribute('tabIndex', -1);
37975             this.deferFocus();
37976         }
37977         this.setSize(this.wrap.getSize());
37978         this.fireEvent('editmodechange', this, this.sourceEditMode);
37979     },
37980
37981     // private used internally
37982     createLink : function(){
37983         var url = prompt(this.createLinkText, this.defaultLinkValue);
37984         if(url && url != 'http:/'+'/'){
37985             this.relayCmd('createlink', url);
37986         }
37987     },
37988
37989     // private (for BoxComponent)
37990     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37991
37992     // private (for BoxComponent)
37993     getResizeEl : function(){
37994         return this.wrap;
37995     },
37996
37997     // private (for BoxComponent)
37998     getPositionEl : function(){
37999         return this.wrap;
38000     },
38001
38002     // private
38003     initEvents : function(){
38004         this.originalValue = this.getValue();
38005     },
38006
38007     /**
38008      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38009      * @method
38010      */
38011     markInvalid : Roo.emptyFn,
38012     /**
38013      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38014      * @method
38015      */
38016     clearInvalid : Roo.emptyFn,
38017
38018     setValue : function(v){
38019         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38020         this.pushValue();
38021     },
38022
38023     /**
38024      * Protected method that will not generally be called directly. If you need/want
38025      * custom HTML cleanup, this is the method you should override.
38026      * @param {String} html The HTML to be cleaned
38027      * return {String} The cleaned HTML
38028      */
38029     cleanHtml : function(html){
38030         html = String(html);
38031         if(html.length > 5){
38032             if(Roo.isSafari){ // strip safari nonsense
38033                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38034             }
38035         }
38036         if(html == '&nbsp;'){
38037             html = '';
38038         }
38039         return html;
38040     },
38041
38042     /**
38043      * Protected method that will not generally be called directly. Syncs the contents
38044      * of the editor iframe with the textarea.
38045      */
38046     syncValue : function(){
38047         if(this.initialized){
38048             var bd = (this.doc.body || this.doc.documentElement);
38049             var html = bd.innerHTML;
38050             if(Roo.isSafari){
38051                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38052                 var m = bs.match(/text-align:(.*?);/i);
38053                 if(m && m[1]){
38054                     html = '<div style="'+m[0]+'">' + html + '</div>';
38055                 }
38056             }
38057             html = this.cleanHtml(html);
38058             if(this.fireEvent('beforesync', this, html) !== false){
38059                 this.el.dom.value = html;
38060                 this.fireEvent('sync', this, html);
38061             }
38062         }
38063     },
38064
38065     /**
38066      * Protected method that will not generally be called directly. Pushes the value of the textarea
38067      * into the iframe editor.
38068      */
38069     pushValue : function(){
38070         if(this.initialized){
38071             var v = this.el.dom.value;
38072             if(v.length < 1){
38073                 v = '&#160;';
38074             }
38075             if(this.fireEvent('beforepush', this, v) !== false){
38076                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38077                 this.fireEvent('push', this, v);
38078             }
38079         }
38080     },
38081
38082     // private
38083     deferFocus : function(){
38084         this.focus.defer(10, this);
38085     },
38086
38087     // doc'ed in Field
38088     focus : function(){
38089         if(this.win && !this.sourceEditMode){
38090             this.win.focus();
38091         }else{
38092             this.el.focus();
38093         }
38094     },
38095     
38096     assignDocWin: function()
38097     {
38098         var iframe = this.iframe;
38099         
38100          if(Roo.isIE){
38101             this.doc = iframe.contentWindow.document;
38102             this.win = iframe.contentWindow;
38103         } else {
38104             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38105             this.win = Roo.get(this.frameId).dom.contentWindow;
38106         }
38107     },
38108     
38109     // private
38110     initEditor : function(){
38111         //console.log("INIT EDITOR");
38112         this.assignDocWin();
38113         
38114         
38115         
38116         this.doc.designMode="on";
38117         this.doc.open();
38118         this.doc.write(this.getDocMarkup());
38119         this.doc.close();
38120         
38121         var dbody = (this.doc.body || this.doc.documentElement);
38122         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38123         // this copies styles from the containing element into thsi one..
38124         // not sure why we need all of this..
38125         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38126         ss['background-attachment'] = 'fixed'; // w3c
38127         dbody.bgProperties = 'fixed'; // ie
38128         Roo.DomHelper.applyStyles(dbody, ss);
38129         Roo.EventManager.on(this.doc, {
38130             'mousedown': this.onEditorEvent,
38131             'dblclick': this.onEditorEvent,
38132             'click': this.onEditorEvent,
38133             'keyup': this.onEditorEvent,
38134             buffer:100,
38135             scope: this
38136         });
38137         if(Roo.isGecko){
38138             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38139         }
38140         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38141             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38142         }
38143         this.initialized = true;
38144
38145         this.fireEvent('initialize', this);
38146         this.pushValue();
38147     },
38148
38149     // private
38150     onDestroy : function(){
38151         
38152         
38153         
38154         if(this.rendered){
38155             
38156             for (var i =0; i < this.toolbars.length;i++) {
38157                 // fixme - ask toolbars for heights?
38158                 this.toolbars[i].onDestroy();
38159             }
38160             
38161             this.wrap.dom.innerHTML = '';
38162             this.wrap.remove();
38163         }
38164     },
38165
38166     // private
38167     onFirstFocus : function(){
38168         
38169         this.assignDocWin();
38170         
38171         
38172         this.activated = true;
38173         for (var i =0; i < this.toolbars.length;i++) {
38174             this.toolbars[i].onFirstFocus();
38175         }
38176        
38177         if(Roo.isGecko){ // prevent silly gecko errors
38178             this.win.focus();
38179             var s = this.win.getSelection();
38180             if(!s.focusNode || s.focusNode.nodeType != 3){
38181                 var r = s.getRangeAt(0);
38182                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38183                 r.collapse(true);
38184                 this.deferFocus();
38185             }
38186             try{
38187                 this.execCmd('useCSS', true);
38188                 this.execCmd('styleWithCSS', false);
38189             }catch(e){}
38190         }
38191         this.fireEvent('activate', this);
38192     },
38193
38194     // private
38195     adjustFont: function(btn){
38196         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38197         //if(Roo.isSafari){ // safari
38198         //    adjust *= 2;
38199        // }
38200         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38201         if(Roo.isSafari){ // safari
38202             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38203             v =  (v < 10) ? 10 : v;
38204             v =  (v > 48) ? 48 : v;
38205             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38206             
38207         }
38208         
38209         
38210         v = Math.max(1, v+adjust);
38211         
38212         this.execCmd('FontSize', v  );
38213     },
38214
38215     onEditorEvent : function(e){
38216         this.fireEvent('editorevent', this, e);
38217       //  this.updateToolbar();
38218         this.syncValue();
38219     },
38220
38221     insertTag : function(tg)
38222     {
38223         // could be a bit smarter... -> wrap the current selected tRoo..
38224         
38225         this.execCmd("formatblock",   tg);
38226         
38227     },
38228     
38229     insertText : function(txt)
38230     {
38231         
38232         
38233         range = this.createRange();
38234         range.deleteContents();
38235                //alert(Sender.getAttribute('label'));
38236                
38237         range.insertNode(this.doc.createTextNode(txt));
38238     } ,
38239     
38240     // private
38241     relayBtnCmd : function(btn){
38242         this.relayCmd(btn.cmd);
38243     },
38244
38245     /**
38246      * Executes a Midas editor command on the editor document and performs necessary focus and
38247      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38248      * @param {String} cmd The Midas command
38249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38250      */
38251     relayCmd : function(cmd, value){
38252         this.win.focus();
38253         this.execCmd(cmd, value);
38254         this.fireEvent('editorevent', this);
38255         //this.updateToolbar();
38256         this.deferFocus();
38257     },
38258
38259     /**
38260      * Executes a Midas editor command directly on the editor document.
38261      * For visual commands, you should use {@link #relayCmd} instead.
38262      * <b>This should only be called after the editor is initialized.</b>
38263      * @param {String} cmd The Midas command
38264      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38265      */
38266     execCmd : function(cmd, value){
38267         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38268         this.syncValue();
38269     },
38270
38271     // private
38272     applyCommand : function(e){
38273         if(e.ctrlKey){
38274             var c = e.getCharCode(), cmd;
38275             if(c > 0){
38276                 c = String.fromCharCode(c);
38277                 switch(c){
38278                     case 'b':
38279                         cmd = 'bold';
38280                     break;
38281                     case 'i':
38282                         cmd = 'italic';
38283                     break;
38284                     case 'u':
38285                         cmd = 'underline';
38286                     break;
38287                 }
38288                 if(cmd){
38289                     this.win.focus();
38290                     this.execCmd(cmd);
38291                     this.deferFocus();
38292                     e.preventDefault();
38293                 }
38294             }
38295         }
38296     },
38297
38298     /**
38299      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38300      * to insert tRoo.
38301      * @param {String} text
38302      */
38303     insertAtCursor : function(text){
38304         if(!this.activated){
38305             return;
38306         }
38307         if(Roo.isIE){
38308             this.win.focus();
38309             var r = this.doc.selection.createRange();
38310             if(r){
38311                 r.collapse(true);
38312                 r.pasteHTML(text);
38313                 this.syncValue();
38314                 this.deferFocus();
38315             }
38316         }else if(Roo.isGecko || Roo.isOpera){
38317             this.win.focus();
38318             this.execCmd('InsertHTML', text);
38319             this.deferFocus();
38320         }else if(Roo.isSafari){
38321             this.execCmd('InsertText', text);
38322             this.deferFocus();
38323         }
38324     },
38325
38326     // private
38327     fixKeys : function(){ // load time branching for fastest keydown performance
38328         if(Roo.isIE){
38329             return function(e){
38330                 var k = e.getKey(), r;
38331                 if(k == e.TAB){
38332                     e.stopEvent();
38333                     r = this.doc.selection.createRange();
38334                     if(r){
38335                         r.collapse(true);
38336                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38337                         this.deferFocus();
38338                     }
38339                 }else if(k == e.ENTER){
38340                     r = this.doc.selection.createRange();
38341                     if(r){
38342                         var target = r.parentElement();
38343                         if(!target || target.tagName.toLowerCase() != 'li'){
38344                             e.stopEvent();
38345                             r.pasteHTML('<br />');
38346                             r.collapse(false);
38347                             r.select();
38348                         }
38349                     }
38350                 }
38351             };
38352         }else if(Roo.isOpera){
38353             return function(e){
38354                 var k = e.getKey();
38355                 if(k == e.TAB){
38356                     e.stopEvent();
38357                     this.win.focus();
38358                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38359                     this.deferFocus();
38360                 }
38361             };
38362         }else if(Roo.isSafari){
38363             return function(e){
38364                 var k = e.getKey();
38365                 if(k == e.TAB){
38366                     e.stopEvent();
38367                     this.execCmd('InsertText','\t');
38368                     this.deferFocus();
38369                 }
38370              };
38371         }
38372     }(),
38373     
38374     getAllAncestors: function()
38375     {
38376         var p = this.getSelectedNode();
38377         var a = [];
38378         if (!p) {
38379             a.push(p); // push blank onto stack..
38380             p = this.getParentElement();
38381         }
38382         
38383         
38384         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38385             a.push(p);
38386             p = p.parentNode;
38387         }
38388         a.push(this.doc.body);
38389         return a;
38390     },
38391     lastSel : false,
38392     lastSelNode : false,
38393     
38394     
38395     getSelection : function() 
38396     {
38397         this.assignDocWin();
38398         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38399     },
38400     
38401     getSelectedNode: function() 
38402     {
38403         // this may only work on Gecko!!!
38404         
38405         // should we cache this!!!!
38406         
38407         
38408         
38409          
38410         var range = this.createRange(this.getSelection());
38411         
38412         if (Roo.isIE) {
38413             var parent = range.parentElement();
38414             while (true) {
38415                 var testRange = range.duplicate();
38416                 testRange.moveToElementText(parent);
38417                 if (testRange.inRange(range)) {
38418                     break;
38419                 }
38420                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38421                     break;
38422                 }
38423                 parent = parent.parentElement;
38424             }
38425             return parent;
38426         }
38427         
38428         
38429         var ar = range.endContainer.childNodes;
38430         if (!ar.length) {
38431             ar = range.commonAncestorContainer.childNodes;
38432             //alert(ar.length);
38433         }
38434         var nodes = [];
38435         var other_nodes = [];
38436         var has_other_nodes = false;
38437         for (var i=0;i<ar.length;i++) {
38438             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38439                 continue;
38440             }
38441             // fullly contained node.
38442             
38443             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38444                 nodes.push(ar[i]);
38445                 continue;
38446             }
38447             
38448             // probably selected..
38449             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38450                 other_nodes.push(ar[i]);
38451                 continue;
38452             }
38453             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38454                 continue;
38455             }
38456             
38457             
38458             has_other_nodes = true;
38459         }
38460         if (!nodes.length && other_nodes.length) {
38461             nodes= other_nodes;
38462         }
38463         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38464             return false;
38465         }
38466         
38467         return nodes[0];
38468     },
38469     createRange: function(sel)
38470     {
38471         // this has strange effects when using with 
38472         // top toolbar - not sure if it's a great idea.
38473         //this.editor.contentWindow.focus();
38474         if (typeof sel != "undefined") {
38475             try {
38476                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38477             } catch(e) {
38478                 return this.doc.createRange();
38479             }
38480         } else {
38481             return this.doc.createRange();
38482         }
38483     },
38484     getParentElement: function()
38485     {
38486         
38487         this.assignDocWin();
38488         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38489         
38490         var range = this.createRange(sel);
38491          
38492         try {
38493             var p = range.commonAncestorContainer;
38494             while (p.nodeType == 3) { // text node
38495                 p = p.parentNode;
38496             }
38497             return p;
38498         } catch (e) {
38499             return null;
38500         }
38501     
38502     },
38503     
38504     
38505     
38506     // BC Hacks - cause I cant work out what i was trying to do..
38507     rangeIntersectsNode : function(range, node)
38508     {
38509         var nodeRange = node.ownerDocument.createRange();
38510         try {
38511             nodeRange.selectNode(node);
38512         }
38513         catch (e) {
38514             nodeRange.selectNodeContents(node);
38515         }
38516
38517         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38518                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38519     },
38520     rangeCompareNode : function(range, node) {
38521         var nodeRange = node.ownerDocument.createRange();
38522         try {
38523             nodeRange.selectNode(node);
38524         } catch (e) {
38525             nodeRange.selectNodeContents(node);
38526         }
38527         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38528         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38529
38530         if (nodeIsBefore && !nodeIsAfter)
38531             return 0;
38532         if (!nodeIsBefore && nodeIsAfter)
38533             return 1;
38534         if (nodeIsBefore && nodeIsAfter)
38535             return 2;
38536
38537         return 3;
38538     }
38539
38540     
38541     
38542     // hide stuff that is not compatible
38543     /**
38544      * @event blur
38545      * @hide
38546      */
38547     /**
38548      * @event change
38549      * @hide
38550      */
38551     /**
38552      * @event focus
38553      * @hide
38554      */
38555     /**
38556      * @event specialkey
38557      * @hide
38558      */
38559     /**
38560      * @cfg {String} fieldClass @hide
38561      */
38562     /**
38563      * @cfg {String} focusClass @hide
38564      */
38565     /**
38566      * @cfg {String} autoCreate @hide
38567      */
38568     /**
38569      * @cfg {String} inputType @hide
38570      */
38571     /**
38572      * @cfg {String} invalidClass @hide
38573      */
38574     /**
38575      * @cfg {String} invalidText @hide
38576      */
38577     /**
38578      * @cfg {String} msgFx @hide
38579      */
38580     /**
38581      * @cfg {String} validateOnBlur @hide
38582      */
38583 });// <script type="text/javascript">
38584 /*
38585  * Based on
38586  * Ext JS Library 1.1.1
38587  * Copyright(c) 2006-2007, Ext JS, LLC.
38588  *  
38589  
38590  */
38591
38592 /**
38593  * @class Roo.form.HtmlEditorToolbar1
38594  * Basic Toolbar
38595  * 
38596  * Usage:
38597  *
38598  new Roo.form.HtmlEditor({
38599     ....
38600     toolbars : [
38601         new Roo.form.HtmlEditorToolbar1({
38602             disable : { fonts: 1 , format: 1, ..., ... , ...],
38603             btns : [ .... ]
38604         })
38605     }
38606      
38607  * 
38608  * @cfg {Object} disable List of elements to disable..
38609  * @cfg {Array} btns List of additional buttons.
38610  * 
38611  * 
38612  * NEEDS Extra CSS? 
38613  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38614  */
38615  
38616 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38617 {
38618     
38619     Roo.apply(this, config);
38620     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38621     // dont call parent... till later.
38622 }
38623
38624 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38625     
38626     tb: false,
38627     
38628     rendered: false,
38629     
38630     editor : false,
38631     /**
38632      * @cfg {Object} disable  List of toolbar elements to disable
38633          
38634      */
38635     disable : false,
38636       /**
38637      * @cfg {Array} fontFamilies An array of available font families
38638      */
38639     fontFamilies : [
38640         'Arial',
38641         'Courier New',
38642         'Tahoma',
38643         'Times New Roman',
38644         'Verdana'
38645     ],
38646     
38647     specialChars : [
38648            "&#169;",
38649           "&#174;",     
38650           "&#8482;",    
38651           "&#163;" ,    
38652          // "&#8212;",    
38653           "&#8230;",    
38654           "&#247;" ,    
38655         //  "&#225;" ,     ?? a acute?
38656            "&#8364;"    , //Euro
38657        //   "&#8220;"    ,
38658         //  "&#8221;"    ,
38659         //  "&#8226;"    ,
38660           "&#176;"  //   , // degrees
38661
38662          // "&#233;"     , // e ecute
38663          // "&#250;"     , // u ecute?
38664     ],
38665     inputElements : [ 
38666             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38667             "input:submit", "input:button", "select", "textarea", "label" ],
38668     formats : [
38669         ["p"] ,  
38670         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38671         ["pre"],[ "code"], 
38672         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38673     ],
38674      /**
38675      * @cfg {String} defaultFont default font to use.
38676      */
38677     defaultFont: 'tahoma',
38678    
38679     fontSelect : false,
38680     
38681     
38682     formatCombo : false,
38683     
38684     init : function(editor)
38685     {
38686         this.editor = editor;
38687         
38688         
38689         var fid = editor.frameId;
38690         var etb = this;
38691         function btn(id, toggle, handler){
38692             var xid = fid + '-'+ id ;
38693             return {
38694                 id : xid,
38695                 cmd : id,
38696                 cls : 'x-btn-icon x-edit-'+id,
38697                 enableToggle:toggle !== false,
38698                 scope: editor, // was editor...
38699                 handler:handler||editor.relayBtnCmd,
38700                 clickEvent:'mousedown',
38701                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38702                 tabIndex:-1
38703             };
38704         }
38705         
38706         
38707         
38708         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38709         this.tb = tb;
38710          // stop form submits
38711         tb.el.on('click', function(e){
38712             e.preventDefault(); // what does this do?
38713         });
38714
38715         if(!this.disable.font && !Roo.isSafari){
38716             /* why no safari for fonts
38717             editor.fontSelect = tb.el.createChild({
38718                 tag:'select',
38719                 tabIndex: -1,
38720                 cls:'x-font-select',
38721                 html: editor.createFontOptions()
38722             });
38723             editor.fontSelect.on('change', function(){
38724                 var font = editor.fontSelect.dom.value;
38725                 editor.relayCmd('fontname', font);
38726                 editor.deferFocus();
38727             }, editor);
38728             tb.add(
38729                 editor.fontSelect.dom,
38730                 '-'
38731             );
38732             */
38733         };
38734         if(!this.disable.formats){
38735             this.formatCombo = new Roo.form.ComboBox({
38736                 store: new Roo.data.SimpleStore({
38737                     id : 'tag',
38738                     fields: ['tag'],
38739                     data : this.formats // from states.js
38740                 }),
38741                 blockFocus : true,
38742                 //autoCreate : {tag: "div",  size: "20"},
38743                 displayField:'tag',
38744                 typeAhead: false,
38745                 mode: 'local',
38746                 editable : false,
38747                 triggerAction: 'all',
38748                 emptyText:'Add tag',
38749                 selectOnFocus:true,
38750                 width:135,
38751                 listeners : {
38752                     'select': function(c, r, i) {
38753                         editor.insertTag(r.get('tag'));
38754                         editor.focus();
38755                     }
38756                 }
38757
38758             });
38759             tb.addField(this.formatCombo);
38760             
38761         }
38762         
38763         if(!this.disable.format){
38764             tb.add(
38765                 btn('bold'),
38766                 btn('italic'),
38767                 btn('underline')
38768             );
38769         };
38770         if(!this.disable.fontSize){
38771             tb.add(
38772                 '-',
38773                 
38774                 
38775                 btn('increasefontsize', false, editor.adjustFont),
38776                 btn('decreasefontsize', false, editor.adjustFont)
38777             );
38778         };
38779         
38780         
38781         if(this.disable.colors){
38782             tb.add(
38783                 '-', {
38784                     id:editor.frameId +'-forecolor',
38785                     cls:'x-btn-icon x-edit-forecolor',
38786                     clickEvent:'mousedown',
38787                     tooltip: this.buttonTips['forecolor'] || undefined,
38788                     tabIndex:-1,
38789                     menu : new Roo.menu.ColorMenu({
38790                         allowReselect: true,
38791                         focus: Roo.emptyFn,
38792                         value:'000000',
38793                         plain:true,
38794                         selectHandler: function(cp, color){
38795                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38796                             editor.deferFocus();
38797                         },
38798                         scope: editor,
38799                         clickEvent:'mousedown'
38800                     })
38801                 }, {
38802                     id:editor.frameId +'backcolor',
38803                     cls:'x-btn-icon x-edit-backcolor',
38804                     clickEvent:'mousedown',
38805                     tooltip: this.buttonTips['backcolor'] || undefined,
38806                     tabIndex:-1,
38807                     menu : new Roo.menu.ColorMenu({
38808                         focus: Roo.emptyFn,
38809                         value:'FFFFFF',
38810                         plain:true,
38811                         allowReselect: true,
38812                         selectHandler: function(cp, color){
38813                             if(Roo.isGecko){
38814                                 editor.execCmd('useCSS', false);
38815                                 editor.execCmd('hilitecolor', color);
38816                                 editor.execCmd('useCSS', true);
38817                                 editor.deferFocus();
38818                             }else{
38819                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
38820                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
38821                                 editor.deferFocus();
38822                             }
38823                         },
38824                         scope:editor,
38825                         clickEvent:'mousedown'
38826                     })
38827                 }
38828             );
38829         };
38830         // now add all the items...
38831         
38832
38833         if(!this.disable.alignments){
38834             tb.add(
38835                 '-',
38836                 btn('justifyleft'),
38837                 btn('justifycenter'),
38838                 btn('justifyright')
38839             );
38840         };
38841
38842         //if(!Roo.isSafari){
38843             if(!this.disable.links){
38844                 tb.add(
38845                     '-',
38846                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
38847                 );
38848             };
38849
38850             if(!this.disable.lists){
38851                 tb.add(
38852                     '-',
38853                     btn('insertorderedlist'),
38854                     btn('insertunorderedlist')
38855                 );
38856             }
38857             if(!this.disable.sourceEdit){
38858                 tb.add(
38859                     '-',
38860                     btn('sourceedit', true, function(btn){
38861                         this.toggleSourceEdit(btn.pressed);
38862                     })
38863                 );
38864             }
38865         //}
38866         
38867         var smenu = { };
38868         // special menu.. - needs to be tidied up..
38869         if (!this.disable.special) {
38870             smenu = {
38871                 text: "&#169;",
38872                 cls: 'x-edit-none',
38873                 menu : {
38874                     items : []
38875                    }
38876             };
38877             for (var i =0; i < this.specialChars.length; i++) {
38878                 smenu.menu.items.push({
38879                     
38880                     text: this.specialChars[i],
38881                     handler: function(a,b) {
38882                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
38883                     },
38884                     tabIndex:-1
38885                 });
38886             }
38887             
38888             
38889             tb.add(smenu);
38890             
38891             
38892         }
38893         if (this.btns) {
38894             for(var i =0; i< this.btns.length;i++) {
38895                 var b = this.btns[i];
38896                 b.cls =  'x-edit-none';
38897                 b.scope = editor;
38898                 tb.add(b);
38899             }
38900         
38901         }
38902         
38903         
38904         
38905         // disable everything...
38906         
38907         this.tb.items.each(function(item){
38908            if(item.id != editor.frameId+ '-sourceedit'){
38909                 item.disable();
38910             }
38911         });
38912         this.rendered = true;
38913         
38914         // the all the btns;
38915         editor.on('editorevent', this.updateToolbar, this);
38916         // other toolbars need to implement this..
38917         //editor.on('editmodechange', this.updateToolbar, this);
38918     },
38919     
38920     
38921     
38922     /**
38923      * Protected method that will not generally be called directly. It triggers
38924      * a toolbar update by reading the markup state of the current selection in the editor.
38925      */
38926     updateToolbar: function(){
38927
38928         if(!this.editor.activated){
38929             this.editor.onFirstFocus();
38930             return;
38931         }
38932
38933         var btns = this.tb.items.map, 
38934             doc = this.editor.doc,
38935             frameId = this.editor.frameId;
38936
38937         if(!this.disable.font && !Roo.isSafari){
38938             /*
38939             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
38940             if(name != this.fontSelect.dom.value){
38941                 this.fontSelect.dom.value = name;
38942             }
38943             */
38944         }
38945         if(!this.disable.format){
38946             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
38947             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
38948             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
38949         }
38950         if(!this.disable.alignments){
38951             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
38952             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
38953             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
38954         }
38955         if(!Roo.isSafari && !this.disable.lists){
38956             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
38957             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
38958         }
38959         
38960         var ans = this.editor.getAllAncestors();
38961         if (this.formatCombo) {
38962             
38963             
38964             var store = this.formatCombo.store;
38965             this.formatCombo.setValue("");
38966             for (var i =0; i < ans.length;i++) {
38967                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
38968                     // select it..
38969                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
38970                     break;
38971                 }
38972             }
38973         }
38974         
38975         
38976         
38977         // hides menus... - so this cant be on a menu...
38978         Roo.menu.MenuMgr.hideAll();
38979
38980         //this.editorsyncValue();
38981     },
38982    
38983     
38984     createFontOptions : function(){
38985         var buf = [], fs = this.fontFamilies, ff, lc;
38986         for(var i = 0, len = fs.length; i< len; i++){
38987             ff = fs[i];
38988             lc = ff.toLowerCase();
38989             buf.push(
38990                 '<option value="',lc,'" style="font-family:',ff,';"',
38991                     (this.defaultFont == lc ? ' selected="true">' : '>'),
38992                     ff,
38993                 '</option>'
38994             );
38995         }
38996         return buf.join('');
38997     },
38998     
38999     toggleSourceEdit : function(sourceEditMode){
39000         if(sourceEditMode === undefined){
39001             sourceEditMode = !this.sourceEditMode;
39002         }
39003         this.sourceEditMode = sourceEditMode === true;
39004         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39005         // just toggle the button?
39006         if(btn.pressed !== this.editor.sourceEditMode){
39007             btn.toggle(this.editor.sourceEditMode);
39008             return;
39009         }
39010         
39011         if(this.sourceEditMode){
39012             this.tb.items.each(function(item){
39013                 if(item.cmd != 'sourceedit'){
39014                     item.disable();
39015                 }
39016             });
39017           
39018         }else{
39019             if(this.initialized){
39020                 this.tb.items.each(function(item){
39021                     item.enable();
39022                 });
39023             }
39024             
39025         }
39026         // tell the editor that it's been pressed..
39027         this.editor.toggleSourceEdit(sourceEditMode);
39028        
39029     },
39030      /**
39031      * Object collection of toolbar tooltips for the buttons in the editor. The key
39032      * is the command id associated with that button and the value is a valid QuickTips object.
39033      * For example:
39034 <pre><code>
39035 {
39036     bold : {
39037         title: 'Bold (Ctrl+B)',
39038         text: 'Make the selected text bold.',
39039         cls: 'x-html-editor-tip'
39040     },
39041     italic : {
39042         title: 'Italic (Ctrl+I)',
39043         text: 'Make the selected text italic.',
39044         cls: 'x-html-editor-tip'
39045     },
39046     ...
39047 </code></pre>
39048     * @type Object
39049      */
39050     buttonTips : {
39051         bold : {
39052             title: 'Bold (Ctrl+B)',
39053             text: 'Make the selected text bold.',
39054             cls: 'x-html-editor-tip'
39055         },
39056         italic : {
39057             title: 'Italic (Ctrl+I)',
39058             text: 'Make the selected text italic.',
39059             cls: 'x-html-editor-tip'
39060         },
39061         underline : {
39062             title: 'Underline (Ctrl+U)',
39063             text: 'Underline the selected text.',
39064             cls: 'x-html-editor-tip'
39065         },
39066         increasefontsize : {
39067             title: 'Grow Text',
39068             text: 'Increase the font size.',
39069             cls: 'x-html-editor-tip'
39070         },
39071         decreasefontsize : {
39072             title: 'Shrink Text',
39073             text: 'Decrease the font size.',
39074             cls: 'x-html-editor-tip'
39075         },
39076         backcolor : {
39077             title: 'Text Highlight Color',
39078             text: 'Change the background color of the selected text.',
39079             cls: 'x-html-editor-tip'
39080         },
39081         forecolor : {
39082             title: 'Font Color',
39083             text: 'Change the color of the selected text.',
39084             cls: 'x-html-editor-tip'
39085         },
39086         justifyleft : {
39087             title: 'Align Text Left',
39088             text: 'Align text to the left.',
39089             cls: 'x-html-editor-tip'
39090         },
39091         justifycenter : {
39092             title: 'Center Text',
39093             text: 'Center text in the editor.',
39094             cls: 'x-html-editor-tip'
39095         },
39096         justifyright : {
39097             title: 'Align Text Right',
39098             text: 'Align text to the right.',
39099             cls: 'x-html-editor-tip'
39100         },
39101         insertunorderedlist : {
39102             title: 'Bullet List',
39103             text: 'Start a bulleted list.',
39104             cls: 'x-html-editor-tip'
39105         },
39106         insertorderedlist : {
39107             title: 'Numbered List',
39108             text: 'Start a numbered list.',
39109             cls: 'x-html-editor-tip'
39110         },
39111         createlink : {
39112             title: 'Hyperlink',
39113             text: 'Make the selected text a hyperlink.',
39114             cls: 'x-html-editor-tip'
39115         },
39116         sourceedit : {
39117             title: 'Source Edit',
39118             text: 'Switch to source editing mode.',
39119             cls: 'x-html-editor-tip'
39120         }
39121     },
39122     // private
39123     onDestroy : function(){
39124         if(this.rendered){
39125             
39126             this.tb.items.each(function(item){
39127                 if(item.menu){
39128                     item.menu.removeAll();
39129                     if(item.menu.el){
39130                         item.menu.el.destroy();
39131                     }
39132                 }
39133                 item.destroy();
39134             });
39135              
39136         }
39137     },
39138     onFirstFocus: function() {
39139         this.tb.items.each(function(item){
39140            item.enable();
39141         });
39142     }
39143 });
39144
39145
39146
39147