roojs-ui-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          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * 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.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * 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]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 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
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     
2475     Roo.lib.Ajax = {
2476         request : function(method, uri, cb, data, options) {
2477             if(options){
2478                 var hs = options.headers;
2479                 if(hs){
2480                     for(var h in hs){
2481                         if(hs.hasOwnProperty(h)){
2482                             this.initHeader(h, hs[h], false);
2483                         }
2484                     }
2485                 }
2486                 if(options.xmlData){
2487                     this.initHeader('Content-Type', 'text/xml', false);
2488                     method = 'POST';
2489                     data = options.xmlData;
2490                 }
2491             }
2492
2493             return this.asyncRequest(method, uri, cb, data);
2494         },
2495
2496         serializeForm : function(form) {
2497             if(typeof form == 'string') {
2498                 form = (document.getElementById(form) || document.forms[form]);
2499             }
2500
2501             var el, name, val, disabled, data = '', hasSubmit = false;
2502             for (var i = 0; i < form.elements.length; i++) {
2503                 el = form.elements[i];
2504                 disabled = form.elements[i].disabled;
2505                 name = form.elements[i].name;
2506                 val = form.elements[i].value;
2507
2508                 if (!disabled && name){
2509                     switch (el.type)
2510                             {
2511                         case 'select-one':
2512                         case 'select-multiple':
2513                             for (var j = 0; j < el.options.length; j++) {
2514                                 if (el.options[j].selected) {
2515                                     if (Roo.isIE) {
2516                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2517                                     }
2518                                     else {
2519                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2520                                     }
2521                                 }
2522                             }
2523                             break;
2524                         case 'radio':
2525                         case 'checkbox':
2526                             if (el.checked) {
2527                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2528                             }
2529                             break;
2530                         case 'file':
2531
2532                         case undefined:
2533
2534                         case 'reset':
2535
2536                         case 'button':
2537
2538                             break;
2539                         case 'submit':
2540                             if(hasSubmit == false) {
2541                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2542                                 hasSubmit = true;
2543                             }
2544                             break;
2545                         default:
2546                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2547                             break;
2548                     }
2549                 }
2550             }
2551             data = data.substr(0, data.length - 1);
2552             return data;
2553         },
2554
2555         headers:{},
2556
2557         hasHeaders:false,
2558
2559         useDefaultHeader:true,
2560
2561         defaultPostHeader:'application/x-www-form-urlencoded',
2562
2563         useDefaultXhrHeader:true,
2564
2565         defaultXhrHeader:'XMLHttpRequest',
2566
2567         hasDefaultHeaders:true,
2568
2569         defaultHeaders:{},
2570
2571         poll:{},
2572
2573         timeout:{},
2574
2575         pollInterval:50,
2576
2577         transactionId:0,
2578
2579         setProgId:function(id)
2580         {
2581             this.activeX.unshift(id);
2582         },
2583
2584         setDefaultPostHeader:function(b)
2585         {
2586             this.useDefaultHeader = b;
2587         },
2588
2589         setDefaultXhrHeader:function(b)
2590         {
2591             this.useDefaultXhrHeader = b;
2592         },
2593
2594         setPollingInterval:function(i)
2595         {
2596             if (typeof i == 'number' && isFinite(i)) {
2597                 this.pollInterval = i;
2598             }
2599         },
2600
2601         createXhrObject:function(transactionId)
2602         {
2603             var obj,http;
2604             try
2605             {
2606
2607                 http = new XMLHttpRequest();
2608
2609                 obj = { conn:http, tId:transactionId };
2610             }
2611             catch(e)
2612             {
2613                 for (var i = 0; i < this.activeX.length; ++i) {
2614                     try
2615                     {
2616
2617                         http = new ActiveXObject(this.activeX[i]);
2618
2619                         obj = { conn:http, tId:transactionId };
2620                         break;
2621                     }
2622                     catch(e) {
2623                     }
2624                 }
2625             }
2626             finally
2627             {
2628                 return obj;
2629             }
2630         },
2631
2632         getConnectionObject:function()
2633         {
2634             var o;
2635             var tId = this.transactionId;
2636
2637             try
2638             {
2639                 o = this.createXhrObject(tId);
2640                 if (o) {
2641                     this.transactionId++;
2642                 }
2643             }
2644             catch(e) {
2645             }
2646             finally
2647             {
2648                 return o;
2649             }
2650         },
2651
2652         asyncRequest:function(method, uri, callback, postData)
2653         {
2654             var o = this.getConnectionObject();
2655
2656             if (!o) {
2657                 return null;
2658             }
2659             else {
2660                 o.conn.open(method, uri, true);
2661
2662                 if (this.useDefaultXhrHeader) {
2663                     if (!this.defaultHeaders['X-Requested-With']) {
2664                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2665                     }
2666                 }
2667
2668                 if(postData && this.useDefaultHeader){
2669                     this.initHeader('Content-Type', this.defaultPostHeader);
2670                 }
2671
2672                  if (this.hasDefaultHeaders || this.hasHeaders) {
2673                     this.setHeader(o);
2674                 }
2675
2676                 this.handleReadyState(o, callback);
2677                 o.conn.send(postData || null);
2678
2679                 return o;
2680             }
2681         },
2682
2683         handleReadyState:function(o, callback)
2684         {
2685             var oConn = this;
2686
2687             if (callback && callback.timeout) {
2688                 this.timeout[o.tId] = window.setTimeout(function() {
2689                     oConn.abort(o, callback, true);
2690                 }, callback.timeout);
2691             }
2692
2693             this.poll[o.tId] = window.setInterval(
2694                     function() {
2695                         if (o.conn && o.conn.readyState == 4) {
2696                             window.clearInterval(oConn.poll[o.tId]);
2697                             delete oConn.poll[o.tId];
2698
2699                             if(callback && callback.timeout) {
2700                                 window.clearTimeout(oConn.timeout[o.tId]);
2701                                 delete oConn.timeout[o.tId];
2702                             }
2703
2704                             oConn.handleTransactionResponse(o, callback);
2705                         }
2706                     }
2707                     , this.pollInterval);
2708         },
2709
2710         handleTransactionResponse:function(o, callback, isAbort)
2711         {
2712
2713             if (!callback) {
2714                 this.releaseObject(o);
2715                 return;
2716             }
2717
2718             var httpStatus, responseObject;
2719
2720             try
2721             {
2722                 if (o.conn.status !== undefined && o.conn.status != 0) {
2723                     httpStatus = o.conn.status;
2724                 }
2725                 else {
2726                     httpStatus = 13030;
2727                 }
2728             }
2729             catch(e) {
2730
2731
2732                 httpStatus = 13030;
2733             }
2734
2735             if (httpStatus >= 200 && httpStatus < 300) {
2736                 responseObject = this.createResponseObject(o, callback.argument);
2737                 if (callback.success) {
2738                     if (!callback.scope) {
2739                         callback.success(responseObject);
2740                     }
2741                     else {
2742
2743
2744                         callback.success.apply(callback.scope, [responseObject]);
2745                     }
2746                 }
2747             }
2748             else {
2749                 switch (httpStatus) {
2750
2751                     case 12002:
2752                     case 12029:
2753                     case 12030:
2754                     case 12031:
2755                     case 12152:
2756                     case 13030:
2757                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2758                         if (callback.failure) {
2759                             if (!callback.scope) {
2760                                 callback.failure(responseObject);
2761                             }
2762                             else {
2763                                 callback.failure.apply(callback.scope, [responseObject]);
2764                             }
2765                         }
2766                         break;
2767                     default:
2768                         responseObject = this.createResponseObject(o, callback.argument);
2769                         if (callback.failure) {
2770                             if (!callback.scope) {
2771                                 callback.failure(responseObject);
2772                             }
2773                             else {
2774                                 callback.failure.apply(callback.scope, [responseObject]);
2775                             }
2776                         }
2777                 }
2778             }
2779
2780             this.releaseObject(o);
2781             responseObject = null;
2782         },
2783
2784         createResponseObject:function(o, callbackArg)
2785         {
2786             var obj = {};
2787             var headerObj = {};
2788
2789             try
2790             {
2791                 var headerStr = o.conn.getAllResponseHeaders();
2792                 var header = headerStr.split('\n');
2793                 for (var i = 0; i < header.length; i++) {
2794                     var delimitPos = header[i].indexOf(':');
2795                     if (delimitPos != -1) {
2796                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2797                     }
2798                 }
2799             }
2800             catch(e) {
2801             }
2802
2803             obj.tId = o.tId;
2804             obj.status = o.conn.status;
2805             obj.statusText = o.conn.statusText;
2806             obj.getResponseHeader = headerObj;
2807             obj.getAllResponseHeaders = headerStr;
2808             obj.responseText = o.conn.responseText;
2809             obj.responseXML = o.conn.responseXML;
2810
2811             if (typeof callbackArg !== undefined) {
2812                 obj.argument = callbackArg;
2813             }
2814
2815             return obj;
2816         },
2817
2818         createExceptionObject:function(tId, callbackArg, isAbort)
2819         {
2820             var COMM_CODE = 0;
2821             var COMM_ERROR = 'communication failure';
2822             var ABORT_CODE = -1;
2823             var ABORT_ERROR = 'transaction aborted';
2824
2825             var obj = {};
2826
2827             obj.tId = tId;
2828             if (isAbort) {
2829                 obj.status = ABORT_CODE;
2830                 obj.statusText = ABORT_ERROR;
2831             }
2832             else {
2833                 obj.status = COMM_CODE;
2834                 obj.statusText = COMM_ERROR;
2835             }
2836
2837             if (callbackArg) {
2838                 obj.argument = callbackArg;
2839             }
2840
2841             return obj;
2842         },
2843
2844         initHeader:function(label, value, isDefault)
2845         {
2846             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2847
2848             if (headerObj[label] === undefined) {
2849                 headerObj[label] = value;
2850             }
2851             else {
2852
2853
2854                 headerObj[label] = value + "," + headerObj[label];
2855             }
2856
2857             if (isDefault) {
2858                 this.hasDefaultHeaders = true;
2859             }
2860             else {
2861                 this.hasHeaders = true;
2862             }
2863         },
2864
2865
2866         setHeader:function(o)
2867         {
2868             if (this.hasDefaultHeaders) {
2869                 for (var prop in this.defaultHeaders) {
2870                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2871                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2872                     }
2873                 }
2874             }
2875
2876             if (this.hasHeaders) {
2877                 for (var prop in this.headers) {
2878                     if (this.headers.hasOwnProperty(prop)) {
2879                         o.conn.setRequestHeader(prop, this.headers[prop]);
2880                     }
2881                 }
2882                 this.headers = {};
2883                 this.hasHeaders = false;
2884             }
2885         },
2886
2887         resetDefaultHeaders:function() {
2888             delete this.defaultHeaders;
2889             this.defaultHeaders = {};
2890             this.hasDefaultHeaders = false;
2891         },
2892
2893         abort:function(o, callback, isTimeout)
2894         {
2895             if(this.isCallInProgress(o)) {
2896                 o.conn.abort();
2897                 window.clearInterval(this.poll[o.tId]);
2898                 delete this.poll[o.tId];
2899                 if (isTimeout) {
2900                     delete this.timeout[o.tId];
2901                 }
2902
2903                 this.handleTransactionResponse(o, callback, true);
2904
2905                 return true;
2906             }
2907             else {
2908                 return false;
2909             }
2910         },
2911
2912
2913         isCallInProgress:function(o)
2914         {
2915             if (o && o.conn) {
2916                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2917             }
2918             else {
2919
2920                 return false;
2921             }
2922         },
2923
2924
2925         releaseObject:function(o)
2926         {
2927
2928             o.conn = null;
2929
2930             o = null;
2931         },
2932
2933         activeX:[
2934         'MSXML2.XMLHTTP.3.0',
2935         'MSXML2.XMLHTTP',
2936         'Microsoft.XMLHTTP'
2937         ]
2938
2939
2940     };
2941 })();/*
2942  * Portions of this file are based on pieces of Yahoo User Interface Library
2943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2944  * YUI licensed under the BSD License:
2945  * http://developer.yahoo.net/yui/license.txt
2946  * <script type="text/javascript">
2947  *
2948  */
2949
2950 Roo.lib.Region = function(t, r, b, l) {
2951     this.top = t;
2952     this[1] = t;
2953     this.right = r;
2954     this.bottom = b;
2955     this.left = l;
2956     this[0] = l;
2957 };
2958
2959
2960 Roo.lib.Region.prototype = {
2961     contains : function(region) {
2962         return ( region.left >= this.left &&
2963                  region.right <= this.right &&
2964                  region.top >= this.top &&
2965                  region.bottom <= this.bottom    );
2966
2967     },
2968
2969     getArea : function() {
2970         return ( (this.bottom - this.top) * (this.right - this.left) );
2971     },
2972
2973     intersect : function(region) {
2974         var t = Math.max(this.top, region.top);
2975         var r = Math.min(this.right, region.right);
2976         var b = Math.min(this.bottom, region.bottom);
2977         var l = Math.max(this.left, region.left);
2978
2979         if (b >= t && r >= l) {
2980             return new Roo.lib.Region(t, r, b, l);
2981         } else {
2982             return null;
2983         }
2984     },
2985     union : function(region) {
2986         var t = Math.min(this.top, region.top);
2987         var r = Math.max(this.right, region.right);
2988         var b = Math.max(this.bottom, region.bottom);
2989         var l = Math.min(this.left, region.left);
2990
2991         return new Roo.lib.Region(t, r, b, l);
2992     },
2993
2994     adjust : function(t, l, b, r) {
2995         this.top += t;
2996         this.left += l;
2997         this.right += r;
2998         this.bottom += b;
2999         return this;
3000     }
3001 };
3002
3003 Roo.lib.Region.getRegion = function(el) {
3004     var p = Roo.lib.Dom.getXY(el);
3005
3006     var t = p[1];
3007     var r = p[0] + el.offsetWidth;
3008     var b = p[1] + el.offsetHeight;
3009     var l = p[0];
3010
3011     return new Roo.lib.Region(t, r, b, l);
3012 };
3013 /*
3014  * Portions of this file are based on pieces of Yahoo User Interface Library
3015  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3016  * YUI licensed under the BSD License:
3017  * http://developer.yahoo.net/yui/license.txt
3018  * <script type="text/javascript">
3019  *
3020  */
3021 //@@dep Roo.lib.Region
3022
3023
3024 Roo.lib.Point = function(x, y) {
3025     if (x instanceof Array) {
3026         y = x[1];
3027         x = x[0];
3028     }
3029     this.x = this.right = this.left = this[0] = x;
3030     this.y = this.top = this.bottom = this[1] = y;
3031 };
3032
3033 Roo.lib.Point.prototype = new Roo.lib.Region();
3034 /*
3035  * Portions of this file are based on pieces of Yahoo User Interface Library
3036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3037  * YUI licensed under the BSD License:
3038  * http://developer.yahoo.net/yui/license.txt
3039  * <script type="text/javascript">
3040  *
3041  */
3042  
3043 (function() {   
3044
3045     Roo.lib.Anim = {
3046         scroll : function(el, args, duration, easing, cb, scope) {
3047             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3048         },
3049
3050         motion : function(el, args, duration, easing, cb, scope) {
3051             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3052         },
3053
3054         color : function(el, args, duration, easing, cb, scope) {
3055             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3056         },
3057
3058         run : function(el, args, duration, easing, cb, scope, type) {
3059             type = type || Roo.lib.AnimBase;
3060             if (typeof easing == "string") {
3061                 easing = Roo.lib.Easing[easing];
3062             }
3063             var anim = new type(el, args, duration, easing);
3064             anim.animateX(function() {
3065                 Roo.callback(cb, scope);
3066             });
3067             return anim;
3068         }
3069     };
3070 })();/*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078
3079 (function() {    
3080     var libFlyweight;
3081     
3082     function fly(el) {
3083         if (!libFlyweight) {
3084             libFlyweight = new Roo.Element.Flyweight();
3085         }
3086         libFlyweight.dom = el;
3087         return libFlyweight;
3088     }
3089
3090     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3091     
3092    
3093     
3094     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3095         if (el) {
3096             this.init(el, attributes, duration, method);
3097         }
3098     };
3099
3100     Roo.lib.AnimBase.fly = fly;
3101     
3102     
3103     
3104     Roo.lib.AnimBase.prototype = {
3105
3106         toString: function() {
3107             var el = this.getEl();
3108             var id = el.id || el.tagName;
3109             return ("Anim " + id);
3110         },
3111
3112         patterns: {
3113             noNegatives:        /width|height|opacity|padding/i,
3114             offsetAttribute:  /^((width|height)|(top|left))$/,
3115             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3116             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3117         },
3118
3119
3120         doMethod: function(attr, start, end) {
3121             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3122         },
3123
3124
3125         setAttribute: function(attr, val, unit) {
3126             if (this.patterns.noNegatives.test(attr)) {
3127                 val = (val > 0) ? val : 0;
3128             }
3129
3130             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3131         },
3132
3133
3134         getAttribute: function(attr) {
3135             var el = this.getEl();
3136             var val = fly(el).getStyle(attr);
3137
3138             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3139                 return parseFloat(val);
3140             }
3141
3142             var a = this.patterns.offsetAttribute.exec(attr) || [];
3143             var pos = !!( a[3] );
3144             var box = !!( a[2] );
3145
3146
3147             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3148                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3149             } else {
3150                 val = 0;
3151             }
3152
3153             return val;
3154         },
3155
3156
3157         getDefaultUnit: function(attr) {
3158             if (this.patterns.defaultUnit.test(attr)) {
3159                 return 'px';
3160             }
3161
3162             return '';
3163         },
3164
3165         animateX : function(callback, scope) {
3166             var f = function() {
3167                 this.onComplete.removeListener(f);
3168                 if (typeof callback == "function") {
3169                     callback.call(scope || this, this);
3170                 }
3171             };
3172             this.onComplete.addListener(f, this);
3173             this.animate();
3174         },
3175
3176
3177         setRuntimeAttribute: function(attr) {
3178             var start;
3179             var end;
3180             var attributes = this.attributes;
3181
3182             this.runtimeAttributes[attr] = {};
3183
3184             var isset = function(prop) {
3185                 return (typeof prop !== 'undefined');
3186             };
3187
3188             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3189                 return false;
3190             }
3191
3192             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3193
3194
3195             if (isset(attributes[attr]['to'])) {
3196                 end = attributes[attr]['to'];
3197             } else if (isset(attributes[attr]['by'])) {
3198                 if (start.constructor == Array) {
3199                     end = [];
3200                     for (var i = 0, len = start.length; i < len; ++i) {
3201                         end[i] = start[i] + attributes[attr]['by'][i];
3202                     }
3203                 } else {
3204                     end = start + attributes[attr]['by'];
3205                 }
3206             }
3207
3208             this.runtimeAttributes[attr].start = start;
3209             this.runtimeAttributes[attr].end = end;
3210
3211
3212             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3213         },
3214
3215
3216         init: function(el, attributes, duration, method) {
3217
3218             var isAnimated = false;
3219
3220
3221             var startTime = null;
3222
3223
3224             var actualFrames = 0;
3225
3226
3227             el = Roo.getDom(el);
3228
3229
3230             this.attributes = attributes || {};
3231
3232
3233             this.duration = duration || 1;
3234
3235
3236             this.method = method || Roo.lib.Easing.easeNone;
3237
3238
3239             this.useSeconds = true;
3240
3241
3242             this.currentFrame = 0;
3243
3244
3245             this.totalFrames = Roo.lib.AnimMgr.fps;
3246
3247
3248             this.getEl = function() {
3249                 return el;
3250             };
3251
3252
3253             this.isAnimated = function() {
3254                 return isAnimated;
3255             };
3256
3257
3258             this.getStartTime = function() {
3259                 return startTime;
3260             };
3261
3262             this.runtimeAttributes = {};
3263
3264
3265             this.animate = function() {
3266                 if (this.isAnimated()) {
3267                     return false;
3268                 }
3269
3270                 this.currentFrame = 0;
3271
3272                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3273
3274                 Roo.lib.AnimMgr.registerElement(this);
3275             };
3276
3277
3278             this.stop = function(finish) {
3279                 if (finish) {
3280                     this.currentFrame = this.totalFrames;
3281                     this._onTween.fire();
3282                 }
3283                 Roo.lib.AnimMgr.stop(this);
3284             };
3285
3286             var onStart = function() {
3287                 this.onStart.fire();
3288
3289                 this.runtimeAttributes = {};
3290                 for (var attr in this.attributes) {
3291                     this.setRuntimeAttribute(attr);
3292                 }
3293
3294                 isAnimated = true;
3295                 actualFrames = 0;
3296                 startTime = new Date();
3297             };
3298
3299
3300             var onTween = function() {
3301                 var data = {
3302                     duration: new Date() - this.getStartTime(),
3303                     currentFrame: this.currentFrame
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', currentFrame: ' + data.currentFrame
3310                             );
3311                 };
3312
3313                 this.onTween.fire(data);
3314
3315                 var runtimeAttributes = this.runtimeAttributes;
3316
3317                 for (var attr in runtimeAttributes) {
3318                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3319                 }
3320
3321                 actualFrames += 1;
3322             };
3323
3324             var onComplete = function() {
3325                 var actual_duration = (new Date() - startTime) / 1000 ;
3326
3327                 var data = {
3328                     duration: actual_duration,
3329                     frames: actualFrames,
3330                     fps: actualFrames / actual_duration
3331                 };
3332
3333                 data.toString = function() {
3334                     return (
3335                             'duration: ' + data.duration +
3336                             ', frames: ' + data.frames +
3337                             ', fps: ' + data.fps
3338                             );
3339                 };
3340
3341                 isAnimated = false;
3342                 actualFrames = 0;
3343                 this.onComplete.fire(data);
3344             };
3345
3346
3347             this._onStart = new Roo.util.Event(this);
3348             this.onStart = new Roo.util.Event(this);
3349             this.onTween = new Roo.util.Event(this);
3350             this._onTween = new Roo.util.Event(this);
3351             this.onComplete = new Roo.util.Event(this);
3352             this._onComplete = new Roo.util.Event(this);
3353             this._onStart.addListener(onStart);
3354             this._onTween.addListener(onTween);
3355             this._onComplete.addListener(onComplete);
3356         }
3357     };
3358 })();
3359 /*
3360  * Portions of this file are based on pieces of Yahoo User Interface Library
3361  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3362  * YUI licensed under the BSD License:
3363  * http://developer.yahoo.net/yui/license.txt
3364  * <script type="text/javascript">
3365  *
3366  */
3367
3368 Roo.lib.AnimMgr = new function() {
3369
3370         var thread = null;
3371
3372
3373         var queue = [];
3374
3375
3376         var tweenCount = 0;
3377
3378
3379         this.fps = 1000;
3380
3381
3382         this.delay = 1;
3383
3384
3385         this.registerElement = function(tween) {
3386             queue[queue.length] = tween;
3387             tweenCount += 1;
3388             tween._onStart.fire();
3389             this.start();
3390         };
3391
3392
3393         this.unRegister = function(tween, index) {
3394             tween._onComplete.fire();
3395             index = index || getIndex(tween);
3396             if (index != -1) {
3397                 queue.splice(index, 1);
3398             }
3399
3400             tweenCount -= 1;
3401             if (tweenCount <= 0) {
3402                 this.stop();
3403             }
3404         };
3405
3406
3407         this.start = function() {
3408             if (thread === null) {
3409                 thread = setInterval(this.run, this.delay);
3410             }
3411         };
3412
3413
3414         this.stop = function(tween) {
3415             if (!tween) {
3416                 clearInterval(thread);
3417
3418                 for (var i = 0, len = queue.length; i < len; ++i) {
3419                     if (queue[0].isAnimated()) {
3420                         this.unRegister(queue[0], 0);
3421                     }
3422                 }
3423
3424                 queue = [];
3425                 thread = null;
3426                 tweenCount = 0;
3427             }
3428             else {
3429                 this.unRegister(tween);
3430             }
3431         };
3432
3433
3434         this.run = function() {
3435             for (var i = 0, len = queue.length; i < len; ++i) {
3436                 var tween = queue[i];
3437                 if (!tween || !tween.isAnimated()) {
3438                     continue;
3439                 }
3440
3441                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3442                 {
3443                     tween.currentFrame += 1;
3444
3445                     if (tween.useSeconds) {
3446                         correctFrame(tween);
3447                     }
3448                     tween._onTween.fire();
3449                 }
3450                 else {
3451                     Roo.lib.AnimMgr.stop(tween, i);
3452                 }
3453             }
3454         };
3455
3456         var getIndex = function(anim) {
3457             for (var i = 0, len = queue.length; i < len; ++i) {
3458                 if (queue[i] == anim) {
3459                     return i;
3460                 }
3461             }
3462             return -1;
3463         };
3464
3465
3466         var correctFrame = function(tween) {
3467             var frames = tween.totalFrames;
3468             var frame = tween.currentFrame;
3469             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3470             var elapsed = (new Date() - tween.getStartTime());
3471             var tweak = 0;
3472
3473             if (elapsed < tween.duration * 1000) {
3474                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3475             } else {
3476                 tweak = frames - (frame + 1);
3477             }
3478             if (tweak > 0 && isFinite(tweak)) {
3479                 if (tween.currentFrame + tweak >= frames) {
3480                     tweak = frames - (frame + 1);
3481                 }
3482
3483                 tween.currentFrame += tweak;
3484             }
3485         };
3486     };/*
3487  * Portions of this file are based on pieces of Yahoo User Interface Library
3488  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3489  * YUI licensed under the BSD License:
3490  * http://developer.yahoo.net/yui/license.txt
3491  * <script type="text/javascript">
3492  *
3493  */
3494 Roo.lib.Bezier = new function() {
3495
3496         this.getPosition = function(points, t) {
3497             var n = points.length;
3498             var tmp = [];
3499
3500             for (var i = 0; i < n; ++i) {
3501                 tmp[i] = [points[i][0], points[i][1]];
3502             }
3503
3504             for (var j = 1; j < n; ++j) {
3505                 for (i = 0; i < n - j; ++i) {
3506                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3507                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3508                 }
3509             }
3510
3511             return [ tmp[0][0], tmp[0][1] ];
3512
3513         };
3514     };/*
3515  * Portions of this file are based on pieces of Yahoo User Interface Library
3516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3517  * YUI licensed under the BSD License:
3518  * http://developer.yahoo.net/yui/license.txt
3519  * <script type="text/javascript">
3520  *
3521  */
3522 (function() {
3523
3524     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3525         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3526     };
3527
3528     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3529
3530     var fly = Roo.lib.AnimBase.fly;
3531     var Y = Roo.lib;
3532     var superclass = Y.ColorAnim.superclass;
3533     var proto = Y.ColorAnim.prototype;
3534
3535     proto.toString = function() {
3536         var el = this.getEl();
3537         var id = el.id || el.tagName;
3538         return ("ColorAnim " + id);
3539     };
3540
3541     proto.patterns.color = /color$/i;
3542     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3543     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3544     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3545     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3546
3547
3548     proto.parseColor = function(s) {
3549         if (s.length == 3) {
3550             return s;
3551         }
3552
3553         var c = this.patterns.hex.exec(s);
3554         if (c && c.length == 4) {
3555             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3556         }
3557
3558         c = this.patterns.rgb.exec(s);
3559         if (c && c.length == 4) {
3560             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3561         }
3562
3563         c = this.patterns.hex3.exec(s);
3564         if (c && c.length == 4) {
3565             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3566         }
3567
3568         return null;
3569     };
3570     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3571     proto.getAttribute = function(attr) {
3572         var el = this.getEl();
3573         if (this.patterns.color.test(attr)) {
3574             var val = fly(el).getStyle(attr);
3575
3576             if (this.patterns.transparent.test(val)) {
3577                 var parent = el.parentNode;
3578                 val = fly(parent).getStyle(attr);
3579
3580                 while (parent && this.patterns.transparent.test(val)) {
3581                     parent = parent.parentNode;
3582                     val = fly(parent).getStyle(attr);
3583                     if (parent.tagName.toUpperCase() == 'HTML') {
3584                         val = '#fff';
3585                     }
3586                 }
3587             }
3588         } else {
3589             val = superclass.getAttribute.call(this, attr);
3590         }
3591
3592         return val;
3593     };
3594     proto.getAttribute = function(attr) {
3595         var el = this.getEl();
3596         if (this.patterns.color.test(attr)) {
3597             var val = fly(el).getStyle(attr);
3598
3599             if (this.patterns.transparent.test(val)) {
3600                 var parent = el.parentNode;
3601                 val = fly(parent).getStyle(attr);
3602
3603                 while (parent && this.patterns.transparent.test(val)) {
3604                     parent = parent.parentNode;
3605                     val = fly(parent).getStyle(attr);
3606                     if (parent.tagName.toUpperCase() == 'HTML') {
3607                         val = '#fff';
3608                     }
3609                 }
3610             }
3611         } else {
3612             val = superclass.getAttribute.call(this, attr);
3613         }
3614
3615         return val;
3616     };
3617
3618     proto.doMethod = function(attr, start, end) {
3619         var val;
3620
3621         if (this.patterns.color.test(attr)) {
3622             val = [];
3623             for (var i = 0, len = start.length; i < len; ++i) {
3624                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3625             }
3626
3627             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3628         }
3629         else {
3630             val = superclass.doMethod.call(this, attr, start, end);
3631         }
3632
3633         return val;
3634     };
3635
3636     proto.setRuntimeAttribute = function(attr) {
3637         superclass.setRuntimeAttribute.call(this, attr);
3638
3639         if (this.patterns.color.test(attr)) {
3640             var attributes = this.attributes;
3641             var start = this.parseColor(this.runtimeAttributes[attr].start);
3642             var end = this.parseColor(this.runtimeAttributes[attr].end);
3643
3644             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3645                 end = this.parseColor(attributes[attr].by);
3646
3647                 for (var i = 0, len = start.length; i < len; ++i) {
3648                     end[i] = start[i] + end[i];
3649                 }
3650             }
3651
3652             this.runtimeAttributes[attr].start = start;
3653             this.runtimeAttributes[attr].end = end;
3654         }
3655     };
3656 })();
3657
3658 /*
3659  * Portions of this file are based on pieces of Yahoo User Interface Library
3660  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3661  * YUI licensed under the BSD License:
3662  * http://developer.yahoo.net/yui/license.txt
3663  * <script type="text/javascript">
3664  *
3665  */
3666 Roo.lib.Easing = {
3667
3668
3669     easeNone: function (t, b, c, d) {
3670         return c * t / d + b;
3671     },
3672
3673
3674     easeIn: function (t, b, c, d) {
3675         return c * (t /= d) * t + b;
3676     },
3677
3678
3679     easeOut: function (t, b, c, d) {
3680         return -c * (t /= d) * (t - 2) + b;
3681     },
3682
3683
3684     easeBoth: function (t, b, c, d) {
3685         if ((t /= d / 2) < 1) {
3686             return c / 2 * t * t + b;
3687         }
3688
3689         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3690     },
3691
3692
3693     easeInStrong: function (t, b, c, d) {
3694         return c * (t /= d) * t * t * t + b;
3695     },
3696
3697
3698     easeOutStrong: function (t, b, c, d) {
3699         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3700     },
3701
3702
3703     easeBothStrong: function (t, b, c, d) {
3704         if ((t /= d / 2) < 1) {
3705             return c / 2 * t * t * t * t + b;
3706         }
3707
3708         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3709     },
3710
3711
3712
3713     elasticIn: function (t, b, c, d, a, p) {
3714         if (t == 0) {
3715             return b;
3716         }
3717         if ((t /= d) == 1) {
3718             return b + c;
3719         }
3720         if (!p) {
3721             p = d * .3;
3722         }
3723
3724         if (!a || a < Math.abs(c)) {
3725             a = c;
3726             var s = p / 4;
3727         }
3728         else {
3729             var s = p / (2 * Math.PI) * Math.asin(c / a);
3730         }
3731
3732         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3733     },
3734
3735
3736     elasticOut: function (t, b, c, d, a, p) {
3737         if (t == 0) {
3738             return b;
3739         }
3740         if ((t /= d) == 1) {
3741             return b + c;
3742         }
3743         if (!p) {
3744             p = d * .3;
3745         }
3746
3747         if (!a || a < Math.abs(c)) {
3748             a = c;
3749             var s = p / 4;
3750         }
3751         else {
3752             var s = p / (2 * Math.PI) * Math.asin(c / a);
3753         }
3754
3755         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3756     },
3757
3758
3759     elasticBoth: function (t, b, c, d, a, p) {
3760         if (t == 0) {
3761             return b;
3762         }
3763
3764         if ((t /= d / 2) == 2) {
3765             return b + c;
3766         }
3767
3768         if (!p) {
3769             p = d * (.3 * 1.5);
3770         }
3771
3772         if (!a || a < Math.abs(c)) {
3773             a = c;
3774             var s = p / 4;
3775         }
3776         else {
3777             var s = p / (2 * Math.PI) * Math.asin(c / a);
3778         }
3779
3780         if (t < 1) {
3781             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3782                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3783         }
3784         return a * Math.pow(2, -10 * (t -= 1)) *
3785                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3786     },
3787
3788
3789
3790     backIn: function (t, b, c, d, s) {
3791         if (typeof s == 'undefined') {
3792             s = 1.70158;
3793         }
3794         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3795     },
3796
3797
3798     backOut: function (t, b, c, d, s) {
3799         if (typeof s == 'undefined') {
3800             s = 1.70158;
3801         }
3802         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3803     },
3804
3805
3806     backBoth: function (t, b, c, d, s) {
3807         if (typeof s == 'undefined') {
3808             s = 1.70158;
3809         }
3810
3811         if ((t /= d / 2 ) < 1) {
3812             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3813         }
3814         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3815     },
3816
3817
3818     bounceIn: function (t, b, c, d) {
3819         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3820     },
3821
3822
3823     bounceOut: function (t, b, c, d) {
3824         if ((t /= d) < (1 / 2.75)) {
3825             return c * (7.5625 * t * t) + b;
3826         } else if (t < (2 / 2.75)) {
3827             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3828         } else if (t < (2.5 / 2.75)) {
3829             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3830         }
3831         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3832     },
3833
3834
3835     bounceBoth: function (t, b, c, d) {
3836         if (t < d / 2) {
3837             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3838         }
3839         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3840     }
3841 };/*
3842  * Portions of this file are based on pieces of Yahoo User Interface Library
3843  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3844  * YUI licensed under the BSD License:
3845  * http://developer.yahoo.net/yui/license.txt
3846  * <script type="text/javascript">
3847  *
3848  */
3849     (function() {
3850         Roo.lib.Motion = function(el, attributes, duration, method) {
3851             if (el) {
3852                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3853             }
3854         };
3855
3856         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3857
3858
3859         var Y = Roo.lib;
3860         var superclass = Y.Motion.superclass;
3861         var proto = Y.Motion.prototype;
3862
3863         proto.toString = function() {
3864             var el = this.getEl();
3865             var id = el.id || el.tagName;
3866             return ("Motion " + id);
3867         };
3868
3869         proto.patterns.points = /^points$/i;
3870
3871         proto.setAttribute = function(attr, val, unit) {
3872             if (this.patterns.points.test(attr)) {
3873                 unit = unit || 'px';
3874                 superclass.setAttribute.call(this, 'left', val[0], unit);
3875                 superclass.setAttribute.call(this, 'top', val[1], unit);
3876             } else {
3877                 superclass.setAttribute.call(this, attr, val, unit);
3878             }
3879         };
3880
3881         proto.getAttribute = function(attr) {
3882             if (this.patterns.points.test(attr)) {
3883                 var val = [
3884                         superclass.getAttribute.call(this, 'left'),
3885                         superclass.getAttribute.call(this, 'top')
3886                         ];
3887             } else {
3888                 val = superclass.getAttribute.call(this, attr);
3889             }
3890
3891             return val;
3892         };
3893
3894         proto.doMethod = function(attr, start, end) {
3895             var val = null;
3896
3897             if (this.patterns.points.test(attr)) {
3898                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3899                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3900             } else {
3901                 val = superclass.doMethod.call(this, attr, start, end);
3902             }
3903             return val;
3904         };
3905
3906         proto.setRuntimeAttribute = function(attr) {
3907             if (this.patterns.points.test(attr)) {
3908                 var el = this.getEl();
3909                 var attributes = this.attributes;
3910                 var start;
3911                 var control = attributes['points']['control'] || [];
3912                 var end;
3913                 var i, len;
3914
3915                 if (control.length > 0 && !(control[0] instanceof Array)) {
3916                     control = [control];
3917                 } else {
3918                     var tmp = [];
3919                     for (i = 0,len = control.length; i < len; ++i) {
3920                         tmp[i] = control[i];
3921                     }
3922                     control = tmp;
3923                 }
3924
3925                 Roo.fly(el).position();
3926
3927                 if (isset(attributes['points']['from'])) {
3928                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3929                 }
3930                 else {
3931                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3932                 }
3933
3934                 start = this.getAttribute('points');
3935
3936
3937                 if (isset(attributes['points']['to'])) {
3938                     end = translateValues.call(this, attributes['points']['to'], start);
3939
3940                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3941                     for (i = 0,len = control.length; i < len; ++i) {
3942                         control[i] = translateValues.call(this, control[i], start);
3943                     }
3944
3945
3946                 } else if (isset(attributes['points']['by'])) {
3947                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3948
3949                     for (i = 0,len = control.length; i < len; ++i) {
3950                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3951                     }
3952                 }
3953
3954                 this.runtimeAttributes[attr] = [start];
3955
3956                 if (control.length > 0) {
3957                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3958                 }
3959
3960                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3961             }
3962             else {
3963                 superclass.setRuntimeAttribute.call(this, attr);
3964             }
3965         };
3966
3967         var translateValues = function(val, start) {
3968             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3969             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3970
3971             return val;
3972         };
3973
3974         var isset = function(prop) {
3975             return (typeof prop !== 'undefined');
3976         };
3977     })();
3978 /*
3979  * Portions of this file are based on pieces of Yahoo User Interface Library
3980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3981  * YUI licensed under the BSD License:
3982  * http://developer.yahoo.net/yui/license.txt
3983  * <script type="text/javascript">
3984  *
3985  */
3986     (function() {
3987         Roo.lib.Scroll = function(el, attributes, duration, method) {
3988             if (el) {
3989                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3990             }
3991         };
3992
3993         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3994
3995
3996         var Y = Roo.lib;
3997         var superclass = Y.Scroll.superclass;
3998         var proto = Y.Scroll.prototype;
3999
4000         proto.toString = function() {
4001             var el = this.getEl();
4002             var id = el.id || el.tagName;
4003             return ("Scroll " + id);
4004         };
4005
4006         proto.doMethod = function(attr, start, end) {
4007             var val = null;
4008
4009             if (attr == 'scroll') {
4010                 val = [
4011                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4012                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4013                         ];
4014
4015             } else {
4016                 val = superclass.doMethod.call(this, attr, start, end);
4017             }
4018             return val;
4019         };
4020
4021         proto.getAttribute = function(attr) {
4022             var val = null;
4023             var el = this.getEl();
4024
4025             if (attr == 'scroll') {
4026                 val = [ el.scrollLeft, el.scrollTop ];
4027             } else {
4028                 val = superclass.getAttribute.call(this, attr);
4029             }
4030
4031             return val;
4032         };
4033
4034         proto.setAttribute = function(attr, val, unit) {
4035             var el = this.getEl();
4036
4037             if (attr == 'scroll') {
4038                 el.scrollLeft = val[0];
4039                 el.scrollTop = val[1];
4040             } else {
4041                 superclass.setAttribute.call(this, attr, val, unit);
4042             }
4043         };
4044     })();
4045 /*
4046  * Based on:
4047  * Ext JS Library 1.1.1
4048  * Copyright(c) 2006-2007, Ext JS, LLC.
4049  *
4050  * Originally Released Under LGPL - original licence link has changed is not relivant.
4051  *
4052  * Fork - LGPL
4053  * <script type="text/javascript">
4054  */
4055
4056
4057 // nasty IE9 hack - what a pile of crap that is..
4058
4059  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4060     Range.prototype.createContextualFragment = function (html) {
4061         var doc = window.document;
4062         var container = doc.createElement("div");
4063         container.innerHTML = html;
4064         var frag = doc.createDocumentFragment(), n;
4065         while ((n = container.firstChild)) {
4066             frag.appendChild(n);
4067         }
4068         return frag;
4069     };
4070 }
4071
4072 /**
4073  * @class Roo.DomHelper
4074  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4075  * 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>.
4076  * @singleton
4077  */
4078 Roo.DomHelper = function(){
4079     var tempTableEl = null;
4080     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4081     var tableRe = /^table|tbody|tr|td$/i;
4082     var xmlns = {};
4083     // build as innerHTML where available
4084     /** @ignore */
4085     var createHtml = function(o){
4086         if(typeof o == 'string'){
4087             return o;
4088         }
4089         var b = "";
4090         if(!o.tag){
4091             o.tag = "div";
4092         }
4093         b += "<" + o.tag;
4094         for(var attr in o){
4095             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4096             if(attr == "style"){
4097                 var s = o["style"];
4098                 if(typeof s == "function"){
4099                     s = s.call();
4100                 }
4101                 if(typeof s == "string"){
4102                     b += ' style="' + s + '"';
4103                 }else if(typeof s == "object"){
4104                     b += ' style="';
4105                     for(var key in s){
4106                         if(typeof s[key] != "function"){
4107                             b += key + ":" + s[key] + ";";
4108                         }
4109                     }
4110                     b += '"';
4111                 }
4112             }else{
4113                 if(attr == "cls"){
4114                     b += ' class="' + o["cls"] + '"';
4115                 }else if(attr == "htmlFor"){
4116                     b += ' for="' + o["htmlFor"] + '"';
4117                 }else{
4118                     b += " " + attr + '="' + o[attr] + '"';
4119                 }
4120             }
4121         }
4122         if(emptyTags.test(o.tag)){
4123             b += "/>";
4124         }else{
4125             b += ">";
4126             var cn = o.children || o.cn;
4127             if(cn){
4128                 //http://bugs.kde.org/show_bug.cgi?id=71506
4129                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4130                     for(var i = 0, len = cn.length; i < len; i++) {
4131                         b += createHtml(cn[i], b);
4132                     }
4133                 }else{
4134                     b += createHtml(cn, b);
4135                 }
4136             }
4137             if(o.html){
4138                 b += o.html;
4139             }
4140             b += "</" + o.tag + ">";
4141         }
4142         return b;
4143     };
4144
4145     // build as dom
4146     /** @ignore */
4147     var createDom = function(o, parentNode){
4148          
4149         // defininition craeted..
4150         var ns = false;
4151         if (o.ns && o.ns != 'html') {
4152                
4153             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4154                 xmlns[o.ns] = o.xmlns;
4155                 ns = o.xmlns;
4156             }
4157             if (typeof(xmlns[o.ns]) == 'undefined') {
4158                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4159             }
4160             ns = xmlns[o.ns];
4161         }
4162         
4163         
4164         if (typeof(o) == 'string') {
4165             return parentNode.appendChild(document.createTextNode(o));
4166         }
4167         o.tag = o.tag || div;
4168         if (o.ns && Roo.isIE) {
4169             ns = false;
4170             o.tag = o.ns + ':' + o.tag;
4171             
4172         }
4173         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4174         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4175         for(var attr in o){
4176             
4177             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4178                     attr == "style" || typeof o[attr] == "function") continue;
4179                     
4180             if(attr=="cls" && Roo.isIE){
4181                 el.className = o["cls"];
4182             }else{
4183                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4184                 else el[attr] = o[attr];
4185             }
4186         }
4187         Roo.DomHelper.applyStyles(el, o.style);
4188         var cn = o.children || o.cn;
4189         if(cn){
4190             //http://bugs.kde.org/show_bug.cgi?id=71506
4191              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4192                 for(var i = 0, len = cn.length; i < len; i++) {
4193                     createDom(cn[i], el);
4194                 }
4195             }else{
4196                 createDom(cn, el);
4197             }
4198         }
4199         if(o.html){
4200             el.innerHTML = o.html;
4201         }
4202         if(parentNode){
4203            parentNode.appendChild(el);
4204         }
4205         return el;
4206     };
4207
4208     var ieTable = function(depth, s, h, e){
4209         tempTableEl.innerHTML = [s, h, e].join('');
4210         var i = -1, el = tempTableEl;
4211         while(++i < depth){
4212             el = el.firstChild;
4213         }
4214         return el;
4215     };
4216
4217     // kill repeat to save bytes
4218     var ts = '<table>',
4219         te = '</table>',
4220         tbs = ts+'<tbody>',
4221         tbe = '</tbody>'+te,
4222         trs = tbs + '<tr>',
4223         tre = '</tr>'+tbe;
4224
4225     /**
4226      * @ignore
4227      * Nasty code for IE's broken table implementation
4228      */
4229     var insertIntoTable = function(tag, where, el, html){
4230         if(!tempTableEl){
4231             tempTableEl = document.createElement('div');
4232         }
4233         var node;
4234         var before = null;
4235         if(tag == 'td'){
4236             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4237                 return;
4238             }
4239             if(where == 'beforebegin'){
4240                 before = el;
4241                 el = el.parentNode;
4242             } else{
4243                 before = el.nextSibling;
4244                 el = el.parentNode;
4245             }
4246             node = ieTable(4, trs, html, tre);
4247         }
4248         else if(tag == 'tr'){
4249             if(where == 'beforebegin'){
4250                 before = el;
4251                 el = el.parentNode;
4252                 node = ieTable(3, tbs, html, tbe);
4253             } else if(where == 'afterend'){
4254                 before = el.nextSibling;
4255                 el = el.parentNode;
4256                 node = ieTable(3, tbs, html, tbe);
4257             } else{ // INTO a TR
4258                 if(where == 'afterbegin'){
4259                     before = el.firstChild;
4260                 }
4261                 node = ieTable(4, trs, html, tre);
4262             }
4263         } else if(tag == 'tbody'){
4264             if(where == 'beforebegin'){
4265                 before = el;
4266                 el = el.parentNode;
4267                 node = ieTable(2, ts, html, te);
4268             } else if(where == 'afterend'){
4269                 before = el.nextSibling;
4270                 el = el.parentNode;
4271                 node = ieTable(2, ts, html, te);
4272             } else{
4273                 if(where == 'afterbegin'){
4274                     before = el.firstChild;
4275                 }
4276                 node = ieTable(3, tbs, html, tbe);
4277             }
4278         } else{ // TABLE
4279             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4280                 return;
4281             }
4282             if(where == 'afterbegin'){
4283                 before = el.firstChild;
4284             }
4285             node = ieTable(2, ts, html, te);
4286         }
4287         el.insertBefore(node, before);
4288         return node;
4289     };
4290
4291     return {
4292     /** True to force the use of DOM instead of html fragments @type Boolean */
4293     useDom : false,
4294
4295     /**
4296      * Returns the markup for the passed Element(s) config
4297      * @param {Object} o The Dom object spec (and children)
4298      * @return {String}
4299      */
4300     markup : function(o){
4301         return createHtml(o);
4302     },
4303
4304     /**
4305      * Applies a style specification to an element
4306      * @param {String/HTMLElement} el The element to apply styles to
4307      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4308      * a function which returns such a specification.
4309      */
4310     applyStyles : function(el, styles){
4311         if(styles){
4312            el = Roo.fly(el);
4313            if(typeof styles == "string"){
4314                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4315                var matches;
4316                while ((matches = re.exec(styles)) != null){
4317                    el.setStyle(matches[1], matches[2]);
4318                }
4319            }else if (typeof styles == "object"){
4320                for (var style in styles){
4321                   el.setStyle(style, styles[style]);
4322                }
4323            }else if (typeof styles == "function"){
4324                 Roo.DomHelper.applyStyles(el, styles.call());
4325            }
4326         }
4327     },
4328
4329     /**
4330      * Inserts an HTML fragment into the Dom
4331      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4332      * @param {HTMLElement} el The context element
4333      * @param {String} html The HTML fragmenet
4334      * @return {HTMLElement} The new node
4335      */
4336     insertHtml : function(where, el, html){
4337         where = where.toLowerCase();
4338         if(el.insertAdjacentHTML){
4339             if(tableRe.test(el.tagName)){
4340                 var rs;
4341                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4342                     return rs;
4343                 }
4344             }
4345             switch(where){
4346                 case "beforebegin":
4347                     el.insertAdjacentHTML('BeforeBegin', html);
4348                     return el.previousSibling;
4349                 case "afterbegin":
4350                     el.insertAdjacentHTML('AfterBegin', html);
4351                     return el.firstChild;
4352                 case "beforeend":
4353                     el.insertAdjacentHTML('BeforeEnd', html);
4354                     return el.lastChild;
4355                 case "afterend":
4356                     el.insertAdjacentHTML('AfterEnd', html);
4357                     return el.nextSibling;
4358             }
4359             throw 'Illegal insertion point -> "' + where + '"';
4360         }
4361         var range = el.ownerDocument.createRange();
4362         var frag;
4363         switch(where){
4364              case "beforebegin":
4365                 range.setStartBefore(el);
4366                 frag = range.createContextualFragment(html);
4367                 el.parentNode.insertBefore(frag, el);
4368                 return el.previousSibling;
4369              case "afterbegin":
4370                 if(el.firstChild){
4371                     range.setStartBefore(el.firstChild);
4372                     frag = range.createContextualFragment(html);
4373                     el.insertBefore(frag, el.firstChild);
4374                     return el.firstChild;
4375                 }else{
4376                     el.innerHTML = html;
4377                     return el.firstChild;
4378                 }
4379             case "beforeend":
4380                 if(el.lastChild){
4381                     range.setStartAfter(el.lastChild);
4382                     frag = range.createContextualFragment(html);
4383                     el.appendChild(frag);
4384                     return el.lastChild;
4385                 }else{
4386                     el.innerHTML = html;
4387                     return el.lastChild;
4388                 }
4389             case "afterend":
4390                 range.setStartAfter(el);
4391                 frag = range.createContextualFragment(html);
4392                 el.parentNode.insertBefore(frag, el.nextSibling);
4393                 return el.nextSibling;
4394             }
4395             throw 'Illegal insertion point -> "' + where + '"';
4396     },
4397
4398     /**
4399      * Creates new Dom element(s) and inserts them before el
4400      * @param {String/HTMLElement/Element} el The context element
4401      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4402      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4403      * @return {HTMLElement/Roo.Element} The new node
4404      */
4405     insertBefore : function(el, o, returnElement){
4406         return this.doInsert(el, o, returnElement, "beforeBegin");
4407     },
4408
4409     /**
4410      * Creates new Dom element(s) and inserts them after el
4411      * @param {String/HTMLElement/Element} el The context element
4412      * @param {Object} o The Dom object spec (and children)
4413      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4414      * @return {HTMLElement/Roo.Element} The new node
4415      */
4416     insertAfter : function(el, o, returnElement){
4417         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4418     },
4419
4420     /**
4421      * Creates new Dom element(s) and inserts them as the first child of el
4422      * @param {String/HTMLElement/Element} el The context element
4423      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4424      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4425      * @return {HTMLElement/Roo.Element} The new node
4426      */
4427     insertFirst : function(el, o, returnElement){
4428         return this.doInsert(el, o, returnElement, "afterBegin");
4429     },
4430
4431     // private
4432     doInsert : function(el, o, returnElement, pos, sibling){
4433         el = Roo.getDom(el);
4434         var newNode;
4435         if(this.useDom || o.ns){
4436             newNode = createDom(o, null);
4437             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4438         }else{
4439             var html = createHtml(o);
4440             newNode = this.insertHtml(pos, el, html);
4441         }
4442         return returnElement ? Roo.get(newNode, true) : newNode;
4443     },
4444
4445     /**
4446      * Creates new Dom element(s) and appends them to el
4447      * @param {String/HTMLElement/Element} el The context element
4448      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4449      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4450      * @return {HTMLElement/Roo.Element} The new node
4451      */
4452     append : function(el, o, returnElement){
4453         el = Roo.getDom(el);
4454         var newNode;
4455         if(this.useDom || o.ns){
4456             newNode = createDom(o, null);
4457             el.appendChild(newNode);
4458         }else{
4459             var html = createHtml(o);
4460             newNode = this.insertHtml("beforeEnd", el, html);
4461         }
4462         return returnElement ? Roo.get(newNode, true) : newNode;
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and overwrites the contents of el with them
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     overwrite : function(el, o, returnElement){
4473         el = Roo.getDom(el);
4474         if (o.ns) {
4475           
4476             while (el.childNodes.length) {
4477                 el.removeChild(el.firstChild);
4478             }
4479             createDom(o, el);
4480         } else {
4481             el.innerHTML = createHtml(o);   
4482         }
4483         
4484         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4485     },
4486
4487     /**
4488      * Creates a new Roo.DomHelper.Template from the Dom object spec
4489      * @param {Object} o The Dom object spec (and children)
4490      * @return {Roo.DomHelper.Template} The new template
4491      */
4492     createTemplate : function(o){
4493         var html = createHtml(o);
4494         return new Roo.Template(html);
4495     }
4496     };
4497 }();
4498 /*
4499  * Based on:
4500  * Ext JS Library 1.1.1
4501  * Copyright(c) 2006-2007, Ext JS, LLC.
4502  *
4503  * Originally Released Under LGPL - original licence link has changed is not relivant.
4504  *
4505  * Fork - LGPL
4506  * <script type="text/javascript">
4507  */
4508  
4509 /**
4510 * @class Roo.Template
4511 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4512 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4513 * Usage:
4514 <pre><code>
4515 var t = new Roo.Template({
4516     html :  '&lt;div name="{id}"&gt;' + 
4517         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4518         '&lt;/div&gt;',
4519     myformat: function (value, allValues) {
4520         return 'XX' + value;
4521     }
4522 });
4523 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4524 </code></pre>
4525 * 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>. 
4526 * @constructor
4527 * @param {Object} cfg - Configuration object.
4528 */
4529 Roo.Template = function(cfg){
4530     // BC!
4531     if(cfg instanceof Array){
4532         cfg = cfg.join("");
4533     }else if(arguments.length > 1){
4534         cfg = Array.prototype.join.call(arguments, "");
4535     }
4536     
4537     
4538     if (typeof(cfg) == 'object') {
4539         Roo.apply(this,cfg)
4540     } else {
4541         // bc
4542         this.html = cfg;
4543     }
4544     
4545     
4546 };
4547 Roo.Template.prototype = {
4548     
4549     /**
4550      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4551      */
4552     html : '',
4553     /**
4554      * Returns an HTML fragment of this template with the specified values applied.
4555      * @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'})
4556      * @return {String} The HTML fragment
4557      */
4558     applyTemplate : function(values){
4559         try {
4560             
4561             if(this.compiled){
4562                 return this.compiled(values);
4563             }
4564             var useF = this.disableFormats !== true;
4565             var fm = Roo.util.Format, tpl = this;
4566             var fn = function(m, name, format, args){
4567                 if(format && useF){
4568                     if(format.substr(0, 5) == "this."){
4569                         return tpl.call(format.substr(5), values[name], values);
4570                     }else{
4571                         if(args){
4572                             // quoted values are required for strings in compiled templates, 
4573                             // but for non compiled we need to strip them
4574                             // quoted reversed for jsmin
4575                             var re = /^\s*['"](.*)["']\s*$/;
4576                             args = args.split(',');
4577                             for(var i = 0, len = args.length; i < len; i++){
4578                                 args[i] = args[i].replace(re, "$1");
4579                             }
4580                             args = [values[name]].concat(args);
4581                         }else{
4582                             args = [values[name]];
4583                         }
4584                         return fm[format].apply(fm, args);
4585                     }
4586                 }else{
4587                     return values[name] !== undefined ? values[name] : "";
4588                 }
4589             };
4590             return this.html.replace(this.re, fn);
4591         } catch (e) {
4592             Roo.log(e);
4593             throw e;
4594         }
4595          
4596     },
4597     
4598     /**
4599      * Sets the HTML used as the template and optionally compiles it.
4600      * @param {String} html
4601      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4602      * @return {Roo.Template} this
4603      */
4604     set : function(html, compile){
4605         this.html = html;
4606         this.compiled = null;
4607         if(compile){
4608             this.compile();
4609         }
4610         return this;
4611     },
4612     
4613     /**
4614      * True to disable format functions (defaults to false)
4615      * @type Boolean
4616      */
4617     disableFormats : false,
4618     
4619     /**
4620     * The regular expression used to match template variables 
4621     * @type RegExp
4622     * @property 
4623     */
4624     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4625     
4626     /**
4627      * Compiles the template into an internal function, eliminating the RegEx overhead.
4628      * @return {Roo.Template} this
4629      */
4630     compile : function(){
4631         var fm = Roo.util.Format;
4632         var useF = this.disableFormats !== true;
4633         var sep = Roo.isGecko ? "+" : ",";
4634         var fn = function(m, name, format, args){
4635             if(format && useF){
4636                 args = args ? ',' + args : "";
4637                 if(format.substr(0, 5) != "this."){
4638                     format = "fm." + format + '(';
4639                 }else{
4640                     format = 'this.call("'+ format.substr(5) + '", ';
4641                     args = ", values";
4642                 }
4643             }else{
4644                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4645             }
4646             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4647         };
4648         var body;
4649         // branched to use + in gecko and [].join() in others
4650         if(Roo.isGecko){
4651             body = "this.compiled = function(values){ return '" +
4652                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4653                     "';};";
4654         }else{
4655             body = ["this.compiled = function(values){ return ['"];
4656             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4657             body.push("'].join('');};");
4658             body = body.join('');
4659         }
4660         /**
4661          * eval:var:values
4662          * eval:var:fm
4663          */
4664         eval(body);
4665         return this;
4666     },
4667     
4668     // private function used to call members
4669     call : function(fnName, value, allValues){
4670         return this[fnName](value, allValues);
4671     },
4672     
4673     /**
4674      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4675      * @param {String/HTMLElement/Roo.Element} el The context element
4676      * @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'})
4677      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4678      * @return {HTMLElement/Roo.Element} The new node or Element
4679      */
4680     insertFirst: function(el, values, returnElement){
4681         return this.doInsert('afterBegin', el, values, returnElement);
4682     },
4683
4684     /**
4685      * Applies the supplied values to the template and inserts the new node(s) before el.
4686      * @param {String/HTMLElement/Roo.Element} el The context element
4687      * @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'})
4688      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4689      * @return {HTMLElement/Roo.Element} The new node or Element
4690      */
4691     insertBefore: function(el, values, returnElement){
4692         return this.doInsert('beforeBegin', el, values, returnElement);
4693     },
4694
4695     /**
4696      * Applies the supplied values to the template and inserts the new node(s) after el.
4697      * @param {String/HTMLElement/Roo.Element} el The context element
4698      * @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'})
4699      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4700      * @return {HTMLElement/Roo.Element} The new node or Element
4701      */
4702     insertAfter : function(el, values, returnElement){
4703         return this.doInsert('afterEnd', el, values, returnElement);
4704     },
4705     
4706     /**
4707      * Applies the supplied values to the template and appends the new node(s) to el.
4708      * @param {String/HTMLElement/Roo.Element} el The context element
4709      * @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'})
4710      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4711      * @return {HTMLElement/Roo.Element} The new node or Element
4712      */
4713     append : function(el, values, returnElement){
4714         return this.doInsert('beforeEnd', el, values, returnElement);
4715     },
4716
4717     doInsert : function(where, el, values, returnEl){
4718         el = Roo.getDom(el);
4719         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4720         return returnEl ? Roo.get(newNode, true) : newNode;
4721     },
4722
4723     /**
4724      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4725      * @param {String/HTMLElement/Roo.Element} el The context element
4726      * @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'})
4727      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4728      * @return {HTMLElement/Roo.Element} The new node or Element
4729      */
4730     overwrite : function(el, values, returnElement){
4731         el = Roo.getDom(el);
4732         el.innerHTML = this.applyTemplate(values);
4733         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4734     }
4735 };
4736 /**
4737  * Alias for {@link #applyTemplate}
4738  * @method
4739  */
4740 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4741
4742 // backwards compat
4743 Roo.DomHelper.Template = Roo.Template;
4744
4745 /**
4746  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4747  * @param {String/HTMLElement} el A DOM element or its id
4748  * @returns {Roo.Template} The created template
4749  * @static
4750  */
4751 Roo.Template.from = function(el){
4752     el = Roo.getDom(el);
4753     return new Roo.Template(el.value || el.innerHTML);
4754 };/*
4755  * Based on:
4756  * Ext JS Library 1.1.1
4757  * Copyright(c) 2006-2007, Ext JS, LLC.
4758  *
4759  * Originally Released Under LGPL - original licence link has changed is not relivant.
4760  *
4761  * Fork - LGPL
4762  * <script type="text/javascript">
4763  */
4764  
4765
4766 /*
4767  * This is code is also distributed under MIT license for use
4768  * with jQuery and prototype JavaScript libraries.
4769  */
4770 /**
4771  * @class Roo.DomQuery
4772 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).
4773 <p>
4774 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>
4775
4776 <p>
4777 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.
4778 </p>
4779 <h4>Element Selectors:</h4>
4780 <ul class="list">
4781     <li> <b>*</b> any element</li>
4782     <li> <b>E</b> an element with the tag E</li>
4783     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4784     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4785     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4786     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4787 </ul>
4788 <h4>Attribute Selectors:</h4>
4789 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4790 <ul class="list">
4791     <li> <b>E[foo]</b> has an attribute "foo"</li>
4792     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4793     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4794     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4795     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4796     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4797     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4798 </ul>
4799 <h4>Pseudo Classes:</h4>
4800 <ul class="list">
4801     <li> <b>E:first-child</b> E is the first child of its parent</li>
4802     <li> <b>E:last-child</b> E is the last child of its parent</li>
4803     <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>
4804     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4805     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4806     <li> <b>E:only-child</b> E is the only child of its parent</li>
4807     <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>
4808     <li> <b>E:first</b> the first E in the resultset</li>
4809     <li> <b>E:last</b> the last E in the resultset</li>
4810     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4811     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4812     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4813     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4814     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4815     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4816     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4817     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4818     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4819 </ul>
4820 <h4>CSS Value Selectors:</h4>
4821 <ul class="list">
4822     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4823     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4824     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4825     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4826     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4827     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4828 </ul>
4829  * @singleton
4830  */
4831 Roo.DomQuery = function(){
4832     var cache = {}, simpleCache = {}, valueCache = {};
4833     var nonSpace = /\S/;
4834     var trimRe = /^\s+|\s+$/g;
4835     var tplRe = /\{(\d+)\}/g;
4836     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4837     var tagTokenRe = /^(#)?([\w-\*]+)/;
4838     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4839
4840     function child(p, index){
4841         var i = 0;
4842         var n = p.firstChild;
4843         while(n){
4844             if(n.nodeType == 1){
4845                if(++i == index){
4846                    return n;
4847                }
4848             }
4849             n = n.nextSibling;
4850         }
4851         return null;
4852     };
4853
4854     function next(n){
4855         while((n = n.nextSibling) && n.nodeType != 1);
4856         return n;
4857     };
4858
4859     function prev(n){
4860         while((n = n.previousSibling) && n.nodeType != 1);
4861         return n;
4862     };
4863
4864     function children(d){
4865         var n = d.firstChild, ni = -1;
4866             while(n){
4867                 var nx = n.nextSibling;
4868                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4869                     d.removeChild(n);
4870                 }else{
4871                     n.nodeIndex = ++ni;
4872                 }
4873                 n = nx;
4874             }
4875             return this;
4876         };
4877
4878     function byClassName(c, a, v){
4879         if(!v){
4880             return c;
4881         }
4882         var r = [], ri = -1, cn;
4883         for(var i = 0, ci; ci = c[i]; i++){
4884             if((' '+ci.className+' ').indexOf(v) != -1){
4885                 r[++ri] = ci;
4886             }
4887         }
4888         return r;
4889     };
4890
4891     function attrValue(n, attr){
4892         if(!n.tagName && typeof n.length != "undefined"){
4893             n = n[0];
4894         }
4895         if(!n){
4896             return null;
4897         }
4898         if(attr == "for"){
4899             return n.htmlFor;
4900         }
4901         if(attr == "class" || attr == "className"){
4902             return n.className;
4903         }
4904         return n.getAttribute(attr) || n[attr];
4905
4906     };
4907
4908     function getNodes(ns, mode, tagName){
4909         var result = [], ri = -1, cs;
4910         if(!ns){
4911             return result;
4912         }
4913         tagName = tagName || "*";
4914         if(typeof ns.getElementsByTagName != "undefined"){
4915             ns = [ns];
4916         }
4917         if(!mode){
4918             for(var i = 0, ni; ni = ns[i]; i++){
4919                 cs = ni.getElementsByTagName(tagName);
4920                 for(var j = 0, ci; ci = cs[j]; j++){
4921                     result[++ri] = ci;
4922                 }
4923             }
4924         }else if(mode == "/" || mode == ">"){
4925             var utag = tagName.toUpperCase();
4926             for(var i = 0, ni, cn; ni = ns[i]; i++){
4927                 cn = ni.children || ni.childNodes;
4928                 for(var j = 0, cj; cj = cn[j]; j++){
4929                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4930                         result[++ri] = cj;
4931                     }
4932                 }
4933             }
4934         }else if(mode == "+"){
4935             var utag = tagName.toUpperCase();
4936             for(var i = 0, n; n = ns[i]; i++){
4937                 while((n = n.nextSibling) && n.nodeType != 1);
4938                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4939                     result[++ri] = n;
4940                 }
4941             }
4942         }else if(mode == "~"){
4943             for(var i = 0, n; n = ns[i]; i++){
4944                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4945                 if(n){
4946                     result[++ri] = n;
4947                 }
4948             }
4949         }
4950         return result;
4951     };
4952
4953     function concat(a, b){
4954         if(b.slice){
4955             return a.concat(b);
4956         }
4957         for(var i = 0, l = b.length; i < l; i++){
4958             a[a.length] = b[i];
4959         }
4960         return a;
4961     }
4962
4963     function byTag(cs, tagName){
4964         if(cs.tagName || cs == document){
4965             cs = [cs];
4966         }
4967         if(!tagName){
4968             return cs;
4969         }
4970         var r = [], ri = -1;
4971         tagName = tagName.toLowerCase();
4972         for(var i = 0, ci; ci = cs[i]; i++){
4973             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4974                 r[++ri] = ci;
4975             }
4976         }
4977         return r;
4978     };
4979
4980     function byId(cs, attr, id){
4981         if(cs.tagName || cs == document){
4982             cs = [cs];
4983         }
4984         if(!id){
4985             return cs;
4986         }
4987         var r = [], ri = -1;
4988         for(var i = 0,ci; ci = cs[i]; i++){
4989             if(ci && ci.id == id){
4990                 r[++ri] = ci;
4991                 return r;
4992             }
4993         }
4994         return r;
4995     };
4996
4997     function byAttribute(cs, attr, value, op, custom){
4998         var r = [], ri = -1, st = custom=="{";
4999         var f = Roo.DomQuery.operators[op];
5000         for(var i = 0, ci; ci = cs[i]; i++){
5001             var a;
5002             if(st){
5003                 a = Roo.DomQuery.getStyle(ci, attr);
5004             }
5005             else if(attr == "class" || attr == "className"){
5006                 a = ci.className;
5007             }else if(attr == "for"){
5008                 a = ci.htmlFor;
5009             }else if(attr == "href"){
5010                 a = ci.getAttribute("href", 2);
5011             }else{
5012                 a = ci.getAttribute(attr);
5013             }
5014             if((f && f(a, value)) || (!f && a)){
5015                 r[++ri] = ci;
5016             }
5017         }
5018         return r;
5019     };
5020
5021     function byPseudo(cs, name, value){
5022         return Roo.DomQuery.pseudos[name](cs, value);
5023     };
5024
5025     // This is for IE MSXML which does not support expandos.
5026     // IE runs the same speed using setAttribute, however FF slows way down
5027     // and Safari completely fails so they need to continue to use expandos.
5028     var isIE = window.ActiveXObject ? true : false;
5029
5030     // this eval is stop the compressor from
5031     // renaming the variable to something shorter
5032     
5033     /** eval:var:batch */
5034     var batch = 30803; 
5035
5036     var key = 30803;
5037
5038     function nodupIEXml(cs){
5039         var d = ++key;
5040         cs[0].setAttribute("_nodup", d);
5041         var r = [cs[0]];
5042         for(var i = 1, len = cs.length; i < len; i++){
5043             var c = cs[i];
5044             if(!c.getAttribute("_nodup") != d){
5045                 c.setAttribute("_nodup", d);
5046                 r[r.length] = c;
5047             }
5048         }
5049         for(var i = 0, len = cs.length; i < len; i++){
5050             cs[i].removeAttribute("_nodup");
5051         }
5052         return r;
5053     }
5054
5055     function nodup(cs){
5056         if(!cs){
5057             return [];
5058         }
5059         var len = cs.length, c, i, r = cs, cj, ri = -1;
5060         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5061             return cs;
5062         }
5063         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5064             return nodupIEXml(cs);
5065         }
5066         var d = ++key;
5067         cs[0]._nodup = d;
5068         for(i = 1; c = cs[i]; i++){
5069             if(c._nodup != d){
5070                 c._nodup = d;
5071             }else{
5072                 r = [];
5073                 for(var j = 0; j < i; j++){
5074                     r[++ri] = cs[j];
5075                 }
5076                 for(j = i+1; cj = cs[j]; j++){
5077                     if(cj._nodup != d){
5078                         cj._nodup = d;
5079                         r[++ri] = cj;
5080                     }
5081                 }
5082                 return r;
5083             }
5084         }
5085         return r;
5086     }
5087
5088     function quickDiffIEXml(c1, c2){
5089         var d = ++key;
5090         for(var i = 0, len = c1.length; i < len; i++){
5091             c1[i].setAttribute("_qdiff", d);
5092         }
5093         var r = [];
5094         for(var i = 0, len = c2.length; i < len; i++){
5095             if(c2[i].getAttribute("_qdiff") != d){
5096                 r[r.length] = c2[i];
5097             }
5098         }
5099         for(var i = 0, len = c1.length; i < len; i++){
5100            c1[i].removeAttribute("_qdiff");
5101         }
5102         return r;
5103     }
5104
5105     function quickDiff(c1, c2){
5106         var len1 = c1.length;
5107         if(!len1){
5108             return c2;
5109         }
5110         if(isIE && c1[0].selectSingleNode){
5111             return quickDiffIEXml(c1, c2);
5112         }
5113         var d = ++key;
5114         for(var i = 0; i < len1; i++){
5115             c1[i]._qdiff = d;
5116         }
5117         var r = [];
5118         for(var i = 0, len = c2.length; i < len; i++){
5119             if(c2[i]._qdiff != d){
5120                 r[r.length] = c2[i];
5121             }
5122         }
5123         return r;
5124     }
5125
5126     function quickId(ns, mode, root, id){
5127         if(ns == root){
5128            var d = root.ownerDocument || root;
5129            return d.getElementById(id);
5130         }
5131         ns = getNodes(ns, mode, "*");
5132         return byId(ns, null, id);
5133     }
5134
5135     return {
5136         getStyle : function(el, name){
5137             return Roo.fly(el).getStyle(name);
5138         },
5139         /**
5140          * Compiles a selector/xpath query into a reusable function. The returned function
5141          * takes one parameter "root" (optional), which is the context node from where the query should start.
5142          * @param {String} selector The selector/xpath query
5143          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5144          * @return {Function}
5145          */
5146         compile : function(path, type){
5147             type = type || "select";
5148             
5149             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5150             var q = path, mode, lq;
5151             var tk = Roo.DomQuery.matchers;
5152             var tklen = tk.length;
5153             var mm;
5154
5155             // accept leading mode switch
5156             var lmode = q.match(modeRe);
5157             if(lmode && lmode[1]){
5158                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5159                 q = q.replace(lmode[1], "");
5160             }
5161             // strip leading slashes
5162             while(path.substr(0, 1)=="/"){
5163                 path = path.substr(1);
5164             }
5165
5166             while(q && lq != q){
5167                 lq = q;
5168                 var tm = q.match(tagTokenRe);
5169                 if(type == "select"){
5170                     if(tm){
5171                         if(tm[1] == "#"){
5172                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5173                         }else{
5174                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5175                         }
5176                         q = q.replace(tm[0], "");
5177                     }else if(q.substr(0, 1) != '@'){
5178                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5179                     }
5180                 }else{
5181                     if(tm){
5182                         if(tm[1] == "#"){
5183                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5184                         }else{
5185                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5186                         }
5187                         q = q.replace(tm[0], "");
5188                     }
5189                 }
5190                 while(!(mm = q.match(modeRe))){
5191                     var matched = false;
5192                     for(var j = 0; j < tklen; j++){
5193                         var t = tk[j];
5194                         var m = q.match(t.re);
5195                         if(m){
5196                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5197                                                     return m[i];
5198                                                 });
5199                             q = q.replace(m[0], "");
5200                             matched = true;
5201                             break;
5202                         }
5203                     }
5204                     // prevent infinite loop on bad selector
5205                     if(!matched){
5206                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5207                     }
5208                 }
5209                 if(mm[1]){
5210                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5211                     q = q.replace(mm[1], "");
5212                 }
5213             }
5214             fn[fn.length] = "return nodup(n);\n}";
5215             
5216              /** 
5217               * list of variables that need from compression as they are used by eval.
5218              *  eval:var:batch 
5219              *  eval:var:nodup
5220              *  eval:var:byTag
5221              *  eval:var:ById
5222              *  eval:var:getNodes
5223              *  eval:var:quickId
5224              *  eval:var:mode
5225              *  eval:var:root
5226              *  eval:var:n
5227              *  eval:var:byClassName
5228              *  eval:var:byPseudo
5229              *  eval:var:byAttribute
5230              *  eval:var:attrValue
5231              * 
5232              **/ 
5233             eval(fn.join(""));
5234             return f;
5235         },
5236
5237         /**
5238          * Selects a group of elements.
5239          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5240          * @param {Node} root (optional) The start of the query (defaults to document).
5241          * @return {Array}
5242          */
5243         select : function(path, root, type){
5244             if(!root || root == document){
5245                 root = document;
5246             }
5247             if(typeof root == "string"){
5248                 root = document.getElementById(root);
5249             }
5250             var paths = path.split(",");
5251             var results = [];
5252             for(var i = 0, len = paths.length; i < len; i++){
5253                 var p = paths[i].replace(trimRe, "");
5254                 if(!cache[p]){
5255                     cache[p] = Roo.DomQuery.compile(p);
5256                     if(!cache[p]){
5257                         throw p + " is not a valid selector";
5258                     }
5259                 }
5260                 var result = cache[p](root);
5261                 if(result && result != document){
5262                     results = results.concat(result);
5263                 }
5264             }
5265             if(paths.length > 1){
5266                 return nodup(results);
5267             }
5268             return results;
5269         },
5270
5271         /**
5272          * Selects a single element.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @return {Element}
5276          */
5277         selectNode : function(path, root){
5278             return Roo.DomQuery.select(path, root)[0];
5279         },
5280
5281         /**
5282          * Selects the value of a node, optionally replacing null with the defaultValue.
5283          * @param {String} selector The selector/xpath query
5284          * @param {Node} root (optional) The start of the query (defaults to document).
5285          * @param {String} defaultValue
5286          */
5287         selectValue : function(path, root, defaultValue){
5288             path = path.replace(trimRe, "");
5289             if(!valueCache[path]){
5290                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5291             }
5292             var n = valueCache[path](root);
5293             n = n[0] ? n[0] : n;
5294             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5295             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5296         },
5297
5298         /**
5299          * Selects the value of a node, parsing integers and floats.
5300          * @param {String} selector The selector/xpath query
5301          * @param {Node} root (optional) The start of the query (defaults to document).
5302          * @param {Number} defaultValue
5303          * @return {Number}
5304          */
5305         selectNumber : function(path, root, defaultValue){
5306             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5307             return parseFloat(v);
5308         },
5309
5310         /**
5311          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5312          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5313          * @param {String} selector The simple selector to test
5314          * @return {Boolean}
5315          */
5316         is : function(el, ss){
5317             if(typeof el == "string"){
5318                 el = document.getElementById(el);
5319             }
5320             var isArray = (el instanceof Array);
5321             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5322             return isArray ? (result.length == el.length) : (result.length > 0);
5323         },
5324
5325         /**
5326          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5327          * @param {Array} el An array of elements to filter
5328          * @param {String} selector The simple selector to test
5329          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5330          * the selector instead of the ones that match
5331          * @return {Array}
5332          */
5333         filter : function(els, ss, nonMatches){
5334             ss = ss.replace(trimRe, "");
5335             if(!simpleCache[ss]){
5336                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5337             }
5338             var result = simpleCache[ss](els);
5339             return nonMatches ? quickDiff(result, els) : result;
5340         },
5341
5342         /**
5343          * Collection of matching regular expressions and code snippets.
5344          */
5345         matchers : [{
5346                 re: /^\.([\w-]+)/,
5347                 select: 'n = byClassName(n, null, " {1} ");'
5348             }, {
5349                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5350                 select: 'n = byPseudo(n, "{1}", "{2}");'
5351             },{
5352                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5353                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5354             }, {
5355                 re: /^#([\w-]+)/,
5356                 select: 'n = byId(n, null, "{1}");'
5357             },{
5358                 re: /^@([\w-]+)/,
5359                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5360             }
5361         ],
5362
5363         /**
5364          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5365          * 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;.
5366          */
5367         operators : {
5368             "=" : function(a, v){
5369                 return a == v;
5370             },
5371             "!=" : function(a, v){
5372                 return a != v;
5373             },
5374             "^=" : function(a, v){
5375                 return a && a.substr(0, v.length) == v;
5376             },
5377             "$=" : function(a, v){
5378                 return a && a.substr(a.length-v.length) == v;
5379             },
5380             "*=" : function(a, v){
5381                 return a && a.indexOf(v) !== -1;
5382             },
5383             "%=" : function(a, v){
5384                 return (a % v) == 0;
5385             },
5386             "|=" : function(a, v){
5387                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5388             },
5389             "~=" : function(a, v){
5390                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5391             }
5392         },
5393
5394         /**
5395          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5396          * and the argument (if any) supplied in the selector.
5397          */
5398         pseudos : {
5399             "first-child" : function(c){
5400                 var r = [], ri = -1, n;
5401                 for(var i = 0, ci; ci = n = c[i]; i++){
5402                     while((n = n.previousSibling) && n.nodeType != 1);
5403                     if(!n){
5404                         r[++ri] = ci;
5405                     }
5406                 }
5407                 return r;
5408             },
5409
5410             "last-child" : function(c){
5411                 var r = [], ri = -1, n;
5412                 for(var i = 0, ci; ci = n = c[i]; i++){
5413                     while((n = n.nextSibling) && n.nodeType != 1);
5414                     if(!n){
5415                         r[++ri] = ci;
5416                     }
5417                 }
5418                 return r;
5419             },
5420
5421             "nth-child" : function(c, a) {
5422                 var r = [], ri = -1;
5423                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5424                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5425                 for(var i = 0, n; n = c[i]; i++){
5426                     var pn = n.parentNode;
5427                     if (batch != pn._batch) {
5428                         var j = 0;
5429                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5430                             if(cn.nodeType == 1){
5431                                cn.nodeIndex = ++j;
5432                             }
5433                         }
5434                         pn._batch = batch;
5435                     }
5436                     if (f == 1) {
5437                         if (l == 0 || n.nodeIndex == l){
5438                             r[++ri] = n;
5439                         }
5440                     } else if ((n.nodeIndex + l) % f == 0){
5441                         r[++ri] = n;
5442                     }
5443                 }
5444
5445                 return r;
5446             },
5447
5448             "only-child" : function(c){
5449                 var r = [], ri = -1;;
5450                 for(var i = 0, ci; ci = c[i]; i++){
5451                     if(!prev(ci) && !next(ci)){
5452                         r[++ri] = ci;
5453                     }
5454                 }
5455                 return r;
5456             },
5457
5458             "empty" : function(c){
5459                 var r = [], ri = -1;
5460                 for(var i = 0, ci; ci = c[i]; i++){
5461                     var cns = ci.childNodes, j = 0, cn, empty = true;
5462                     while(cn = cns[j]){
5463                         ++j;
5464                         if(cn.nodeType == 1 || cn.nodeType == 3){
5465                             empty = false;
5466                             break;
5467                         }
5468                     }
5469                     if(empty){
5470                         r[++ri] = ci;
5471                     }
5472                 }
5473                 return r;
5474             },
5475
5476             "contains" : function(c, v){
5477                 var r = [], ri = -1;
5478                 for(var i = 0, ci; ci = c[i]; i++){
5479                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5480                         r[++ri] = ci;
5481                     }
5482                 }
5483                 return r;
5484             },
5485
5486             "nodeValue" : function(c, v){
5487                 var r = [], ri = -1;
5488                 for(var i = 0, ci; ci = c[i]; i++){
5489                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5490                         r[++ri] = ci;
5491                     }
5492                 }
5493                 return r;
5494             },
5495
5496             "checked" : function(c){
5497                 var r = [], ri = -1;
5498                 for(var i = 0, ci; ci = c[i]; i++){
5499                     if(ci.checked == true){
5500                         r[++ri] = ci;
5501                     }
5502                 }
5503                 return r;
5504             },
5505
5506             "not" : function(c, ss){
5507                 return Roo.DomQuery.filter(c, ss, true);
5508             },
5509
5510             "odd" : function(c){
5511                 return this["nth-child"](c, "odd");
5512             },
5513
5514             "even" : function(c){
5515                 return this["nth-child"](c, "even");
5516             },
5517
5518             "nth" : function(c, a){
5519                 return c[a-1] || [];
5520             },
5521
5522             "first" : function(c){
5523                 return c[0] || [];
5524             },
5525
5526             "last" : function(c){
5527                 return c[c.length-1] || [];
5528             },
5529
5530             "has" : function(c, ss){
5531                 var s = Roo.DomQuery.select;
5532                 var r = [], ri = -1;
5533                 for(var i = 0, ci; ci = c[i]; i++){
5534                     if(s(ss, ci).length > 0){
5535                         r[++ri] = ci;
5536                     }
5537                 }
5538                 return r;
5539             },
5540
5541             "next" : function(c, ss){
5542                 var is = Roo.DomQuery.is;
5543                 var r = [], ri = -1;
5544                 for(var i = 0, ci; ci = c[i]; i++){
5545                     var n = next(ci);
5546                     if(n && is(n, ss)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "prev" : function(c, ss){
5554                 var is = Roo.DomQuery.is;
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     var n = prev(ci);
5558                     if(n && is(n, ss)){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             }
5564         }
5565     };
5566 }();
5567
5568 /**
5569  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5570  * @param {String} path The selector/xpath query
5571  * @param {Node} root (optional) The start of the query (defaults to document).
5572  * @return {Array}
5573  * @member Roo
5574  * @method query
5575  */
5576 Roo.query = Roo.DomQuery.select;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587
5588 /**
5589  * @class Roo.util.Observable
5590  * Base class that provides a common interface for publishing events. Subclasses are expected to
5591  * to have a property "events" with all the events defined.<br>
5592  * For example:
5593  * <pre><code>
5594  Employee = function(name){
5595     this.name = name;
5596     this.addEvents({
5597         "fired" : true,
5598         "quit" : true
5599     });
5600  }
5601  Roo.extend(Employee, Roo.util.Observable);
5602 </code></pre>
5603  * @param {Object} config properties to use (incuding events / listeners)
5604  */
5605
5606 Roo.util.Observable = function(cfg){
5607     
5608     cfg = cfg|| {};
5609     this.addEvents(cfg.events || {});
5610     if (cfg.events) {
5611         delete cfg.events; // make sure
5612     }
5613      
5614     Roo.apply(this, cfg);
5615     
5616     if(this.listeners){
5617         this.on(this.listeners);
5618         delete this.listeners;
5619     }
5620 };
5621 Roo.util.Observable.prototype = {
5622     /** 
5623  * @cfg {Object} listeners  list of events and functions to call for this object, 
5624  * For example :
5625  * <pre><code>
5626     listeners :  { 
5627        'click' : function(e) {
5628            ..... 
5629         } ,
5630         .... 
5631     } 
5632   </code></pre>
5633  */
5634     
5635     
5636     /**
5637      * Fires the specified event with the passed parameters (minus the event name).
5638      * @param {String} eventName
5639      * @param {Object...} args Variable number of parameters are passed to handlers
5640      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5641      */
5642     fireEvent : function(){
5643         var ce = this.events[arguments[0].toLowerCase()];
5644         if(typeof ce == "object"){
5645             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5646         }else{
5647             return true;
5648         }
5649     },
5650
5651     // private
5652     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5653
5654     /**
5655      * Appends an event handler to this component
5656      * @param {String}   eventName The type of event to listen for
5657      * @param {Function} handler The method the event invokes
5658      * @param {Object}   scope (optional) The scope in which to execute the handler
5659      * function. The handler function's "this" context.
5660      * @param {Object}   options (optional) An object containing handler configuration
5661      * properties. This may contain any of the following properties:<ul>
5662      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5663      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5664      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5665      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5666      * by the specified number of milliseconds. If the event fires again within that time, the original
5667      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5668      * </ul><br>
5669      * <p>
5670      * <b>Combining Options</b><br>
5671      * Using the options argument, it is possible to combine different types of listeners:<br>
5672      * <br>
5673      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5674                 <pre><code>
5675                 el.on('click', this.onClick, this, {
5676                         single: true,
5677                 delay: 100,
5678                 forumId: 4
5679                 });
5680                 </code></pre>
5681      * <p>
5682      * <b>Attaching multiple handlers in 1 call</b><br>
5683      * The method also allows for a single argument to be passed which is a config object containing properties
5684      * which specify multiple handlers.
5685      * <pre><code>
5686                 el.on({
5687                         'click': {
5688                         fn: this.onClick,
5689                         scope: this,
5690                         delay: 100
5691                 }, 
5692                 'mouseover': {
5693                         fn: this.onMouseOver,
5694                         scope: this
5695                 },
5696                 'mouseout': {
5697                         fn: this.onMouseOut,
5698                         scope: this
5699                 }
5700                 });
5701                 </code></pre>
5702      * <p>
5703      * Or a shorthand syntax which passes the same scope object to all handlers:
5704         <pre><code>
5705                 el.on({
5706                         'click': this.onClick,
5707                 'mouseover': this.onMouseOver,
5708                 'mouseout': this.onMouseOut,
5709                 scope: this
5710                 });
5711                 </code></pre>
5712      */
5713     addListener : function(eventName, fn, scope, o){
5714         if(typeof eventName == "object"){
5715             o = eventName;
5716             for(var e in o){
5717                 if(this.filterOptRe.test(e)){
5718                     continue;
5719                 }
5720                 if(typeof o[e] == "function"){
5721                     // shared options
5722                     this.addListener(e, o[e], o.scope,  o);
5723                 }else{
5724                     // individual options
5725                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5726                 }
5727             }
5728             return;
5729         }
5730         o = (!o || typeof o == "boolean") ? {} : o;
5731         eventName = eventName.toLowerCase();
5732         var ce = this.events[eventName] || true;
5733         if(typeof ce == "boolean"){
5734             ce = new Roo.util.Event(this, eventName);
5735             this.events[eventName] = ce;
5736         }
5737         ce.addListener(fn, scope, o);
5738     },
5739
5740     /**
5741      * Removes a listener
5742      * @param {String}   eventName     The type of event to listen for
5743      * @param {Function} handler        The handler to remove
5744      * @param {Object}   scope  (optional) The scope (this object) for the handler
5745      */
5746     removeListener : function(eventName, fn, scope){
5747         var ce = this.events[eventName.toLowerCase()];
5748         if(typeof ce == "object"){
5749             ce.removeListener(fn, scope);
5750         }
5751     },
5752
5753     /**
5754      * Removes all listeners for this object
5755      */
5756     purgeListeners : function(){
5757         for(var evt in this.events){
5758             if(typeof this.events[evt] == "object"){
5759                  this.events[evt].clearListeners();
5760             }
5761         }
5762     },
5763
5764     relayEvents : function(o, events){
5765         var createHandler = function(ename){
5766             return function(){
5767                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5768             };
5769         };
5770         for(var i = 0, len = events.length; i < len; i++){
5771             var ename = events[i];
5772             if(!this.events[ename]){ this.events[ename] = true; };
5773             o.on(ename, createHandler(ename), this);
5774         }
5775     },
5776
5777     /**
5778      * Used to define events on this Observable
5779      * @param {Object} object The object with the events defined
5780      */
5781     addEvents : function(o){
5782         if(!this.events){
5783             this.events = {};
5784         }
5785         Roo.applyIf(this.events, o);
5786     },
5787
5788     /**
5789      * Checks to see if this object has any listeners for a specified event
5790      * @param {String} eventName The name of the event to check for
5791      * @return {Boolean} True if the event is being listened for, else false
5792      */
5793     hasListener : function(eventName){
5794         var e = this.events[eventName];
5795         return typeof e == "object" && e.listeners.length > 0;
5796     }
5797 };
5798 /**
5799  * Appends an event handler to this element (shorthand for addListener)
5800  * @param {String}   eventName     The type of event to listen for
5801  * @param {Function} handler        The method the event invokes
5802  * @param {Object}   scope (optional) The scope in which to execute the handler
5803  * function. The handler function's "this" context.
5804  * @param {Object}   options  (optional)
5805  * @method
5806  */
5807 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5808 /**
5809  * Removes a listener (shorthand for removeListener)
5810  * @param {String}   eventName     The type of event to listen for
5811  * @param {Function} handler        The handler to remove
5812  * @param {Object}   scope  (optional) The scope (this object) for the handler
5813  * @method
5814  */
5815 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5816
5817 /**
5818  * Starts capture on the specified Observable. All events will be passed
5819  * to the supplied function with the event name + standard signature of the event
5820  * <b>before</b> the event is fired. If the supplied function returns false,
5821  * the event will not fire.
5822  * @param {Observable} o The Observable to capture
5823  * @param {Function} fn The function to call
5824  * @param {Object} scope (optional) The scope (this object) for the fn
5825  * @static
5826  */
5827 Roo.util.Observable.capture = function(o, fn, scope){
5828     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5829 };
5830
5831 /**
5832  * Removes <b>all</b> added captures from the Observable.
5833  * @param {Observable} o The Observable to release
5834  * @static
5835  */
5836 Roo.util.Observable.releaseCapture = function(o){
5837     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5838 };
5839
5840 (function(){
5841
5842     var createBuffered = function(h, o, scope){
5843         var task = new Roo.util.DelayedTask();
5844         return function(){
5845             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5846         };
5847     };
5848
5849     var createSingle = function(h, e, fn, scope){
5850         return function(){
5851             e.removeListener(fn, scope);
5852             return h.apply(scope, arguments);
5853         };
5854     };
5855
5856     var createDelayed = function(h, o, scope){
5857         return function(){
5858             var args = Array.prototype.slice.call(arguments, 0);
5859             setTimeout(function(){
5860                 h.apply(scope, args);
5861             }, o.delay || 10);
5862         };
5863     };
5864
5865     Roo.util.Event = function(obj, name){
5866         this.name = name;
5867         this.obj = obj;
5868         this.listeners = [];
5869     };
5870
5871     Roo.util.Event.prototype = {
5872         addListener : function(fn, scope, options){
5873             var o = options || {};
5874             scope = scope || this.obj;
5875             if(!this.isListening(fn, scope)){
5876                 var l = {fn: fn, scope: scope, options: o};
5877                 var h = fn;
5878                 if(o.delay){
5879                     h = createDelayed(h, o, scope);
5880                 }
5881                 if(o.single){
5882                     h = createSingle(h, this, fn, scope);
5883                 }
5884                 if(o.buffer){
5885                     h = createBuffered(h, o, scope);
5886                 }
5887                 l.fireFn = h;
5888                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5889                     this.listeners.push(l);
5890                 }else{
5891                     this.listeners = this.listeners.slice(0);
5892                     this.listeners.push(l);
5893                 }
5894             }
5895         },
5896
5897         findListener : function(fn, scope){
5898             scope = scope || this.obj;
5899             var ls = this.listeners;
5900             for(var i = 0, len = ls.length; i < len; i++){
5901                 var l = ls[i];
5902                 if(l.fn == fn && l.scope == scope){
5903                     return i;
5904                 }
5905             }
5906             return -1;
5907         },
5908
5909         isListening : function(fn, scope){
5910             return this.findListener(fn, scope) != -1;
5911         },
5912
5913         removeListener : function(fn, scope){
5914             var index;
5915             if((index = this.findListener(fn, scope)) != -1){
5916                 if(!this.firing){
5917                     this.listeners.splice(index, 1);
5918                 }else{
5919                     this.listeners = this.listeners.slice(0);
5920                     this.listeners.splice(index, 1);
5921                 }
5922                 return true;
5923             }
5924             return false;
5925         },
5926
5927         clearListeners : function(){
5928             this.listeners = [];
5929         },
5930
5931         fire : function(){
5932             var ls = this.listeners, scope, len = ls.length;
5933             if(len > 0){
5934                 this.firing = true;
5935                 var args = Array.prototype.slice.call(arguments, 0);
5936                 for(var i = 0; i < len; i++){
5937                     var l = ls[i];
5938                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5939                         this.firing = false;
5940                         return false;
5941                     }
5942                 }
5943                 this.firing = false;
5944             }
5945             return true;
5946         }
5947     };
5948 })();/*
5949  * Based on:
5950  * Ext JS Library 1.1.1
5951  * Copyright(c) 2006-2007, Ext JS, LLC.
5952  *
5953  * Originally Released Under LGPL - original licence link has changed is not relivant.
5954  *
5955  * Fork - LGPL
5956  * <script type="text/javascript">
5957  */
5958
5959 /**
5960  * @class Roo.EventManager
5961  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5962  * several useful events directly.
5963  * See {@link Roo.EventObject} for more details on normalized event objects.
5964  * @singleton
5965  */
5966 Roo.EventManager = function(){
5967     var docReadyEvent, docReadyProcId, docReadyState = false;
5968     var resizeEvent, resizeTask, textEvent, textSize;
5969     var E = Roo.lib.Event;
5970     var D = Roo.lib.Dom;
5971
5972
5973     var fireDocReady = function(){
5974         if(!docReadyState){
5975             docReadyState = true;
5976             Roo.isReady = true;
5977             if(docReadyProcId){
5978                 clearInterval(docReadyProcId);
5979             }
5980             if(Roo.isGecko || Roo.isOpera) {
5981                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5982             }
5983             if(Roo.isIE){
5984                 var defer = document.getElementById("ie-deferred-loader");
5985                 if(defer){
5986                     defer.onreadystatechange = null;
5987                     defer.parentNode.removeChild(defer);
5988                 }
5989             }
5990             if(docReadyEvent){
5991                 docReadyEvent.fire();
5992                 docReadyEvent.clearListeners();
5993             }
5994         }
5995     };
5996     
5997     var initDocReady = function(){
5998         docReadyEvent = new Roo.util.Event();
5999         if(Roo.isGecko || Roo.isOpera) {
6000             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6001         }else if(Roo.isIE){
6002             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6003             var defer = document.getElementById("ie-deferred-loader");
6004             defer.onreadystatechange = function(){
6005                 if(this.readyState == "complete"){
6006                     fireDocReady();
6007                 }
6008             };
6009         }else if(Roo.isSafari){ 
6010             docReadyProcId = setInterval(function(){
6011                 var rs = document.readyState;
6012                 if(rs == "complete") {
6013                     fireDocReady();     
6014                  }
6015             }, 10);
6016         }
6017         // no matter what, make sure it fires on load
6018         E.on(window, "load", fireDocReady);
6019     };
6020
6021     var createBuffered = function(h, o){
6022         var task = new Roo.util.DelayedTask(h);
6023         return function(e){
6024             // create new event object impl so new events don't wipe out properties
6025             e = new Roo.EventObjectImpl(e);
6026             task.delay(o.buffer, h, null, [e]);
6027         };
6028     };
6029
6030     var createSingle = function(h, el, ename, fn){
6031         return function(e){
6032             Roo.EventManager.removeListener(el, ename, fn);
6033             h(e);
6034         };
6035     };
6036
6037     var createDelayed = function(h, o){
6038         return function(e){
6039             // create new event object impl so new events don't wipe out properties
6040             e = new Roo.EventObjectImpl(e);
6041             setTimeout(function(){
6042                 h(e);
6043             }, o.delay || 10);
6044         };
6045     };
6046
6047     var listen = function(element, ename, opt, fn, scope){
6048         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6049         fn = fn || o.fn; scope = scope || o.scope;
6050         var el = Roo.getDom(element);
6051         if(!el){
6052             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6053         }
6054         var h = function(e){
6055             e = Roo.EventObject.setEvent(e);
6056             var t;
6057             if(o.delegate){
6058                 t = e.getTarget(o.delegate, el);
6059                 if(!t){
6060                     return;
6061                 }
6062             }else{
6063                 t = e.target;
6064             }
6065             if(o.stopEvent === true){
6066                 e.stopEvent();
6067             }
6068             if(o.preventDefault === true){
6069                e.preventDefault();
6070             }
6071             if(o.stopPropagation === true){
6072                 e.stopPropagation();
6073             }
6074
6075             if(o.normalized === false){
6076                 e = e.browserEvent;
6077             }
6078
6079             fn.call(scope || el, e, t, o);
6080         };
6081         if(o.delay){
6082             h = createDelayed(h, o);
6083         }
6084         if(o.single){
6085             h = createSingle(h, el, ename, fn);
6086         }
6087         if(o.buffer){
6088             h = createBuffered(h, o);
6089         }
6090         fn._handlers = fn._handlers || [];
6091         fn._handlers.push([Roo.id(el), ename, h]);
6092
6093         E.on(el, ename, h);
6094         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6095             el.addEventListener("DOMMouseScroll", h, false);
6096             E.on(window, 'unload', function(){
6097                 el.removeEventListener("DOMMouseScroll", h, false);
6098             });
6099         }
6100         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6101             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6102         }
6103         return h;
6104     };
6105
6106     var stopListening = function(el, ename, fn){
6107         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6108         if(hds){
6109             for(var i = 0, len = hds.length; i < len; i++){
6110                 var h = hds[i];
6111                 if(h[0] == id && h[1] == ename){
6112                     hd = h[2];
6113                     hds.splice(i, 1);
6114                     break;
6115                 }
6116             }
6117         }
6118         E.un(el, ename, hd);
6119         el = Roo.getDom(el);
6120         if(ename == "mousewheel" && el.addEventListener){
6121             el.removeEventListener("DOMMouseScroll", hd, false);
6122         }
6123         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6124             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6125         }
6126     };
6127
6128     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6129     
6130     var pub = {
6131         
6132         
6133         /** 
6134          * Fix for doc tools
6135          * @scope Roo.EventManager
6136          */
6137         
6138         
6139         /** 
6140          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6141          * object with a Roo.EventObject
6142          * @param {Function} fn        The method the event invokes
6143          * @param {Object}   scope    An object that becomes the scope of the handler
6144          * @param {boolean}  override If true, the obj passed in becomes
6145          *                             the execution scope of the listener
6146          * @return {Function} The wrapped function
6147          * @deprecated
6148          */
6149         wrap : function(fn, scope, override){
6150             return function(e){
6151                 Roo.EventObject.setEvent(e);
6152                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6153             };
6154         },
6155         
6156         /**
6157      * Appends an event handler to an element (shorthand for addListener)
6158      * @param {String/HTMLElement}   element        The html element or id to assign the
6159      * @param {String}   eventName The type of event to listen for
6160      * @param {Function} handler The method the event invokes
6161      * @param {Object}   scope (optional) The scope in which to execute the handler
6162      * function. The handler function's "this" context.
6163      * @param {Object}   options (optional) An object containing handler configuration
6164      * properties. This may contain any of the following properties:<ul>
6165      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6166      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6167      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6168      * <li>preventDefault {Boolean} True to prevent the default action</li>
6169      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6170      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6171      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6172      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6173      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6174      * by the specified number of milliseconds. If the event fires again within that time, the original
6175      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6176      * </ul><br>
6177      * <p>
6178      * <b>Combining Options</b><br>
6179      * Using the options argument, it is possible to combine different types of listeners:<br>
6180      * <br>
6181      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6182      * Code:<pre><code>
6183 el.on('click', this.onClick, this, {
6184     single: true,
6185     delay: 100,
6186     stopEvent : true,
6187     forumId: 4
6188 });</code></pre>
6189      * <p>
6190      * <b>Attaching multiple handlers in 1 call</b><br>
6191       * The method also allows for a single argument to be passed which is a config object containing properties
6192      * which specify multiple handlers.
6193      * <p>
6194      * Code:<pre><code>
6195 el.on({
6196     'click' : {
6197         fn: this.onClick
6198         scope: this,
6199         delay: 100
6200     },
6201     'mouseover' : {
6202         fn: this.onMouseOver
6203         scope: this
6204     },
6205     'mouseout' : {
6206         fn: this.onMouseOut
6207         scope: this
6208     }
6209 });</code></pre>
6210      * <p>
6211      * Or a shorthand syntax:<br>
6212      * Code:<pre><code>
6213 el.on({
6214     'click' : this.onClick,
6215     'mouseover' : this.onMouseOver,
6216     'mouseout' : this.onMouseOut
6217     scope: this
6218 });</code></pre>
6219      */
6220         addListener : function(element, eventName, fn, scope, options){
6221             if(typeof eventName == "object"){
6222                 var o = eventName;
6223                 for(var e in o){
6224                     if(propRe.test(e)){
6225                         continue;
6226                     }
6227                     if(typeof o[e] == "function"){
6228                         // shared options
6229                         listen(element, e, o, o[e], o.scope);
6230                     }else{
6231                         // individual options
6232                         listen(element, e, o[e]);
6233                     }
6234                 }
6235                 return;
6236             }
6237             return listen(element, eventName, options, fn, scope);
6238         },
6239         
6240         /**
6241          * Removes an event handler
6242          *
6243          * @param {String/HTMLElement}   element        The id or html element to remove the 
6244          *                             event from
6245          * @param {String}   eventName     The type of event
6246          * @param {Function} fn
6247          * @return {Boolean} True if a listener was actually removed
6248          */
6249         removeListener : function(element, eventName, fn){
6250             return stopListening(element, eventName, fn);
6251         },
6252         
6253         /**
6254          * Fires when the document is ready (before onload and before images are loaded). Can be 
6255          * accessed shorthanded Roo.onReady().
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An  object that becomes the scope of the handler
6258          * @param {boolean}  options
6259          */
6260         onDocumentReady : function(fn, scope, options){
6261             if(docReadyState){ // if it already fired
6262                 docReadyEvent.addListener(fn, scope, options);
6263                 docReadyEvent.fire();
6264                 docReadyEvent.clearListeners();
6265                 return;
6266             }
6267             if(!docReadyEvent){
6268                 initDocReady();
6269             }
6270             docReadyEvent.addListener(fn, scope, options);
6271         },
6272         
6273         /**
6274          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6275          * @param {Function} fn        The method the event invokes
6276          * @param {Object}   scope    An object that becomes the scope of the handler
6277          * @param {boolean}  options
6278          */
6279         onWindowResize : function(fn, scope, options){
6280             if(!resizeEvent){
6281                 resizeEvent = new Roo.util.Event();
6282                 resizeTask = new Roo.util.DelayedTask(function(){
6283                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6284                 });
6285                 E.on(window, "resize", function(){
6286                     if(Roo.isIE){
6287                         resizeTask.delay(50);
6288                     }else{
6289                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                     }
6291                 });
6292             }
6293             resizeEvent.addListener(fn, scope, options);
6294         },
6295
6296         /**
6297          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6298          * @param {Function} fn        The method the event invokes
6299          * @param {Object}   scope    An object that becomes the scope of the handler
6300          * @param {boolean}  options
6301          */
6302         onTextResize : function(fn, scope, options){
6303             if(!textEvent){
6304                 textEvent = new Roo.util.Event();
6305                 var textEl = new Roo.Element(document.createElement('div'));
6306                 textEl.dom.className = 'x-text-resize';
6307                 textEl.dom.innerHTML = 'X';
6308                 textEl.appendTo(document.body);
6309                 textSize = textEl.dom.offsetHeight;
6310                 setInterval(function(){
6311                     if(textEl.dom.offsetHeight != textSize){
6312                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6313                     }
6314                 }, this.textResizeInterval);
6315             }
6316             textEvent.addListener(fn, scope, options);
6317         },
6318
6319         /**
6320          * Removes the passed window resize listener.
6321          * @param {Function} fn        The method the event invokes
6322          * @param {Object}   scope    The scope of handler
6323          */
6324         removeResizeListener : function(fn, scope){
6325             if(resizeEvent){
6326                 resizeEvent.removeListener(fn, scope);
6327             }
6328         },
6329
6330         // private
6331         fireResize : function(){
6332             if(resizeEvent){
6333                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6334             }   
6335         },
6336         /**
6337          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6338          */
6339         ieDeferSrc : false,
6340         /**
6341          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6342          */
6343         textResizeInterval : 50
6344     };
6345     
6346     /**
6347      * Fix for doc tools
6348      * @scopeAlias pub=Roo.EventManager
6349      */
6350     
6351      /**
6352      * Appends an event handler to an element (shorthand for addListener)
6353      * @param {String/HTMLElement}   element        The html element or id to assign the
6354      * @param {String}   eventName The type of event to listen for
6355      * @param {Function} handler The method the event invokes
6356      * @param {Object}   scope (optional) The scope in which to execute the handler
6357      * function. The handler function's "this" context.
6358      * @param {Object}   options (optional) An object containing handler configuration
6359      * properties. This may contain any of the following properties:<ul>
6360      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6361      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6362      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6363      * <li>preventDefault {Boolean} True to prevent the default action</li>
6364      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6365      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6366      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6367      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6368      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6369      * by the specified number of milliseconds. If the event fires again within that time, the original
6370      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6371      * </ul><br>
6372      * <p>
6373      * <b>Combining Options</b><br>
6374      * Using the options argument, it is possible to combine different types of listeners:<br>
6375      * <br>
6376      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6377      * Code:<pre><code>
6378 el.on('click', this.onClick, this, {
6379     single: true,
6380     delay: 100,
6381     stopEvent : true,
6382     forumId: 4
6383 });</code></pre>
6384      * <p>
6385      * <b>Attaching multiple handlers in 1 call</b><br>
6386       * The method also allows for a single argument to be passed which is a config object containing properties
6387      * which specify multiple handlers.
6388      * <p>
6389      * Code:<pre><code>
6390 el.on({
6391     'click' : {
6392         fn: this.onClick
6393         scope: this,
6394         delay: 100
6395     },
6396     'mouseover' : {
6397         fn: this.onMouseOver
6398         scope: this
6399     },
6400     'mouseout' : {
6401         fn: this.onMouseOut
6402         scope: this
6403     }
6404 });</code></pre>
6405      * <p>
6406      * Or a shorthand syntax:<br>
6407      * Code:<pre><code>
6408 el.on({
6409     'click' : this.onClick,
6410     'mouseover' : this.onMouseOver,
6411     'mouseout' : this.onMouseOut
6412     scope: this
6413 });</code></pre>
6414      */
6415     pub.on = pub.addListener;
6416     pub.un = pub.removeListener;
6417
6418     pub.stoppedMouseDownEvent = new Roo.util.Event();
6419     return pub;
6420 }();
6421 /**
6422   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6423   * @param {Function} fn        The method the event invokes
6424   * @param {Object}   scope    An  object that becomes the scope of the handler
6425   * @param {boolean}  override If true, the obj passed in becomes
6426   *                             the execution scope of the listener
6427   * @member Roo
6428   * @method onReady
6429  */
6430 Roo.onReady = Roo.EventManager.onDocumentReady;
6431
6432 Roo.onReady(function(){
6433     var bd = Roo.get(document.body);
6434     if(!bd){ return; }
6435
6436     var cls = [
6437             Roo.isIE ? "roo-ie"
6438             : Roo.isGecko ? "roo-gecko"
6439             : Roo.isOpera ? "roo-opera"
6440             : Roo.isSafari ? "roo-safari" : ""];
6441
6442     if(Roo.isMac){
6443         cls.push("roo-mac");
6444     }
6445     if(Roo.isLinux){
6446         cls.push("roo-linux");
6447     }
6448     if(Roo.isBorderBox){
6449         cls.push('roo-border-box');
6450     }
6451     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6452         var p = bd.dom.parentNode;
6453         if(p){
6454             p.className += ' roo-strict';
6455         }
6456     }
6457     bd.addClass(cls.join(' '));
6458 });
6459
6460 /**
6461  * @class Roo.EventObject
6462  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6463  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6464  * Example:
6465  * <pre><code>
6466  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6467     e.preventDefault();
6468     var target = e.getTarget();
6469     ...
6470  }
6471  var myDiv = Roo.get("myDiv");
6472  myDiv.on("click", handleClick);
6473  //or
6474  Roo.EventManager.on("myDiv", 'click', handleClick);
6475  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6476  </code></pre>
6477  * @singleton
6478  */
6479 Roo.EventObject = function(){
6480     
6481     var E = Roo.lib.Event;
6482     
6483     // safari keypress events for special keys return bad keycodes
6484     var safariKeys = {
6485         63234 : 37, // left
6486         63235 : 39, // right
6487         63232 : 38, // up
6488         63233 : 40, // down
6489         63276 : 33, // page up
6490         63277 : 34, // page down
6491         63272 : 46, // delete
6492         63273 : 36, // home
6493         63275 : 35  // end
6494     };
6495
6496     // normalize button clicks
6497     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6498                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6499
6500     Roo.EventObjectImpl = function(e){
6501         if(e){
6502             this.setEvent(e.browserEvent || e);
6503         }
6504     };
6505     Roo.EventObjectImpl.prototype = {
6506         /**
6507          * Used to fix doc tools.
6508          * @scope Roo.EventObject.prototype
6509          */
6510             
6511
6512         
6513         
6514         /** The normal browser event */
6515         browserEvent : null,
6516         /** The button pressed in a mouse event */
6517         button : -1,
6518         /** True if the shift key was down during the event */
6519         shiftKey : false,
6520         /** True if the control key was down during the event */
6521         ctrlKey : false,
6522         /** True if the alt key was down during the event */
6523         altKey : false,
6524
6525         /** Key constant 
6526         * @type Number */
6527         BACKSPACE : 8,
6528         /** Key constant 
6529         * @type Number */
6530         TAB : 9,
6531         /** Key constant 
6532         * @type Number */
6533         RETURN : 13,
6534         /** Key constant 
6535         * @type Number */
6536         ENTER : 13,
6537         /** Key constant 
6538         * @type Number */
6539         SHIFT : 16,
6540         /** Key constant 
6541         * @type Number */
6542         CONTROL : 17,
6543         /** Key constant 
6544         * @type Number */
6545         ESC : 27,
6546         /** Key constant 
6547         * @type Number */
6548         SPACE : 32,
6549         /** Key constant 
6550         * @type Number */
6551         PAGEUP : 33,
6552         /** Key constant 
6553         * @type Number */
6554         PAGEDOWN : 34,
6555         /** Key constant 
6556         * @type Number */
6557         END : 35,
6558         /** Key constant 
6559         * @type Number */
6560         HOME : 36,
6561         /** Key constant 
6562         * @type Number */
6563         LEFT : 37,
6564         /** Key constant 
6565         * @type Number */
6566         UP : 38,
6567         /** Key constant 
6568         * @type Number */
6569         RIGHT : 39,
6570         /** Key constant 
6571         * @type Number */
6572         DOWN : 40,
6573         /** Key constant 
6574         * @type Number */
6575         DELETE : 46,
6576         /** Key constant 
6577         * @type Number */
6578         F5 : 116,
6579
6580            /** @private */
6581         setEvent : function(e){
6582             if(e == this || (e && e.browserEvent)){ // already wrapped
6583                 return e;
6584             }
6585             this.browserEvent = e;
6586             if(e){
6587                 // normalize buttons
6588                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6589                 if(e.type == 'click' && this.button == -1){
6590                     this.button = 0;
6591                 }
6592                 this.type = e.type;
6593                 this.shiftKey = e.shiftKey;
6594                 // mac metaKey behaves like ctrlKey
6595                 this.ctrlKey = e.ctrlKey || e.metaKey;
6596                 this.altKey = e.altKey;
6597                 // in getKey these will be normalized for the mac
6598                 this.keyCode = e.keyCode;
6599                 // keyup warnings on firefox.
6600                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6601                 // cache the target for the delayed and or buffered events
6602                 this.target = E.getTarget(e);
6603                 // same for XY
6604                 this.xy = E.getXY(e);
6605             }else{
6606                 this.button = -1;
6607                 this.shiftKey = false;
6608                 this.ctrlKey = false;
6609                 this.altKey = false;
6610                 this.keyCode = 0;
6611                 this.charCode =0;
6612                 this.target = null;
6613                 this.xy = [0, 0];
6614             }
6615             return this;
6616         },
6617
6618         /**
6619          * Stop the event (preventDefault and stopPropagation)
6620          */
6621         stopEvent : function(){
6622             if(this.browserEvent){
6623                 if(this.browserEvent.type == 'mousedown'){
6624                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6625                 }
6626                 E.stopEvent(this.browserEvent);
6627             }
6628         },
6629
6630         /**
6631          * Prevents the browsers default handling of the event.
6632          */
6633         preventDefault : function(){
6634             if(this.browserEvent){
6635                 E.preventDefault(this.browserEvent);
6636             }
6637         },
6638
6639         /** @private */
6640         isNavKeyPress : function(){
6641             var k = this.keyCode;
6642             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6643             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6644         },
6645
6646         isSpecialKey : function(){
6647             var k = this.keyCode;
6648             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6649             (k == 16) || (k == 17) ||
6650             (k >= 18 && k <= 20) ||
6651             (k >= 33 && k <= 35) ||
6652             (k >= 36 && k <= 39) ||
6653             (k >= 44 && k <= 45);
6654         },
6655         /**
6656          * Cancels bubbling of the event.
6657          */
6658         stopPropagation : function(){
6659             if(this.browserEvent){
6660                 if(this.type == 'mousedown'){
6661                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6662                 }
6663                 E.stopPropagation(this.browserEvent);
6664             }
6665         },
6666
6667         /**
6668          * Gets the key code for the event.
6669          * @return {Number}
6670          */
6671         getCharCode : function(){
6672             return this.charCode || this.keyCode;
6673         },
6674
6675         /**
6676          * Returns a normalized keyCode for the event.
6677          * @return {Number} The key code
6678          */
6679         getKey : function(){
6680             var k = this.keyCode || this.charCode;
6681             return Roo.isSafari ? (safariKeys[k] || k) : k;
6682         },
6683
6684         /**
6685          * Gets the x coordinate of the event.
6686          * @return {Number}
6687          */
6688         getPageX : function(){
6689             return this.xy[0];
6690         },
6691
6692         /**
6693          * Gets the y coordinate of the event.
6694          * @return {Number}
6695          */
6696         getPageY : function(){
6697             return this.xy[1];
6698         },
6699
6700         /**
6701          * Gets the time of the event.
6702          * @return {Number}
6703          */
6704         getTime : function(){
6705             if(this.browserEvent){
6706                 return E.getTime(this.browserEvent);
6707             }
6708             return null;
6709         },
6710
6711         /**
6712          * Gets the page coordinates of the event.
6713          * @return {Array} The xy values like [x, y]
6714          */
6715         getXY : function(){
6716             return this.xy;
6717         },
6718
6719         /**
6720          * Gets the target for the event.
6721          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6722          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6723                 search as a number or element (defaults to 10 || document.body)
6724          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6725          * @return {HTMLelement}
6726          */
6727         getTarget : function(selector, maxDepth, returnEl){
6728             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6729         },
6730         /**
6731          * Gets the related target.
6732          * @return {HTMLElement}
6733          */
6734         getRelatedTarget : function(){
6735             if(this.browserEvent){
6736                 return E.getRelatedTarget(this.browserEvent);
6737             }
6738             return null;
6739         },
6740
6741         /**
6742          * Normalizes mouse wheel delta across browsers
6743          * @return {Number} The delta
6744          */
6745         getWheelDelta : function(){
6746             var e = this.browserEvent;
6747             var delta = 0;
6748             if(e.wheelDelta){ /* IE/Opera. */
6749                 delta = e.wheelDelta/120;
6750             }else if(e.detail){ /* Mozilla case. */
6751                 delta = -e.detail/3;
6752             }
6753             return delta;
6754         },
6755
6756         /**
6757          * Returns true if the control, meta, shift or alt key was pressed during this event.
6758          * @return {Boolean}
6759          */
6760         hasModifier : function(){
6761             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6762         },
6763
6764         /**
6765          * Returns true if the target of this event equals el or is a child of el
6766          * @param {String/HTMLElement/Element} el
6767          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6768          * @return {Boolean}
6769          */
6770         within : function(el, related){
6771             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6772             return t && Roo.fly(el).contains(t);
6773         },
6774
6775         getPoint : function(){
6776             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6777         }
6778     };
6779
6780     return new Roo.EventObjectImpl();
6781 }();
6782             
6783     /*
6784  * Based on:
6785  * Ext JS Library 1.1.1
6786  * Copyright(c) 2006-2007, Ext JS, LLC.
6787  *
6788  * Originally Released Under LGPL - original licence link has changed is not relivant.
6789  *
6790  * Fork - LGPL
6791  * <script type="text/javascript">
6792  */
6793
6794  
6795 // was in Composite Element!??!?!
6796  
6797 (function(){
6798     var D = Roo.lib.Dom;
6799     var E = Roo.lib.Event;
6800     var A = Roo.lib.Anim;
6801
6802     // local style camelizing for speed
6803     var propCache = {};
6804     var camelRe = /(-[a-z])/gi;
6805     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6806     var view = document.defaultView;
6807
6808 /**
6809  * @class Roo.Element
6810  * Represents an Element in the DOM.<br><br>
6811  * Usage:<br>
6812 <pre><code>
6813 var el = Roo.get("my-div");
6814
6815 // or with getEl
6816 var el = getEl("my-div");
6817
6818 // or with a DOM element
6819 var el = Roo.get(myDivElement);
6820 </code></pre>
6821  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6822  * each call instead of constructing a new one.<br><br>
6823  * <b>Animations</b><br />
6824  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6825  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6826 <pre>
6827 Option    Default   Description
6828 --------- --------  ---------------------------------------------
6829 duration  .35       The duration of the animation in seconds
6830 easing    easeOut   The YUI easing method
6831 callback  none      A function to execute when the anim completes
6832 scope     this      The scope (this) of the callback function
6833 </pre>
6834 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6835 * manipulate the animation. Here's an example:
6836 <pre><code>
6837 var el = Roo.get("my-div");
6838
6839 // no animation
6840 el.setWidth(100);
6841
6842 // default animation
6843 el.setWidth(100, true);
6844
6845 // animation with some options set
6846 el.setWidth(100, {
6847     duration: 1,
6848     callback: this.foo,
6849     scope: this
6850 });
6851
6852 // using the "anim" property to get the Anim object
6853 var opt = {
6854     duration: 1,
6855     callback: this.foo,
6856     scope: this
6857 };
6858 el.setWidth(100, opt);
6859 ...
6860 if(opt.anim.isAnimated()){
6861     opt.anim.stop();
6862 }
6863 </code></pre>
6864 * <b> Composite (Collections of) Elements</b><br />
6865  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6866  * @constructor Create a new Element directly.
6867  * @param {String/HTMLElement} element
6868  * @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).
6869  */
6870     Roo.Element = function(element, forceNew){
6871         var dom = typeof element == "string" ?
6872                 document.getElementById(element) : element;
6873         if(!dom){ // invalid id/element
6874             return null;
6875         }
6876         var id = dom.id;
6877         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6878             return Roo.Element.cache[id];
6879         }
6880
6881         /**
6882          * The DOM element
6883          * @type HTMLElement
6884          */
6885         this.dom = dom;
6886
6887         /**
6888          * The DOM element ID
6889          * @type String
6890          */
6891         this.id = id || Roo.id(dom);
6892     };
6893
6894     var El = Roo.Element;
6895
6896     El.prototype = {
6897         /**
6898          * The element's default display mode  (defaults to "")
6899          * @type String
6900          */
6901         originalDisplay : "",
6902
6903         visibilityMode : 1,
6904         /**
6905          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6906          * @type String
6907          */
6908         defaultUnit : "px",
6909         /**
6910          * Sets the element's visibility mode. When setVisible() is called it
6911          * will use this to determine whether to set the visibility or the display property.
6912          * @param visMode Element.VISIBILITY or Element.DISPLAY
6913          * @return {Roo.Element} this
6914          */
6915         setVisibilityMode : function(visMode){
6916             this.visibilityMode = visMode;
6917             return this;
6918         },
6919         /**
6920          * Convenience method for setVisibilityMode(Element.DISPLAY)
6921          * @param {String} display (optional) What to set display to when visible
6922          * @return {Roo.Element} this
6923          */
6924         enableDisplayMode : function(display){
6925             this.setVisibilityMode(El.DISPLAY);
6926             if(typeof display != "undefined") this.originalDisplay = display;
6927             return this;
6928         },
6929
6930         /**
6931          * 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)
6932          * @param {String} selector The simple selector to test
6933          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6934                 search as a number or element (defaults to 10 || document.body)
6935          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6936          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6937          */
6938         findParent : function(simpleSelector, maxDepth, returnEl){
6939             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6940             maxDepth = maxDepth || 50;
6941             if(typeof maxDepth != "number"){
6942                 stopEl = Roo.getDom(maxDepth);
6943                 maxDepth = 10;
6944             }
6945             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6946                 if(dq.is(p, simpleSelector)){
6947                     return returnEl ? Roo.get(p) : p;
6948                 }
6949                 depth++;
6950                 p = p.parentNode;
6951             }
6952             return null;
6953         },
6954
6955
6956         /**
6957          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6960                 search as a number or element (defaults to 10 || document.body)
6961          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6962          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6963          */
6964         findParentNode : function(simpleSelector, maxDepth, returnEl){
6965             var p = Roo.fly(this.dom.parentNode, '_internal');
6966             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6967         },
6968
6969         /**
6970          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6971          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6972          * @param {String} selector The simple selector to test
6973          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6974                 search as a number or element (defaults to 10 || document.body)
6975          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6976          */
6977         up : function(simpleSelector, maxDepth){
6978             return this.findParentNode(simpleSelector, maxDepth, true);
6979         },
6980
6981
6982
6983         /**
6984          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6985          * @param {String} selector The simple selector to test
6986          * @return {Boolean} True if this element matches the selector, else false
6987          */
6988         is : function(simpleSelector){
6989             return Roo.DomQuery.is(this.dom, simpleSelector);
6990         },
6991
6992         /**
6993          * Perform animation on this element.
6994          * @param {Object} args The YUI animation control args
6995          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6996          * @param {Function} onComplete (optional) Function to call when animation completes
6997          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6998          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6999          * @return {Roo.Element} this
7000          */
7001         animate : function(args, duration, onComplete, easing, animType){
7002             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7003             return this;
7004         },
7005
7006         /*
7007          * @private Internal animation call
7008          */
7009         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7010             animType = animType || 'run';
7011             opt = opt || {};
7012             var anim = Roo.lib.Anim[animType](
7013                 this.dom, args,
7014                 (opt.duration || defaultDur) || .35,
7015                 (opt.easing || defaultEase) || 'easeOut',
7016                 function(){
7017                     Roo.callback(cb, this);
7018                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7019                 },
7020                 this
7021             );
7022             opt.anim = anim;
7023             return anim;
7024         },
7025
7026         // private legacy anim prep
7027         preanim : function(a, i){
7028             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7029         },
7030
7031         /**
7032          * Removes worthless text nodes
7033          * @param {Boolean} forceReclean (optional) By default the element
7034          * keeps track if it has been cleaned already so
7035          * you can call this over and over. However, if you update the element and
7036          * need to force a reclean, you can pass true.
7037          */
7038         clean : function(forceReclean){
7039             if(this.isCleaned && forceReclean !== true){
7040                 return this;
7041             }
7042             var ns = /\S/;
7043             var d = this.dom, n = d.firstChild, ni = -1;
7044             while(n){
7045                 var nx = n.nextSibling;
7046                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7047                     d.removeChild(n);
7048                 }else{
7049                     n.nodeIndex = ++ni;
7050                 }
7051                 n = nx;
7052             }
7053             this.isCleaned = true;
7054             return this;
7055         },
7056
7057         // private
7058         calcOffsetsTo : function(el){
7059             el = Roo.get(el);
7060             var d = el.dom;
7061             var restorePos = false;
7062             if(el.getStyle('position') == 'static'){
7063                 el.position('relative');
7064                 restorePos = true;
7065             }
7066             var x = 0, y =0;
7067             var op = this.dom;
7068             while(op && op != d && op.tagName != 'HTML'){
7069                 x+= op.offsetLeft;
7070                 y+= op.offsetTop;
7071                 op = op.offsetParent;
7072             }
7073             if(restorePos){
7074                 el.position('static');
7075             }
7076             return [x, y];
7077         },
7078
7079         /**
7080          * Scrolls this element into view within the passed container.
7081          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7082          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7083          * @return {Roo.Element} this
7084          */
7085         scrollIntoView : function(container, hscroll){
7086             var c = Roo.getDom(container) || document.body;
7087             var el = this.dom;
7088
7089             var o = this.calcOffsetsTo(c),
7090                 l = o[0],
7091                 t = o[1],
7092                 b = t+el.offsetHeight,
7093                 r = l+el.offsetWidth;
7094
7095             var ch = c.clientHeight;
7096             var ct = parseInt(c.scrollTop, 10);
7097             var cl = parseInt(c.scrollLeft, 10);
7098             var cb = ct + ch;
7099             var cr = cl + c.clientWidth;
7100
7101             if(t < ct){
7102                 c.scrollTop = t;
7103             }else if(b > cb){
7104                 c.scrollTop = b-ch;
7105             }
7106
7107             if(hscroll !== false){
7108                 if(l < cl){
7109                     c.scrollLeft = l;
7110                 }else if(r > cr){
7111                     c.scrollLeft = r-c.clientWidth;
7112                 }
7113             }
7114             return this;
7115         },
7116
7117         // private
7118         scrollChildIntoView : function(child, hscroll){
7119             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7120         },
7121
7122         /**
7123          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7124          * the new height may not be available immediately.
7125          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7126          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7129          * @return {Roo.Element} this
7130          */
7131         autoHeight : function(animate, duration, onComplete, easing){
7132             var oldHeight = this.getHeight();
7133             this.clip();
7134             this.setHeight(1); // force clipping
7135             setTimeout(function(){
7136                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7137                 if(!animate){
7138                     this.setHeight(height);
7139                     this.unclip();
7140                     if(typeof onComplete == "function"){
7141                         onComplete();
7142                     }
7143                 }else{
7144                     this.setHeight(oldHeight); // restore original height
7145                     this.setHeight(height, animate, duration, function(){
7146                         this.unclip();
7147                         if(typeof onComplete == "function") onComplete();
7148                     }.createDelegate(this), easing);
7149                 }
7150             }.createDelegate(this), 0);
7151             return this;
7152         },
7153
7154         /**
7155          * Returns true if this element is an ancestor of the passed element
7156          * @param {HTMLElement/String} el The element to check
7157          * @return {Boolean} True if this element is an ancestor of el, else false
7158          */
7159         contains : function(el){
7160             if(!el){return false;}
7161             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7162         },
7163
7164         /**
7165          * Checks whether the element is currently visible using both visibility and display properties.
7166          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7167          * @return {Boolean} True if the element is currently visible, else false
7168          */
7169         isVisible : function(deep) {
7170             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7171             if(deep !== true || !vis){
7172                 return vis;
7173             }
7174             var p = this.dom.parentNode;
7175             while(p && p.tagName.toLowerCase() != "body"){
7176                 if(!Roo.fly(p, '_isVisible').isVisible()){
7177                     return false;
7178                 }
7179                 p = p.parentNode;
7180             }
7181             return true;
7182         },
7183
7184         /**
7185          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7186          * @param {String} selector The CSS selector
7187          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7188          * @return {CompositeElement/CompositeElementLite} The composite element
7189          */
7190         select : function(selector, unique){
7191             return El.select(selector, unique, this.dom);
7192         },
7193
7194         /**
7195          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7196          * @param {String} selector The CSS selector
7197          * @return {Array} An array of the matched nodes
7198          */
7199         query : function(selector, unique){
7200             return Roo.DomQuery.select(selector, this.dom);
7201         },
7202
7203         /**
7204          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7205          * @param {String} selector The CSS selector
7206          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7207          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7208          */
7209         child : function(selector, returnDom){
7210             var n = Roo.DomQuery.selectNode(selector, this.dom);
7211             return returnDom ? n : Roo.get(n);
7212         },
7213
7214         /**
7215          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7216          * @param {String} selector The CSS selector
7217          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7218          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7219          */
7220         down : function(selector, returnDom){
7221             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7222             return returnDom ? n : Roo.get(n);
7223         },
7224
7225         /**
7226          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7227          * @param {String} group The group the DD object is member of
7228          * @param {Object} config The DD config object
7229          * @param {Object} overrides An object containing methods to override/implement on the DD object
7230          * @return {Roo.dd.DD} The DD object
7231          */
7232         initDD : function(group, config, overrides){
7233             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7234             return Roo.apply(dd, overrides);
7235         },
7236
7237         /**
7238          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7239          * @param {String} group The group the DDProxy object is member of
7240          * @param {Object} config The DDProxy config object
7241          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7242          * @return {Roo.dd.DDProxy} The DDProxy object
7243          */
7244         initDDProxy : function(group, config, overrides){
7245             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7246             return Roo.apply(dd, overrides);
7247         },
7248
7249         /**
7250          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7251          * @param {String} group The group the DDTarget object is member of
7252          * @param {Object} config The DDTarget config object
7253          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7254          * @return {Roo.dd.DDTarget} The DDTarget object
7255          */
7256         initDDTarget : function(group, config, overrides){
7257             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7258             return Roo.apply(dd, overrides);
7259         },
7260
7261         /**
7262          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7263          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7264          * @param {Boolean} visible Whether the element is visible
7265          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7266          * @return {Roo.Element} this
7267          */
7268          setVisible : function(visible, animate){
7269             if(!animate || !A){
7270                 if(this.visibilityMode == El.DISPLAY){
7271                     this.setDisplayed(visible);
7272                 }else{
7273                     this.fixDisplay();
7274                     this.dom.style.visibility = visible ? "visible" : "hidden";
7275                 }
7276             }else{
7277                 // closure for composites
7278                 var dom = this.dom;
7279                 var visMode = this.visibilityMode;
7280                 if(visible){
7281                     this.setOpacity(.01);
7282                     this.setVisible(true);
7283                 }
7284                 this.anim({opacity: { to: (visible?1:0) }},
7285                       this.preanim(arguments, 1),
7286                       null, .35, 'easeIn', function(){
7287                          if(!visible){
7288                              if(visMode == El.DISPLAY){
7289                                  dom.style.display = "none";
7290                              }else{
7291                                  dom.style.visibility = "hidden";
7292                              }
7293                              Roo.get(dom).setOpacity(1);
7294                          }
7295                      });
7296             }
7297             return this;
7298         },
7299
7300         /**
7301          * Returns true if display is not "none"
7302          * @return {Boolean}
7303          */
7304         isDisplayed : function() {
7305             return this.getStyle("display") != "none";
7306         },
7307
7308         /**
7309          * Toggles the element's visibility or display, depending on visibility mode.
7310          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7311          * @return {Roo.Element} this
7312          */
7313         toggle : function(animate){
7314             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7315             return this;
7316         },
7317
7318         /**
7319          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7320          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7321          * @return {Roo.Element} this
7322          */
7323         setDisplayed : function(value) {
7324             if(typeof value == "boolean"){
7325                value = value ? this.originalDisplay : "none";
7326             }
7327             this.setStyle("display", value);
7328             return this;
7329         },
7330
7331         /**
7332          * Tries to focus the element. Any exceptions are caught and ignored.
7333          * @return {Roo.Element} this
7334          */
7335         focus : function() {
7336             try{
7337                 this.dom.focus();
7338             }catch(e){}
7339             return this;
7340         },
7341
7342         /**
7343          * Tries to blur the element. Any exceptions are caught and ignored.
7344          * @return {Roo.Element} this
7345          */
7346         blur : function() {
7347             try{
7348                 this.dom.blur();
7349             }catch(e){}
7350             return this;
7351         },
7352
7353         /**
7354          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7355          * @param {String/Array} className The CSS class to add, or an array of classes
7356          * @return {Roo.Element} this
7357          */
7358         addClass : function(className){
7359             if(className instanceof Array){
7360                 for(var i = 0, len = className.length; i < len; i++) {
7361                     this.addClass(className[i]);
7362                 }
7363             }else{
7364                 if(className && !this.hasClass(className)){
7365                     this.dom.className = this.dom.className + " " + className;
7366                 }
7367             }
7368             return this;
7369         },
7370
7371         /**
7372          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7373          * @param {String/Array} className The CSS class to add, or an array of classes
7374          * @return {Roo.Element} this
7375          */
7376         radioClass : function(className){
7377             var siblings = this.dom.parentNode.childNodes;
7378             for(var i = 0; i < siblings.length; i++) {
7379                 var s = siblings[i];
7380                 if(s.nodeType == 1){
7381                     Roo.get(s).removeClass(className);
7382                 }
7383             }
7384             this.addClass(className);
7385             return this;
7386         },
7387
7388         /**
7389          * Removes one or more CSS classes from the element.
7390          * @param {String/Array} className The CSS class to remove, or an array of classes
7391          * @return {Roo.Element} this
7392          */
7393         removeClass : function(className){
7394             if(!className || !this.dom.className){
7395                 return this;
7396             }
7397             if(className instanceof Array){
7398                 for(var i = 0, len = className.length; i < len; i++) {
7399                     this.removeClass(className[i]);
7400                 }
7401             }else{
7402                 if(this.hasClass(className)){
7403                     var re = this.classReCache[className];
7404                     if (!re) {
7405                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7406                        this.classReCache[className] = re;
7407                     }
7408                     this.dom.className =
7409                         this.dom.className.replace(re, " ");
7410                 }
7411             }
7412             return this;
7413         },
7414
7415         // private
7416         classReCache: {},
7417
7418         /**
7419          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7420          * @param {String} className The CSS class to toggle
7421          * @return {Roo.Element} this
7422          */
7423         toggleClass : function(className){
7424             if(this.hasClass(className)){
7425                 this.removeClass(className);
7426             }else{
7427                 this.addClass(className);
7428             }
7429             return this;
7430         },
7431
7432         /**
7433          * Checks if the specified CSS class exists on this element's DOM node.
7434          * @param {String} className The CSS class to check for
7435          * @return {Boolean} True if the class exists, else false
7436          */
7437         hasClass : function(className){
7438             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7439         },
7440
7441         /**
7442          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7443          * @param {String} oldClassName The CSS class to replace
7444          * @param {String} newClassName The replacement CSS class
7445          * @return {Roo.Element} this
7446          */
7447         replaceClass : function(oldClassName, newClassName){
7448             this.removeClass(oldClassName);
7449             this.addClass(newClassName);
7450             return this;
7451         },
7452
7453         /**
7454          * Returns an object with properties matching the styles requested.
7455          * For example, el.getStyles('color', 'font-size', 'width') might return
7456          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7457          * @param {String} style1 A style name
7458          * @param {String} style2 A style name
7459          * @param {String} etc.
7460          * @return {Object} The style object
7461          */
7462         getStyles : function(){
7463             var a = arguments, len = a.length, r = {};
7464             for(var i = 0; i < len; i++){
7465                 r[a[i]] = this.getStyle(a[i]);
7466             }
7467             return r;
7468         },
7469
7470         /**
7471          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7472          * @param {String} property The style property whose value is returned.
7473          * @return {String} The current value of the style property for this element.
7474          */
7475         getStyle : function(){
7476             return view && view.getComputedStyle ?
7477                 function(prop){
7478                     var el = this.dom, v, cs, camel;
7479                     if(prop == 'float'){
7480                         prop = "cssFloat";
7481                     }
7482                     if(el.style && (v = el.style[prop])){
7483                         return v;
7484                     }
7485                     if(cs = view.getComputedStyle(el, "")){
7486                         if(!(camel = propCache[prop])){
7487                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7488                         }
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 } :
7493                 function(prop){
7494                     var el = this.dom, v, cs, camel;
7495                     if(prop == 'opacity'){
7496                         if(typeof el.style.filter == 'string'){
7497                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7498                             if(m){
7499                                 var fv = parseFloat(m[1]);
7500                                 if(!isNaN(fv)){
7501                                     return fv ? fv / 100 : 0;
7502                                 }
7503                             }
7504                         }
7505                         return 1;
7506                     }else if(prop == 'float'){
7507                         prop = "styleFloat";
7508                     }
7509                     if(!(camel = propCache[prop])){
7510                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7511                     }
7512                     if(v = el.style[camel]){
7513                         return v;
7514                     }
7515                     if(cs = el.currentStyle){
7516                         return cs[camel];
7517                     }
7518                     return null;
7519                 };
7520         }(),
7521
7522         /**
7523          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7524          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7525          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7526          * @return {Roo.Element} this
7527          */
7528         setStyle : function(prop, value){
7529             if(typeof prop == "string"){
7530                 
7531                 if (prop == 'float') {
7532                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7533                     return this;
7534                 }
7535                 
7536                 var camel;
7537                 if(!(camel = propCache[prop])){
7538                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7539                 }
7540                 
7541                 if(camel == 'opacity') {
7542                     this.setOpacity(value);
7543                 }else{
7544                     this.dom.style[camel] = value;
7545                 }
7546             }else{
7547                 for(var style in prop){
7548                     if(typeof prop[style] != "function"){
7549                        this.setStyle(style, prop[style]);
7550                     }
7551                 }
7552             }
7553             return this;
7554         },
7555
7556         /**
7557          * More flexible version of {@link #setStyle} for setting style properties.
7558          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7559          * a function which returns such a specification.
7560          * @return {Roo.Element} this
7561          */
7562         applyStyles : function(style){
7563             Roo.DomHelper.applyStyles(this.dom, style);
7564             return this;
7565         },
7566
7567         /**
7568           * 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).
7569           * @return {Number} The X position of the element
7570           */
7571         getX : function(){
7572             return D.getX(this.dom);
7573         },
7574
7575         /**
7576           * 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).
7577           * @return {Number} The Y position of the element
7578           */
7579         getY : function(){
7580             return D.getY(this.dom);
7581         },
7582
7583         /**
7584           * 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).
7585           * @return {Array} The XY position of the element
7586           */
7587         getXY : function(){
7588             return D.getXY(this.dom);
7589         },
7590
7591         /**
7592          * 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).
7593          * @param {Number} The X position of the element
7594          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7595          * @return {Roo.Element} this
7596          */
7597         setX : function(x, animate){
7598             if(!animate || !A){
7599                 D.setX(this.dom, x);
7600             }else{
7601                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7602             }
7603             return this;
7604         },
7605
7606         /**
7607          * 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).
7608          * @param {Number} The Y position of the element
7609          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7610          * @return {Roo.Element} this
7611          */
7612         setY : function(y, animate){
7613             if(!animate || !A){
7614                 D.setY(this.dom, y);
7615             }else{
7616                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7617             }
7618             return this;
7619         },
7620
7621         /**
7622          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7623          * @param {String} left The left CSS property value
7624          * @return {Roo.Element} this
7625          */
7626         setLeft : function(left){
7627             this.setStyle("left", this.addUnits(left));
7628             return this;
7629         },
7630
7631         /**
7632          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7633          * @param {String} top The top CSS property value
7634          * @return {Roo.Element} this
7635          */
7636         setTop : function(top){
7637             this.setStyle("top", this.addUnits(top));
7638             return this;
7639         },
7640
7641         /**
7642          * Sets the element's CSS right style.
7643          * @param {String} right The right CSS property value
7644          * @return {Roo.Element} this
7645          */
7646         setRight : function(right){
7647             this.setStyle("right", this.addUnits(right));
7648             return this;
7649         },
7650
7651         /**
7652          * Sets the element's CSS bottom style.
7653          * @param {String} bottom The bottom CSS property value
7654          * @return {Roo.Element} this
7655          */
7656         setBottom : function(bottom){
7657             this.setStyle("bottom", this.addUnits(bottom));
7658             return this;
7659         },
7660
7661         /**
7662          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7663          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7664          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7665          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7666          * @return {Roo.Element} this
7667          */
7668         setXY : function(pos, animate){
7669             if(!animate || !A){
7670                 D.setXY(this.dom, pos);
7671             }else{
7672                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7673             }
7674             return this;
7675         },
7676
7677         /**
7678          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7679          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7680          * @param {Number} x X value for new position (coordinates are page-based)
7681          * @param {Number} y Y value for new position (coordinates are page-based)
7682          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7683          * @return {Roo.Element} this
7684          */
7685         setLocation : function(x, y, animate){
7686             this.setXY([x, y], this.preanim(arguments, 2));
7687             return this;
7688         },
7689
7690         /**
7691          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7692          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7693          * @param {Number} x X value for new position (coordinates are page-based)
7694          * @param {Number} y Y value for new position (coordinates are page-based)
7695          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7696          * @return {Roo.Element} this
7697          */
7698         moveTo : function(x, y, animate){
7699             this.setXY([x, y], this.preanim(arguments, 2));
7700             return this;
7701         },
7702
7703         /**
7704          * Returns the region of the given element.
7705          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7706          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7707          */
7708         getRegion : function(){
7709             return D.getRegion(this.dom);
7710         },
7711
7712         /**
7713          * Returns the offset height of the element
7714          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7715          * @return {Number} The element's height
7716          */
7717         getHeight : function(contentHeight){
7718             var h = this.dom.offsetHeight || 0;
7719             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7720         },
7721
7722         /**
7723          * Returns the offset width of the element
7724          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7725          * @return {Number} The element's width
7726          */
7727         getWidth : function(contentWidth){
7728             var w = this.dom.offsetWidth || 0;
7729             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7730         },
7731
7732         /**
7733          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7734          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7735          * if a height has not been set using CSS.
7736          * @return {Number}
7737          */
7738         getComputedHeight : function(){
7739             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7740             if(!h){
7741                 h = parseInt(this.getStyle('height'), 10) || 0;
7742                 if(!this.isBorderBox()){
7743                     h += this.getFrameWidth('tb');
7744                 }
7745             }
7746             return h;
7747         },
7748
7749         /**
7750          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7751          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7752          * if a width has not been set using CSS.
7753          * @return {Number}
7754          */
7755         getComputedWidth : function(){
7756             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7757             if(!w){
7758                 w = parseInt(this.getStyle('width'), 10) || 0;
7759                 if(!this.isBorderBox()){
7760                     w += this.getFrameWidth('lr');
7761                 }
7762             }
7763             return w;
7764         },
7765
7766         /**
7767          * Returns the size of the element.
7768          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7769          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7770          */
7771         getSize : function(contentSize){
7772             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7773         },
7774
7775         /**
7776          * Returns the width and height of the viewport.
7777          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7778          */
7779         getViewSize : function(){
7780             var d = this.dom, doc = document, aw = 0, ah = 0;
7781             if(d == doc || d == doc.body){
7782                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7783             }else{
7784                 return {
7785                     width : d.clientWidth,
7786                     height: d.clientHeight
7787                 };
7788             }
7789         },
7790
7791         /**
7792          * Returns the value of the "value" attribute
7793          * @param {Boolean} asNumber true to parse the value as a number
7794          * @return {String/Number}
7795          */
7796         getValue : function(asNumber){
7797             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7798         },
7799
7800         // private
7801         adjustWidth : function(width){
7802             if(typeof width == "number"){
7803                 if(this.autoBoxAdjust && !this.isBorderBox()){
7804                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7805                 }
7806                 if(width < 0){
7807                     width = 0;
7808                 }
7809             }
7810             return width;
7811         },
7812
7813         // private
7814         adjustHeight : function(height){
7815             if(typeof height == "number"){
7816                if(this.autoBoxAdjust && !this.isBorderBox()){
7817                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7818                }
7819                if(height < 0){
7820                    height = 0;
7821                }
7822             }
7823             return height;
7824         },
7825
7826         /**
7827          * Set the width of the element
7828          * @param {Number} width The new width
7829          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7830          * @return {Roo.Element} this
7831          */
7832         setWidth : function(width, animate){
7833             width = this.adjustWidth(width);
7834             if(!animate || !A){
7835                 this.dom.style.width = this.addUnits(width);
7836             }else{
7837                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7838             }
7839             return this;
7840         },
7841
7842         /**
7843          * Set the height of the element
7844          * @param {Number} height The new height
7845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7846          * @return {Roo.Element} this
7847          */
7848          setHeight : function(height, animate){
7849             height = this.adjustHeight(height);
7850             if(!animate || !A){
7851                 this.dom.style.height = this.addUnits(height);
7852             }else{
7853                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7854             }
7855             return this;
7856         },
7857
7858         /**
7859          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7860          * @param {Number} width The new width
7861          * @param {Number} height The new height
7862          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7863          * @return {Roo.Element} this
7864          */
7865          setSize : function(width, height, animate){
7866             if(typeof width == "object"){ // in case of object from getSize()
7867                 height = width.height; width = width.width;
7868             }
7869             width = this.adjustWidth(width); height = this.adjustHeight(height);
7870             if(!animate || !A){
7871                 this.dom.style.width = this.addUnits(width);
7872                 this.dom.style.height = this.addUnits(height);
7873             }else{
7874                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7875             }
7876             return this;
7877         },
7878
7879         /**
7880          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7881          * @param {Number} x X value for new position (coordinates are page-based)
7882          * @param {Number} y Y value for new position (coordinates are page-based)
7883          * @param {Number} width The new width
7884          * @param {Number} height The new height
7885          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7886          * @return {Roo.Element} this
7887          */
7888         setBounds : function(x, y, width, height, animate){
7889             if(!animate || !A){
7890                 this.setSize(width, height);
7891                 this.setLocation(x, y);
7892             }else{
7893                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7894                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7895                               this.preanim(arguments, 4), 'motion');
7896             }
7897             return this;
7898         },
7899
7900         /**
7901          * 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.
7902          * @param {Roo.lib.Region} region The region to fill
7903          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7904          * @return {Roo.Element} this
7905          */
7906         setRegion : function(region, animate){
7907             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7908             return this;
7909         },
7910
7911         /**
7912          * Appends an event handler
7913          *
7914          * @param {String}   eventName     The type of event to append
7915          * @param {Function} fn        The method the event invokes
7916          * @param {Object} scope       (optional) The scope (this object) of the fn
7917          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7918          */
7919         addListener : function(eventName, fn, scope, options){
7920             if (this.dom) {
7921                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7922             }
7923         },
7924
7925         /**
7926          * Removes an event handler from this element
7927          * @param {String} eventName the type of event to remove
7928          * @param {Function} fn the method the event invokes
7929          * @return {Roo.Element} this
7930          */
7931         removeListener : function(eventName, fn){
7932             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7933             return this;
7934         },
7935
7936         /**
7937          * Removes all previous added listeners from this element
7938          * @return {Roo.Element} this
7939          */
7940         removeAllListeners : function(){
7941             E.purgeElement(this.dom);
7942             return this;
7943         },
7944
7945         relayEvent : function(eventName, observable){
7946             this.on(eventName, function(e){
7947                 observable.fireEvent(eventName, e);
7948             });
7949         },
7950
7951         /**
7952          * Set the opacity of the element
7953          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7954          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7955          * @return {Roo.Element} this
7956          */
7957          setOpacity : function(opacity, animate){
7958             if(!animate || !A){
7959                 var s = this.dom.style;
7960                 if(Roo.isIE){
7961                     s.zoom = 1;
7962                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7963                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7964                 }else{
7965                     s.opacity = opacity;
7966                 }
7967             }else{
7968                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Gets the left X coordinate
7975          * @param {Boolean} local True to get the local css position instead of page coordinate
7976          * @return {Number}
7977          */
7978         getLeft : function(local){
7979             if(!local){
7980                 return this.getX();
7981             }else{
7982                 return parseInt(this.getStyle("left"), 10) || 0;
7983             }
7984         },
7985
7986         /**
7987          * Gets the right X coordinate of the element (element X position + element width)
7988          * @param {Boolean} local True to get the local css position instead of page coordinate
7989          * @return {Number}
7990          */
7991         getRight : function(local){
7992             if(!local){
7993                 return this.getX() + this.getWidth();
7994             }else{
7995                 return (this.getLeft(true) + this.getWidth()) || 0;
7996             }
7997         },
7998
7999         /**
8000          * Gets the top Y coordinate
8001          * @param {Boolean} local True to get the local css position instead of page coordinate
8002          * @return {Number}
8003          */
8004         getTop : function(local) {
8005             if(!local){
8006                 return this.getY();
8007             }else{
8008                 return parseInt(this.getStyle("top"), 10) || 0;
8009             }
8010         },
8011
8012         /**
8013          * Gets the bottom Y coordinate of the element (element Y position + element height)
8014          * @param {Boolean} local True to get the local css position instead of page coordinate
8015          * @return {Number}
8016          */
8017         getBottom : function(local){
8018             if(!local){
8019                 return this.getY() + this.getHeight();
8020             }else{
8021                 return (this.getTop(true) + this.getHeight()) || 0;
8022             }
8023         },
8024
8025         /**
8026         * Initializes positioning on this element. If a desired position is not passed, it will make the
8027         * the element positioned relative IF it is not already positioned.
8028         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8029         * @param {Number} zIndex (optional) The zIndex to apply
8030         * @param {Number} x (optional) Set the page X position
8031         * @param {Number} y (optional) Set the page Y position
8032         */
8033         position : function(pos, zIndex, x, y){
8034             if(!pos){
8035                if(this.getStyle('position') == 'static'){
8036                    this.setStyle('position', 'relative');
8037                }
8038             }else{
8039                 this.setStyle("position", pos);
8040             }
8041             if(zIndex){
8042                 this.setStyle("z-index", zIndex);
8043             }
8044             if(x !== undefined && y !== undefined){
8045                 this.setXY([x, y]);
8046             }else if(x !== undefined){
8047                 this.setX(x);
8048             }else if(y !== undefined){
8049                 this.setY(y);
8050             }
8051         },
8052
8053         /**
8054         * Clear positioning back to the default when the document was loaded
8055         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8056         * @return {Roo.Element} this
8057          */
8058         clearPositioning : function(value){
8059             value = value ||'';
8060             this.setStyle({
8061                 "left": value,
8062                 "right": value,
8063                 "top": value,
8064                 "bottom": value,
8065                 "z-index": "",
8066                 "position" : "static"
8067             });
8068             return this;
8069         },
8070
8071         /**
8072         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8073         * snapshot before performing an update and then restoring the element.
8074         * @return {Object}
8075         */
8076         getPositioning : function(){
8077             var l = this.getStyle("left");
8078             var t = this.getStyle("top");
8079             return {
8080                 "position" : this.getStyle("position"),
8081                 "left" : l,
8082                 "right" : l ? "" : this.getStyle("right"),
8083                 "top" : t,
8084                 "bottom" : t ? "" : this.getStyle("bottom"),
8085                 "z-index" : this.getStyle("z-index")
8086             };
8087         },
8088
8089         /**
8090          * Gets the width of the border(s) for the specified side(s)
8091          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8092          * passing lr would get the border (l)eft width + the border (r)ight width.
8093          * @return {Number} The width of the sides passed added together
8094          */
8095         getBorderWidth : function(side){
8096             return this.addStyles(side, El.borders);
8097         },
8098
8099         /**
8100          * Gets the width of the padding(s) for the specified side(s)
8101          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8102          * passing lr would get the padding (l)eft + the padding (r)ight.
8103          * @return {Number} The padding of the sides passed added together
8104          */
8105         getPadding : function(side){
8106             return this.addStyles(side, El.paddings);
8107         },
8108
8109         /**
8110         * Set positioning with an object returned by getPositioning().
8111         * @param {Object} posCfg
8112         * @return {Roo.Element} this
8113          */
8114         setPositioning : function(pc){
8115             this.applyStyles(pc);
8116             if(pc.right == "auto"){
8117                 this.dom.style.right = "";
8118             }
8119             if(pc.bottom == "auto"){
8120                 this.dom.style.bottom = "";
8121             }
8122             return this;
8123         },
8124
8125         // private
8126         fixDisplay : function(){
8127             if(this.getStyle("display") == "none"){
8128                 this.setStyle("visibility", "hidden");
8129                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8130                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8131                     this.setStyle("display", "block");
8132                 }
8133             }
8134         },
8135
8136         /**
8137          * Quick set left and top adding default units
8138          * @param {String} left The left CSS property value
8139          * @param {String} top The top CSS property value
8140          * @return {Roo.Element} this
8141          */
8142          setLeftTop : function(left, top){
8143             this.dom.style.left = this.addUnits(left);
8144             this.dom.style.top = this.addUnits(top);
8145             return this;
8146         },
8147
8148         /**
8149          * Move this element relative to its current position.
8150          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8151          * @param {Number} distance How far to move the element in pixels
8152          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8153          * @return {Roo.Element} this
8154          */
8155          move : function(direction, distance, animate){
8156             var xy = this.getXY();
8157             direction = direction.toLowerCase();
8158             switch(direction){
8159                 case "l":
8160                 case "left":
8161                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8162                     break;
8163                case "r":
8164                case "right":
8165                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8166                     break;
8167                case "t":
8168                case "top":
8169                case "up":
8170                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8171                     break;
8172                case "b":
8173                case "bottom":
8174                case "down":
8175                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8176                     break;
8177             }
8178             return this;
8179         },
8180
8181         /**
8182          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8183          * @return {Roo.Element} this
8184          */
8185         clip : function(){
8186             if(!this.isClipped){
8187                this.isClipped = true;
8188                this.originalClip = {
8189                    "o": this.getStyle("overflow"),
8190                    "x": this.getStyle("overflow-x"),
8191                    "y": this.getStyle("overflow-y")
8192                };
8193                this.setStyle("overflow", "hidden");
8194                this.setStyle("overflow-x", "hidden");
8195                this.setStyle("overflow-y", "hidden");
8196             }
8197             return this;
8198         },
8199
8200         /**
8201          *  Return clipping (overflow) to original clipping before clip() was called
8202          * @return {Roo.Element} this
8203          */
8204         unclip : function(){
8205             if(this.isClipped){
8206                 this.isClipped = false;
8207                 var o = this.originalClip;
8208                 if(o.o){this.setStyle("overflow", o.o);}
8209                 if(o.x){this.setStyle("overflow-x", o.x);}
8210                 if(o.y){this.setStyle("overflow-y", o.y);}
8211             }
8212             return this;
8213         },
8214
8215
8216         /**
8217          * Gets the x,y coordinates specified by the anchor position on the element.
8218          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8219          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8220          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8221          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8222          * @return {Array} [x, y] An array containing the element's x and y coordinates
8223          */
8224         getAnchorXY : function(anchor, local, s){
8225             //Passing a different size is useful for pre-calculating anchors,
8226             //especially for anchored animations that change the el size.
8227
8228             var w, h, vp = false;
8229             if(!s){
8230                 var d = this.dom;
8231                 if(d == document.body || d == document){
8232                     vp = true;
8233                     w = D.getViewWidth(); h = D.getViewHeight();
8234                 }else{
8235                     w = this.getWidth(); h = this.getHeight();
8236                 }
8237             }else{
8238                 w = s.width;  h = s.height;
8239             }
8240             var x = 0, y = 0, r = Math.round;
8241             switch((anchor || "tl").toLowerCase()){
8242                 case "c":
8243                     x = r(w*.5);
8244                     y = r(h*.5);
8245                 break;
8246                 case "t":
8247                     x = r(w*.5);
8248                     y = 0;
8249                 break;
8250                 case "l":
8251                     x = 0;
8252                     y = r(h*.5);
8253                 break;
8254                 case "r":
8255                     x = w;
8256                     y = r(h*.5);
8257                 break;
8258                 case "b":
8259                     x = r(w*.5);
8260                     y = h;
8261                 break;
8262                 case "tl":
8263                     x = 0;
8264                     y = 0;
8265                 break;
8266                 case "bl":
8267                     x = 0;
8268                     y = h;
8269                 break;
8270                 case "br":
8271                     x = w;
8272                     y = h;
8273                 break;
8274                 case "tr":
8275                     x = w;
8276                     y = 0;
8277                 break;
8278             }
8279             if(local === true){
8280                 return [x, y];
8281             }
8282             if(vp){
8283                 var sc = this.getScroll();
8284                 return [x + sc.left, y + sc.top];
8285             }
8286             //Add the element's offset xy
8287             var o = this.getXY();
8288             return [x+o[0], y+o[1]];
8289         },
8290
8291         /**
8292          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8293          * supported position values.
8294          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8295          * @param {String} position The position to align to.
8296          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8297          * @return {Array} [x, y]
8298          */
8299         getAlignToXY : function(el, p, o){
8300             el = Roo.get(el);
8301             var d = this.dom;
8302             if(!el.dom){
8303                 throw "Element.alignTo with an element that doesn't exist";
8304             }
8305             var c = false; //constrain to viewport
8306             var p1 = "", p2 = "";
8307             o = o || [0,0];
8308
8309             if(!p){
8310                 p = "tl-bl";
8311             }else if(p == "?"){
8312                 p = "tl-bl?";
8313             }else if(p.indexOf("-") == -1){
8314                 p = "tl-" + p;
8315             }
8316             p = p.toLowerCase();
8317             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8318             if(!m){
8319                throw "Element.alignTo with an invalid alignment " + p;
8320             }
8321             p1 = m[1]; p2 = m[2]; c = !!m[3];
8322
8323             //Subtract the aligned el's internal xy from the target's offset xy
8324             //plus custom offset to get the aligned el's new offset xy
8325             var a1 = this.getAnchorXY(p1, true);
8326             var a2 = el.getAnchorXY(p2, false);
8327             var x = a2[0] - a1[0] + o[0];
8328             var y = a2[1] - a1[1] + o[1];
8329             if(c){
8330                 //constrain the aligned el to viewport if necessary
8331                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8332                 // 5px of margin for ie
8333                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8334
8335                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8336                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8337                 //otherwise swap the aligned el to the opposite border of the target.
8338                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8339                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8340                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8341                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8342
8343                var doc = document;
8344                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8345                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8346
8347                if((x+w) > dw + scrollX){
8348                     x = swapX ? r.left-w : dw+scrollX-w;
8349                 }
8350                if(x < scrollX){
8351                    x = swapX ? r.right : scrollX;
8352                }
8353                if((y+h) > dh + scrollY){
8354                     y = swapY ? r.top-h : dh+scrollY-h;
8355                 }
8356                if (y < scrollY){
8357                    y = swapY ? r.bottom : scrollY;
8358                }
8359             }
8360             return [x,y];
8361         },
8362
8363         // private
8364         getConstrainToXY : function(){
8365             var os = {top:0, left:0, bottom:0, right: 0};
8366
8367             return function(el, local, offsets, proposedXY){
8368                 el = Roo.get(el);
8369                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8370
8371                 var vw, vh, vx = 0, vy = 0;
8372                 if(el.dom == document.body || el.dom == document){
8373                     vw = Roo.lib.Dom.getViewWidth();
8374                     vh = Roo.lib.Dom.getViewHeight();
8375                 }else{
8376                     vw = el.dom.clientWidth;
8377                     vh = el.dom.clientHeight;
8378                     if(!local){
8379                         var vxy = el.getXY();
8380                         vx = vxy[0];
8381                         vy = vxy[1];
8382                     }
8383                 }
8384
8385                 var s = el.getScroll();
8386
8387                 vx += offsets.left + s.left;
8388                 vy += offsets.top + s.top;
8389
8390                 vw -= offsets.right;
8391                 vh -= offsets.bottom;
8392
8393                 var vr = vx+vw;
8394                 var vb = vy+vh;
8395
8396                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8397                 var x = xy[0], y = xy[1];
8398                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8399
8400                 // only move it if it needs it
8401                 var moved = false;
8402
8403                 // first validate right/bottom
8404                 if((x + w) > vr){
8405                     x = vr - w;
8406                     moved = true;
8407                 }
8408                 if((y + h) > vb){
8409                     y = vb - h;
8410                     moved = true;
8411                 }
8412                 // then make sure top/left isn't negative
8413                 if(x < vx){
8414                     x = vx;
8415                     moved = true;
8416                 }
8417                 if(y < vy){
8418                     y = vy;
8419                     moved = true;
8420                 }
8421                 return moved ? [x, y] : false;
8422             };
8423         }(),
8424
8425         // private
8426         adjustForConstraints : function(xy, parent, offsets){
8427             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8428         },
8429
8430         /**
8431          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8432          * document it aligns it to the viewport.
8433          * The position parameter is optional, and can be specified in any one of the following formats:
8434          * <ul>
8435          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8436          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8437          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8438          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8439          *   <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
8440          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8441          * </ul>
8442          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8443          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8444          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8445          * that specified in order to enforce the viewport constraints.
8446          * Following are all of the supported anchor positions:
8447     <pre>
8448     Value  Description
8449     -----  -----------------------------
8450     tl     The top left corner (default)
8451     t      The center of the top edge
8452     tr     The top right corner
8453     l      The center of the left edge
8454     c      In the center of the element
8455     r      The center of the right edge
8456     bl     The bottom left corner
8457     b      The center of the bottom edge
8458     br     The bottom right corner
8459     </pre>
8460     Example Usage:
8461     <pre><code>
8462     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8463     el.alignTo("other-el");
8464
8465     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8466     el.alignTo("other-el", "tr?");
8467
8468     // align the bottom right corner of el with the center left edge of other-el
8469     el.alignTo("other-el", "br-l?");
8470
8471     // align the center of el with the bottom left corner of other-el and
8472     // adjust the x position by -6 pixels (and the y position by 0)
8473     el.alignTo("other-el", "c-bl", [-6, 0]);
8474     </code></pre>
8475          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8476          * @param {String} position The position to align to.
8477          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8478          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8479          * @return {Roo.Element} this
8480          */
8481         alignTo : function(element, position, offsets, animate){
8482             var xy = this.getAlignToXY(element, position, offsets);
8483             this.setXY(xy, this.preanim(arguments, 3));
8484             return this;
8485         },
8486
8487         /**
8488          * Anchors an element to another element and realigns it when the window is resized.
8489          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8490          * @param {String} position The position to align to.
8491          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8492          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8493          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8494          * is a number, it is used as the buffer delay (defaults to 50ms).
8495          * @param {Function} callback The function to call after the animation finishes
8496          * @return {Roo.Element} this
8497          */
8498         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8499             var action = function(){
8500                 this.alignTo(el, alignment, offsets, animate);
8501                 Roo.callback(callback, this);
8502             };
8503             Roo.EventManager.onWindowResize(action, this);
8504             var tm = typeof monitorScroll;
8505             if(tm != 'undefined'){
8506                 Roo.EventManager.on(window, 'scroll', action, this,
8507                     {buffer: tm == 'number' ? monitorScroll : 50});
8508             }
8509             action.call(this); // align immediately
8510             return this;
8511         },
8512         /**
8513          * Clears any opacity settings from this element. Required in some cases for IE.
8514          * @return {Roo.Element} this
8515          */
8516         clearOpacity : function(){
8517             if (window.ActiveXObject) {
8518                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8519                     this.dom.style.filter = "";
8520                 }
8521             } else {
8522                 this.dom.style.opacity = "";
8523                 this.dom.style["-moz-opacity"] = "";
8524                 this.dom.style["-khtml-opacity"] = "";
8525             }
8526             return this;
8527         },
8528
8529         /**
8530          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8531          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8532          * @return {Roo.Element} this
8533          */
8534         hide : function(animate){
8535             this.setVisible(false, this.preanim(arguments, 0));
8536             return this;
8537         },
8538
8539         /**
8540         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8541         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8542          * @return {Roo.Element} this
8543          */
8544         show : function(animate){
8545             this.setVisible(true, this.preanim(arguments, 0));
8546             return this;
8547         },
8548
8549         /**
8550          * @private Test if size has a unit, otherwise appends the default
8551          */
8552         addUnits : function(size){
8553             return Roo.Element.addUnits(size, this.defaultUnit);
8554         },
8555
8556         /**
8557          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8558          * @return {Roo.Element} this
8559          */
8560         beginMeasure : function(){
8561             var el = this.dom;
8562             if(el.offsetWidth || el.offsetHeight){
8563                 return this; // offsets work already
8564             }
8565             var changed = [];
8566             var p = this.dom, b = document.body; // start with this element
8567             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8568                 var pe = Roo.get(p);
8569                 if(pe.getStyle('display') == 'none'){
8570                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8571                     p.style.visibility = "hidden";
8572                     p.style.display = "block";
8573                 }
8574                 p = p.parentNode;
8575             }
8576             this._measureChanged = changed;
8577             return this;
8578
8579         },
8580
8581         /**
8582          * Restores displays to before beginMeasure was called
8583          * @return {Roo.Element} this
8584          */
8585         endMeasure : function(){
8586             var changed = this._measureChanged;
8587             if(changed){
8588                 for(var i = 0, len = changed.length; i < len; i++) {
8589                     var r = changed[i];
8590                     r.el.style.visibility = r.visibility;
8591                     r.el.style.display = "none";
8592                 }
8593                 this._measureChanged = null;
8594             }
8595             return this;
8596         },
8597
8598         /**
8599         * Update the innerHTML of this element, optionally searching for and processing scripts
8600         * @param {String} html The new HTML
8601         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8602         * @param {Function} callback For async script loading you can be noticed when the update completes
8603         * @return {Roo.Element} this
8604          */
8605         update : function(html, loadScripts, callback){
8606             if(typeof html == "undefined"){
8607                 html = "";
8608             }
8609             if(loadScripts !== true){
8610                 this.dom.innerHTML = html;
8611                 if(typeof callback == "function"){
8612                     callback();
8613                 }
8614                 return this;
8615             }
8616             var id = Roo.id();
8617             var dom = this.dom;
8618
8619             html += '<span id="' + id + '"></span>';
8620
8621             E.onAvailable(id, function(){
8622                 var hd = document.getElementsByTagName("head")[0];
8623                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8624                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8625                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8626
8627                 var match;
8628                 while(match = re.exec(html)){
8629                     var attrs = match[1];
8630                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8631                     if(srcMatch && srcMatch[2]){
8632                        var s = document.createElement("script");
8633                        s.src = srcMatch[2];
8634                        var typeMatch = attrs.match(typeRe);
8635                        if(typeMatch && typeMatch[2]){
8636                            s.type = typeMatch[2];
8637                        }
8638                        hd.appendChild(s);
8639                     }else if(match[2] && match[2].length > 0){
8640                         if(window.execScript) {
8641                            window.execScript(match[2]);
8642                         } else {
8643                             /**
8644                              * eval:var:id
8645                              * eval:var:dom
8646                              * eval:var:html
8647                              * 
8648                              */
8649                            window.eval(match[2]);
8650                         }
8651                     }
8652                 }
8653                 var el = document.getElementById(id);
8654                 if(el){el.parentNode.removeChild(el);}
8655                 if(typeof callback == "function"){
8656                     callback();
8657                 }
8658             });
8659             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8660             return this;
8661         },
8662
8663         /**
8664          * Direct access to the UpdateManager update() method (takes the same parameters).
8665          * @param {String/Function} url The url for this request or a function to call to get the url
8666          * @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}
8667          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8668          * @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.
8669          * @return {Roo.Element} this
8670          */
8671         load : function(){
8672             var um = this.getUpdateManager();
8673             um.update.apply(um, arguments);
8674             return this;
8675         },
8676
8677         /**
8678         * Gets this element's UpdateManager
8679         * @return {Roo.UpdateManager} The UpdateManager
8680         */
8681         getUpdateManager : function(){
8682             if(!this.updateManager){
8683                 this.updateManager = new Roo.UpdateManager(this);
8684             }
8685             return this.updateManager;
8686         },
8687
8688         /**
8689          * Disables text selection for this element (normalized across browsers)
8690          * @return {Roo.Element} this
8691          */
8692         unselectable : function(){
8693             this.dom.unselectable = "on";
8694             this.swallowEvent("selectstart", true);
8695             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8696             this.addClass("x-unselectable");
8697             return this;
8698         },
8699
8700         /**
8701         * Calculates the x, y to center this element on the screen
8702         * @return {Array} The x, y values [x, y]
8703         */
8704         getCenterXY : function(){
8705             return this.getAlignToXY(document, 'c-c');
8706         },
8707
8708         /**
8709         * Centers the Element in either the viewport, or another Element.
8710         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8711         */
8712         center : function(centerIn){
8713             this.alignTo(centerIn || document, 'c-c');
8714             return this;
8715         },
8716
8717         /**
8718          * Tests various css rules/browsers to determine if this element uses a border box
8719          * @return {Boolean}
8720          */
8721         isBorderBox : function(){
8722             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8723         },
8724
8725         /**
8726          * Return a box {x, y, width, height} that can be used to set another elements
8727          * size/location to match this element.
8728          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8729          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8730          * @return {Object} box An object in the format {x, y, width, height}
8731          */
8732         getBox : function(contentBox, local){
8733             var xy;
8734             if(!local){
8735                 xy = this.getXY();
8736             }else{
8737                 var left = parseInt(this.getStyle("left"), 10) || 0;
8738                 var top = parseInt(this.getStyle("top"), 10) || 0;
8739                 xy = [left, top];
8740             }
8741             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8742             if(!contentBox){
8743                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8744             }else{
8745                 var l = this.getBorderWidth("l")+this.getPadding("l");
8746                 var r = this.getBorderWidth("r")+this.getPadding("r");
8747                 var t = this.getBorderWidth("t")+this.getPadding("t");
8748                 var b = this.getBorderWidth("b")+this.getPadding("b");
8749                 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)};
8750             }
8751             bx.right = bx.x + bx.width;
8752             bx.bottom = bx.y + bx.height;
8753             return bx;
8754         },
8755
8756         /**
8757          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8758          for more information about the sides.
8759          * @param {String} sides
8760          * @return {Number}
8761          */
8762         getFrameWidth : function(sides, onlyContentBox){
8763             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8764         },
8765
8766         /**
8767          * 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.
8768          * @param {Object} box The box to fill {x, y, width, height}
8769          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8770          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8771          * @return {Roo.Element} this
8772          */
8773         setBox : function(box, adjust, animate){
8774             var w = box.width, h = box.height;
8775             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8776                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8777                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8778             }
8779             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8780             return this;
8781         },
8782
8783         /**
8784          * Forces the browser to repaint this element
8785          * @return {Roo.Element} this
8786          */
8787          repaint : function(){
8788             var dom = this.dom;
8789             this.addClass("x-repaint");
8790             setTimeout(function(){
8791                 Roo.get(dom).removeClass("x-repaint");
8792             }, 1);
8793             return this;
8794         },
8795
8796         /**
8797          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8798          * then it returns the calculated width of the sides (see getPadding)
8799          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8800          * @return {Object/Number}
8801          */
8802         getMargins : function(side){
8803             if(!side){
8804                 return {
8805                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8806                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8807                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8808                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8809                 };
8810             }else{
8811                 return this.addStyles(side, El.margins);
8812              }
8813         },
8814
8815         // private
8816         addStyles : function(sides, styles){
8817             var val = 0, v, w;
8818             for(var i = 0, len = sides.length; i < len; i++){
8819                 v = this.getStyle(styles[sides.charAt(i)]);
8820                 if(v){
8821                      w = parseInt(v, 10);
8822                      if(w){ val += w; }
8823                 }
8824             }
8825             return val;
8826         },
8827
8828         /**
8829          * Creates a proxy element of this element
8830          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8831          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8832          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8833          * @return {Roo.Element} The new proxy element
8834          */
8835         createProxy : function(config, renderTo, matchBox){
8836             if(renderTo){
8837                 renderTo = Roo.getDom(renderTo);
8838             }else{
8839                 renderTo = document.body;
8840             }
8841             config = typeof config == "object" ?
8842                 config : {tag : "div", cls: config};
8843             var proxy = Roo.DomHelper.append(renderTo, config, true);
8844             if(matchBox){
8845                proxy.setBox(this.getBox());
8846             }
8847             return proxy;
8848         },
8849
8850         /**
8851          * Puts a mask over this element to disable user interaction. Requires core.css.
8852          * This method can only be applied to elements which accept child nodes.
8853          * @param {String} msg (optional) A message to display in the mask
8854          * @param {String} msgCls (optional) A css class to apply to the msg element
8855          * @return {Element} The mask  element
8856          */
8857         mask : function(msg, msgCls){
8858             if(this.getStyle("position") == "static"){
8859                 this.setStyle("position", "relative");
8860             }
8861             if(!this._mask){
8862                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8863             }
8864             this.addClass("x-masked");
8865             this._mask.setDisplayed(true);
8866             if(typeof msg == 'string'){
8867                 if(!this._maskMsg){
8868                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8869                 }
8870                 var mm = this._maskMsg;
8871                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8872                 mm.dom.firstChild.innerHTML = msg;
8873                 mm.setDisplayed(true);
8874                 mm.center(this);
8875             }
8876             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8877                 this._mask.setHeight(this.getHeight());
8878             }
8879             return this._mask;
8880         },
8881
8882         /**
8883          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8884          * it is cached for reuse.
8885          */
8886         unmask : function(removeEl){
8887             if(this._mask){
8888                 if(removeEl === true){
8889                     this._mask.remove();
8890                     delete this._mask;
8891                     if(this._maskMsg){
8892                         this._maskMsg.remove();
8893                         delete this._maskMsg;
8894                     }
8895                 }else{
8896                     this._mask.setDisplayed(false);
8897                     if(this._maskMsg){
8898                         this._maskMsg.setDisplayed(false);
8899                     }
8900                 }
8901             }
8902             this.removeClass("x-masked");
8903         },
8904
8905         /**
8906          * Returns true if this element is masked
8907          * @return {Boolean}
8908          */
8909         isMasked : function(){
8910             return this._mask && this._mask.isVisible();
8911         },
8912
8913         /**
8914          * Creates an iframe shim for this element to keep selects and other windowed objects from
8915          * showing through.
8916          * @return {Roo.Element} The new shim element
8917          */
8918         createShim : function(){
8919             var el = document.createElement('iframe');
8920             el.frameBorder = 'no';
8921             el.className = 'roo-shim';
8922             if(Roo.isIE && Roo.isSecure){
8923                 el.src = Roo.SSL_SECURE_URL;
8924             }
8925             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8926             shim.autoBoxAdjust = false;
8927             return shim;
8928         },
8929
8930         /**
8931          * Removes this element from the DOM and deletes it from the cache
8932          */
8933         remove : function(){
8934             if(this.dom.parentNode){
8935                 this.dom.parentNode.removeChild(this.dom);
8936             }
8937             delete El.cache[this.dom.id];
8938         },
8939
8940         /**
8941          * Sets up event handlers to add and remove a css class when the mouse is over this element
8942          * @param {String} className
8943          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8944          * mouseout events for children elements
8945          * @return {Roo.Element} this
8946          */
8947         addClassOnOver : function(className, preventFlicker){
8948             this.on("mouseover", function(){
8949                 Roo.fly(this, '_internal').addClass(className);
8950             }, this.dom);
8951             var removeFn = function(e){
8952                 if(preventFlicker !== true || !e.within(this, true)){
8953                     Roo.fly(this, '_internal').removeClass(className);
8954                 }
8955             };
8956             this.on("mouseout", removeFn, this.dom);
8957             return this;
8958         },
8959
8960         /**
8961          * Sets up event handlers to add and remove a css class when this element has the focus
8962          * @param {String} className
8963          * @return {Roo.Element} this
8964          */
8965         addClassOnFocus : function(className){
8966             this.on("focus", function(){
8967                 Roo.fly(this, '_internal').addClass(className);
8968             }, this.dom);
8969             this.on("blur", function(){
8970                 Roo.fly(this, '_internal').removeClass(className);
8971             }, this.dom);
8972             return this;
8973         },
8974         /**
8975          * 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)
8976          * @param {String} className
8977          * @return {Roo.Element} this
8978          */
8979         addClassOnClick : function(className){
8980             var dom = this.dom;
8981             this.on("mousedown", function(){
8982                 Roo.fly(dom, '_internal').addClass(className);
8983                 var d = Roo.get(document);
8984                 var fn = function(){
8985                     Roo.fly(dom, '_internal').removeClass(className);
8986                     d.removeListener("mouseup", fn);
8987                 };
8988                 d.on("mouseup", fn);
8989             });
8990             return this;
8991         },
8992
8993         /**
8994          * Stops the specified event from bubbling and optionally prevents the default action
8995          * @param {String} eventName
8996          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8997          * @return {Roo.Element} this
8998          */
8999         swallowEvent : function(eventName, preventDefault){
9000             var fn = function(e){
9001                 e.stopPropagation();
9002                 if(preventDefault){
9003                     e.preventDefault();
9004                 }
9005             };
9006             if(eventName instanceof Array){
9007                 for(var i = 0, len = eventName.length; i < len; i++){
9008                      this.on(eventName[i], fn);
9009                 }
9010                 return this;
9011             }
9012             this.on(eventName, fn);
9013             return this;
9014         },
9015
9016         /**
9017          * @private
9018          */
9019       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9020
9021         /**
9022          * Sizes this element to its parent element's dimensions performing
9023          * neccessary box adjustments.
9024          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9025          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9026          * @return {Roo.Element} this
9027          */
9028         fitToParent : function(monitorResize, targetParent) {
9029           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9030           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9031           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9032             return;
9033           }
9034           var p = Roo.get(targetParent || this.dom.parentNode);
9035           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9036           if (monitorResize === true) {
9037             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9038             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9039           }
9040           return this;
9041         },
9042
9043         /**
9044          * Gets the next sibling, skipping text nodes
9045          * @return {HTMLElement} The next sibling or null
9046          */
9047         getNextSibling : function(){
9048             var n = this.dom.nextSibling;
9049             while(n && n.nodeType != 1){
9050                 n = n.nextSibling;
9051             }
9052             return n;
9053         },
9054
9055         /**
9056          * Gets the previous sibling, skipping text nodes
9057          * @return {HTMLElement} The previous sibling or null
9058          */
9059         getPrevSibling : function(){
9060             var n = this.dom.previousSibling;
9061             while(n && n.nodeType != 1){
9062                 n = n.previousSibling;
9063             }
9064             return n;
9065         },
9066
9067
9068         /**
9069          * Appends the passed element(s) to this element
9070          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9071          * @return {Roo.Element} this
9072          */
9073         appendChild: function(el){
9074             el = Roo.get(el);
9075             el.appendTo(this);
9076             return this;
9077         },
9078
9079         /**
9080          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9081          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9082          * automatically generated with the specified attributes.
9083          * @param {HTMLElement} insertBefore (optional) a child element of this element
9084          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9085          * @return {Roo.Element} The new child element
9086          */
9087         createChild: function(config, insertBefore, returnDom){
9088             config = config || {tag:'div'};
9089             if(insertBefore){
9090                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9091             }
9092             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9093         },
9094
9095         /**
9096          * Appends this element to the passed element
9097          * @param {String/HTMLElement/Element} el The new parent element
9098          * @return {Roo.Element} this
9099          */
9100         appendTo: function(el){
9101             el = Roo.getDom(el);
9102             el.appendChild(this.dom);
9103             return this;
9104         },
9105
9106         /**
9107          * Inserts this element before the passed element in the DOM
9108          * @param {String/HTMLElement/Element} el The element to insert before
9109          * @return {Roo.Element} this
9110          */
9111         insertBefore: function(el){
9112             el = Roo.getDom(el);
9113             el.parentNode.insertBefore(this.dom, el);
9114             return this;
9115         },
9116
9117         /**
9118          * Inserts this element after the passed element in the DOM
9119          * @param {String/HTMLElement/Element} el The element to insert after
9120          * @return {Roo.Element} this
9121          */
9122         insertAfter: function(el){
9123             el = Roo.getDom(el);
9124             el.parentNode.insertBefore(this.dom, el.nextSibling);
9125             return this;
9126         },
9127
9128         /**
9129          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9130          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9131          * @return {Roo.Element} The new child
9132          */
9133         insertFirst: function(el, returnDom){
9134             el = el || {};
9135             if(typeof el == 'object' && !el.nodeType){ // dh config
9136                 return this.createChild(el, this.dom.firstChild, returnDom);
9137             }else{
9138                 el = Roo.getDom(el);
9139                 this.dom.insertBefore(el, this.dom.firstChild);
9140                 return !returnDom ? Roo.get(el) : el;
9141             }
9142         },
9143
9144         /**
9145          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9146          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9147          * @param {String} where (optional) 'before' or 'after' defaults to before
9148          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9149          * @return {Roo.Element} the inserted Element
9150          */
9151         insertSibling: function(el, where, returnDom){
9152             where = where ? where.toLowerCase() : 'before';
9153             el = el || {};
9154             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9155
9156             if(typeof el == 'object' && !el.nodeType){ // dh config
9157                 if(where == 'after' && !this.dom.nextSibling){
9158                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9159                 }else{
9160                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9161                 }
9162
9163             }else{
9164                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9165                             where == 'before' ? this.dom : this.dom.nextSibling);
9166                 if(!returnDom){
9167                     rt = Roo.get(rt);
9168                 }
9169             }
9170             return rt;
9171         },
9172
9173         /**
9174          * Creates and wraps this element with another element
9175          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9176          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9177          * @return {HTMLElement/Element} The newly created wrapper element
9178          */
9179         wrap: function(config, returnDom){
9180             if(!config){
9181                 config = {tag: "div"};
9182             }
9183             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9184             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9185             return newEl;
9186         },
9187
9188         /**
9189          * Replaces the passed element with this element
9190          * @param {String/HTMLElement/Element} el The element to replace
9191          * @return {Roo.Element} this
9192          */
9193         replace: function(el){
9194             el = Roo.get(el);
9195             this.insertBefore(el);
9196             el.remove();
9197             return this;
9198         },
9199
9200         /**
9201          * Inserts an html fragment into this element
9202          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9203          * @param {String} html The HTML fragment
9204          * @param {Boolean} returnEl True to return an Roo.Element
9205          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9206          */
9207         insertHtml : function(where, html, returnEl){
9208             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9209             return returnEl ? Roo.get(el) : el;
9210         },
9211
9212         /**
9213          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9214          * @param {Object} o The object with the attributes
9215          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9216          * @return {Roo.Element} this
9217          */
9218         set : function(o, useSet){
9219             var el = this.dom;
9220             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9221             for(var attr in o){
9222                 if(attr == "style" || typeof o[attr] == "function") continue;
9223                 if(attr=="cls"){
9224                     el.className = o["cls"];
9225                 }else{
9226                     if(useSet) el.setAttribute(attr, o[attr]);
9227                     else el[attr] = o[attr];
9228                 }
9229             }
9230             if(o.style){
9231                 Roo.DomHelper.applyStyles(el, o.style);
9232             }
9233             return this;
9234         },
9235
9236         /**
9237          * Convenience method for constructing a KeyMap
9238          * @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:
9239          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9240          * @param {Function} fn The function to call
9241          * @param {Object} scope (optional) The scope of the function
9242          * @return {Roo.KeyMap} The KeyMap created
9243          */
9244         addKeyListener : function(key, fn, scope){
9245             var config;
9246             if(typeof key != "object" || key instanceof Array){
9247                 config = {
9248                     key: key,
9249                     fn: fn,
9250                     scope: scope
9251                 };
9252             }else{
9253                 config = {
9254                     key : key.key,
9255                     shift : key.shift,
9256                     ctrl : key.ctrl,
9257                     alt : key.alt,
9258                     fn: fn,
9259                     scope: scope
9260                 };
9261             }
9262             return new Roo.KeyMap(this, config);
9263         },
9264
9265         /**
9266          * Creates a KeyMap for this element
9267          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9268          * @return {Roo.KeyMap} The KeyMap created
9269          */
9270         addKeyMap : function(config){
9271             return new Roo.KeyMap(this, config);
9272         },
9273
9274         /**
9275          * Returns true if this element is scrollable.
9276          * @return {Boolean}
9277          */
9278          isScrollable : function(){
9279             var dom = this.dom;
9280             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9281         },
9282
9283         /**
9284          * 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().
9285          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9286          * @param {Number} value The new scroll value
9287          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9288          * @return {Element} this
9289          */
9290
9291         scrollTo : function(side, value, animate){
9292             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9293             if(!animate || !A){
9294                 this.dom[prop] = value;
9295             }else{
9296                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9297                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9298             }
9299             return this;
9300         },
9301
9302         /**
9303          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9304          * within this element's scrollable range.
9305          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9306          * @param {Number} distance How far to scroll the element in pixels
9307          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9308          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9309          * was scrolled as far as it could go.
9310          */
9311          scroll : function(direction, distance, animate){
9312              if(!this.isScrollable()){
9313                  return;
9314              }
9315              var el = this.dom;
9316              var l = el.scrollLeft, t = el.scrollTop;
9317              var w = el.scrollWidth, h = el.scrollHeight;
9318              var cw = el.clientWidth, ch = el.clientHeight;
9319              direction = direction.toLowerCase();
9320              var scrolled = false;
9321              var a = this.preanim(arguments, 2);
9322              switch(direction){
9323                  case "l":
9324                  case "left":
9325                      if(w - l > cw){
9326                          var v = Math.min(l + distance, w-cw);
9327                          this.scrollTo("left", v, a);
9328                          scrolled = true;
9329                      }
9330                      break;
9331                 case "r":
9332                 case "right":
9333                      if(l > 0){
9334                          var v = Math.max(l - distance, 0);
9335                          this.scrollTo("left", v, a);
9336                          scrolled = true;
9337                      }
9338                      break;
9339                 case "t":
9340                 case "top":
9341                 case "up":
9342                      if(t > 0){
9343                          var v = Math.max(t - distance, 0);
9344                          this.scrollTo("top", v, a);
9345                          scrolled = true;
9346                      }
9347                      break;
9348                 case "b":
9349                 case "bottom":
9350                 case "down":
9351                      if(h - t > ch){
9352                          var v = Math.min(t + distance, h-ch);
9353                          this.scrollTo("top", v, a);
9354                          scrolled = true;
9355                      }
9356                      break;
9357              }
9358              return scrolled;
9359         },
9360
9361         /**
9362          * Translates the passed page coordinates into left/top css values for this element
9363          * @param {Number/Array} x The page x or an array containing [x, y]
9364          * @param {Number} y The page y
9365          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9366          */
9367         translatePoints : function(x, y){
9368             if(typeof x == 'object' || x instanceof Array){
9369                 y = x[1]; x = x[0];
9370             }
9371             var p = this.getStyle('position');
9372             var o = this.getXY();
9373
9374             var l = parseInt(this.getStyle('left'), 10);
9375             var t = parseInt(this.getStyle('top'), 10);
9376
9377             if(isNaN(l)){
9378                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9379             }
9380             if(isNaN(t)){
9381                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9382             }
9383
9384             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9385         },
9386
9387         /**
9388          * Returns the current scroll position of the element.
9389          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9390          */
9391         getScroll : function(){
9392             var d = this.dom, doc = document;
9393             if(d == doc || d == doc.body){
9394                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9395                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9396                 return {left: l, top: t};
9397             }else{
9398                 return {left: d.scrollLeft, top: d.scrollTop};
9399             }
9400         },
9401
9402         /**
9403          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9404          * are convert to standard 6 digit hex color.
9405          * @param {String} attr The css attribute
9406          * @param {String} defaultValue The default value to use when a valid color isn't found
9407          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9408          * YUI color anims.
9409          */
9410         getColor : function(attr, defaultValue, prefix){
9411             var v = this.getStyle(attr);
9412             if(!v || v == "transparent" || v == "inherit") {
9413                 return defaultValue;
9414             }
9415             var color = typeof prefix == "undefined" ? "#" : prefix;
9416             if(v.substr(0, 4) == "rgb("){
9417                 var rvs = v.slice(4, v.length -1).split(",");
9418                 for(var i = 0; i < 3; i++){
9419                     var h = parseInt(rvs[i]).toString(16);
9420                     if(h < 16){
9421                         h = "0" + h;
9422                     }
9423                     color += h;
9424                 }
9425             } else {
9426                 if(v.substr(0, 1) == "#"){
9427                     if(v.length == 4) {
9428                         for(var i = 1; i < 4; i++){
9429                             var c = v.charAt(i);
9430                             color +=  c + c;
9431                         }
9432                     }else if(v.length == 7){
9433                         color += v.substr(1);
9434                     }
9435                 }
9436             }
9437             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9438         },
9439
9440         /**
9441          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9442          * gradient background, rounded corners and a 4-way shadow.
9443          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9444          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9445          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9446          * @return {Roo.Element} this
9447          */
9448         boxWrap : function(cls){
9449             cls = cls || 'x-box';
9450             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9451             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9452             return el;
9453         },
9454
9455         /**
9456          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9457          * @param {String} namespace The namespace in which to look for the attribute
9458          * @param {String} name The attribute name
9459          * @return {String} The attribute value
9460          */
9461         getAttributeNS : Roo.isIE ? function(ns, name){
9462             var d = this.dom;
9463             var type = typeof d[ns+":"+name];
9464             if(type != 'undefined' && type != 'unknown'){
9465                 return d[ns+":"+name];
9466             }
9467             return d[name];
9468         } : function(ns, name){
9469             var d = this.dom;
9470             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9471         }
9472     };
9473
9474     var ep = El.prototype;
9475
9476     /**
9477      * Appends an event handler (Shorthand for addListener)
9478      * @param {String}   eventName     The type of event to append
9479      * @param {Function} fn        The method the event invokes
9480      * @param {Object} scope       (optional) The scope (this object) of the fn
9481      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9482      * @method
9483      */
9484     ep.on = ep.addListener;
9485         // backwards compat
9486     ep.mon = ep.addListener;
9487
9488     /**
9489      * Removes an event handler from this element (shorthand for removeListener)
9490      * @param {String} eventName the type of event to remove
9491      * @param {Function} fn the method the event invokes
9492      * @return {Roo.Element} this
9493      * @method
9494      */
9495     ep.un = ep.removeListener;
9496
9497     /**
9498      * true to automatically adjust width and height settings for box-model issues (default to true)
9499      */
9500     ep.autoBoxAdjust = true;
9501
9502     // private
9503     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9504
9505     // private
9506     El.addUnits = function(v, defaultUnit){
9507         if(v === "" || v == "auto"){
9508             return v;
9509         }
9510         if(v === undefined){
9511             return '';
9512         }
9513         if(typeof v == "number" || !El.unitPattern.test(v)){
9514             return v + (defaultUnit || 'px');
9515         }
9516         return v;
9517     };
9518
9519     // special markup used throughout Roo when box wrapping elements
9520     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>';
9521     /**
9522      * Visibility mode constant - Use visibility to hide element
9523      * @static
9524      * @type Number
9525      */
9526     El.VISIBILITY = 1;
9527     /**
9528      * Visibility mode constant - Use display to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.DISPLAY = 2;
9533
9534     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9535     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9536     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9537
9538
9539
9540     /**
9541      * @private
9542      */
9543     El.cache = {};
9544
9545     var docEl;
9546
9547     /**
9548      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9549      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9550      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9551      * @return {Element} The Element object
9552      * @static
9553      */
9554     El.get = function(el){
9555         var ex, elm, id;
9556         if(!el){ return null; }
9557         if(typeof el == "string"){ // element id
9558             if(!(elm = document.getElementById(el))){
9559                 return null;
9560             }
9561             if(ex = El.cache[el]){
9562                 ex.dom = elm;
9563             }else{
9564                 ex = El.cache[el] = new El(elm);
9565             }
9566             return ex;
9567         }else if(el.tagName){ // dom element
9568             if(!(id = el.id)){
9569                 id = Roo.id(el);
9570             }
9571             if(ex = El.cache[id]){
9572                 ex.dom = el;
9573             }else{
9574                 ex = El.cache[id] = new El(el);
9575             }
9576             return ex;
9577         }else if(el instanceof El){
9578             if(el != docEl){
9579                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9580                                                               // catch case where it hasn't been appended
9581                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9582             }
9583             return el;
9584         }else if(el.isComposite){
9585             return el;
9586         }else if(el instanceof Array){
9587             return El.select(el);
9588         }else if(el == document){
9589             // create a bogus element object representing the document object
9590             if(!docEl){
9591                 var f = function(){};
9592                 f.prototype = El.prototype;
9593                 docEl = new f();
9594                 docEl.dom = document;
9595             }
9596             return docEl;
9597         }
9598         return null;
9599     };
9600
9601     // private
9602     El.uncache = function(el){
9603         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9604             if(a[i]){
9605                 delete El.cache[a[i].id || a[i]];
9606             }
9607         }
9608     };
9609
9610     // private
9611     // Garbage collection - uncache elements/purge listeners on orphaned elements
9612     // so we don't hold a reference and cause the browser to retain them
9613     El.garbageCollect = function(){
9614         if(!Roo.enableGarbageCollector){
9615             clearInterval(El.collectorThread);
9616             return;
9617         }
9618         for(var eid in El.cache){
9619             var el = El.cache[eid], d = el.dom;
9620             // -------------------------------------------------------
9621             // Determining what is garbage:
9622             // -------------------------------------------------------
9623             // !d
9624             // dom node is null, definitely garbage
9625             // -------------------------------------------------------
9626             // !d.parentNode
9627             // no parentNode == direct orphan, definitely garbage
9628             // -------------------------------------------------------
9629             // !d.offsetParent && !document.getElementById(eid)
9630             // display none elements have no offsetParent so we will
9631             // also try to look it up by it's id. However, check
9632             // offsetParent first so we don't do unneeded lookups.
9633             // This enables collection of elements that are not orphans
9634             // directly, but somewhere up the line they have an orphan
9635             // parent.
9636             // -------------------------------------------------------
9637             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9638                 delete El.cache[eid];
9639                 if(d && Roo.enableListenerCollection){
9640                     E.purgeElement(d);
9641                 }
9642             }
9643         }
9644     }
9645     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9646
9647
9648     // dom is optional
9649     El.Flyweight = function(dom){
9650         this.dom = dom;
9651     };
9652     El.Flyweight.prototype = El.prototype;
9653
9654     El._flyweights = {};
9655     /**
9656      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9657      * the dom node can be overwritten by other code.
9658      * @param {String/HTMLElement} el The dom node or id
9659      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9660      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9661      * @static
9662      * @return {Element} The shared Element object
9663      */
9664     El.fly = function(el, named){
9665         named = named || '_global';
9666         el = Roo.getDom(el);
9667         if(!el){
9668             return null;
9669         }
9670         if(!El._flyweights[named]){
9671             El._flyweights[named] = new El.Flyweight();
9672         }
9673         El._flyweights[named].dom = el;
9674         return El._flyweights[named];
9675     };
9676
9677     /**
9678      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9679      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9680      * Shorthand of {@link Roo.Element#get}
9681      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9682      * @return {Element} The Element object
9683      * @member Roo
9684      * @method get
9685      */
9686     Roo.get = El.get;
9687     /**
9688      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9689      * the dom node can be overwritten by other code.
9690      * Shorthand of {@link Roo.Element#fly}
9691      * @param {String/HTMLElement} el The dom node or id
9692      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9693      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9694      * @static
9695      * @return {Element} The shared Element object
9696      * @member Roo
9697      * @method fly
9698      */
9699     Roo.fly = El.fly;
9700
9701     // speedy lookup for elements never to box adjust
9702     var noBoxAdjust = Roo.isStrict ? {
9703         select:1
9704     } : {
9705         input:1, select:1, textarea:1
9706     };
9707     if(Roo.isIE || Roo.isGecko){
9708         noBoxAdjust['button'] = 1;
9709     }
9710
9711
9712     Roo.EventManager.on(window, 'unload', function(){
9713         delete El.cache;
9714         delete El._flyweights;
9715     });
9716 })();
9717
9718
9719
9720
9721 if(Roo.DomQuery){
9722     Roo.Element.selectorFunction = Roo.DomQuery.select;
9723 }
9724
9725 Roo.Element.select = function(selector, unique, root){
9726     var els;
9727     if(typeof selector == "string"){
9728         els = Roo.Element.selectorFunction(selector, root);
9729     }else if(selector.length !== undefined){
9730         els = selector;
9731     }else{
9732         throw "Invalid selector";
9733     }
9734     if(unique === true){
9735         return new Roo.CompositeElement(els);
9736     }else{
9737         return new Roo.CompositeElementLite(els);
9738     }
9739 };
9740 /**
9741  * Selects elements based on the passed CSS selector to enable working on them as 1.
9742  * @param {String/Array} selector The CSS selector or an array of elements
9743  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9744  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9745  * @return {CompositeElementLite/CompositeElement}
9746  * @member Roo
9747  * @method select
9748  */
9749 Roo.select = Roo.Element.select;
9750
9751
9752
9753
9754
9755
9756
9757
9758
9759
9760
9761
9762
9763
9764 /*
9765  * Based on:
9766  * Ext JS Library 1.1.1
9767  * Copyright(c) 2006-2007, Ext JS, LLC.
9768  *
9769  * Originally Released Under LGPL - original licence link has changed is not relivant.
9770  *
9771  * Fork - LGPL
9772  * <script type="text/javascript">
9773  */
9774
9775
9776
9777 //Notifies Element that fx methods are available
9778 Roo.enableFx = true;
9779
9780 /**
9781  * @class Roo.Fx
9782  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9783  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9784  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9785  * Element effects to work.</p><br/>
9786  *
9787  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9788  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9789  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9790  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9791  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9792  * expected results and should be done with care.</p><br/>
9793  *
9794  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9795  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9796 <pre>
9797 Value  Description
9798 -----  -----------------------------
9799 tl     The top left corner
9800 t      The center of the top edge
9801 tr     The top right corner
9802 l      The center of the left edge
9803 r      The center of the right edge
9804 bl     The bottom left corner
9805 b      The center of the bottom edge
9806 br     The bottom right corner
9807 </pre>
9808  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9809  * below are common options that can be passed to any Fx method.</b>
9810  * @cfg {Function} callback A function called when the effect is finished
9811  * @cfg {Object} scope The scope of the effect function
9812  * @cfg {String} easing A valid Easing value for the effect
9813  * @cfg {String} afterCls A css class to apply after the effect
9814  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9815  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9816  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9817  * effects that end with the element being visually hidden, ignored otherwise)
9818  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9819  * a function which returns such a specification that will be applied to the Element after the effect finishes
9820  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9821  * @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
9822  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9823  */
9824 Roo.Fx = {
9825         /**
9826          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9827          * origin for the slide effect.  This function automatically handles wrapping the element with
9828          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9829          * Usage:
9830          *<pre><code>
9831 // default: slide the element in from the top
9832 el.slideIn();
9833
9834 // custom: slide the element in from the right with a 2-second duration
9835 el.slideIn('r', { duration: 2 });
9836
9837 // common config options shown with default values
9838 el.slideIn('t', {
9839     easing: 'easeOut',
9840     duration: .5
9841 });
9842 </code></pre>
9843          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9844          * @param {Object} options (optional) Object literal with any of the Fx config options
9845          * @return {Roo.Element} The Element
9846          */
9847     slideIn : function(anchor, o){
9848         var el = this.getFxEl();
9849         o = o || {};
9850
9851         el.queueFx(o, function(){
9852
9853             anchor = anchor || "t";
9854
9855             // fix display to visibility
9856             this.fixDisplay();
9857
9858             // restore values after effect
9859             var r = this.getFxRestore();
9860             var b = this.getBox();
9861             // fixed size for slide
9862             this.setSize(b);
9863
9864             // wrap if needed
9865             var wrap = this.fxWrap(r.pos, o, "hidden");
9866
9867             var st = this.dom.style;
9868             st.visibility = "visible";
9869             st.position = "absolute";
9870
9871             // clear out temp styles after slide and unwrap
9872             var after = function(){
9873                 el.fxUnwrap(wrap, r.pos, o);
9874                 st.width = r.width;
9875                 st.height = r.height;
9876                 el.afterFx(o);
9877             };
9878             // time to calc the positions
9879             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9880
9881             switch(anchor.toLowerCase()){
9882                 case "t":
9883                     wrap.setSize(b.width, 0);
9884                     st.left = st.bottom = "0";
9885                     a = {height: bh};
9886                 break;
9887                 case "l":
9888                     wrap.setSize(0, b.height);
9889                     st.right = st.top = "0";
9890                     a = {width: bw};
9891                 break;
9892                 case "r":
9893                     wrap.setSize(0, b.height);
9894                     wrap.setX(b.right);
9895                     st.left = st.top = "0";
9896                     a = {width: bw, points: pt};
9897                 break;
9898                 case "b":
9899                     wrap.setSize(b.width, 0);
9900                     wrap.setY(b.bottom);
9901                     st.left = st.top = "0";
9902                     a = {height: bh, points: pt};
9903                 break;
9904                 case "tl":
9905                     wrap.setSize(0, 0);
9906                     st.right = st.bottom = "0";
9907                     a = {width: bw, height: bh};
9908                 break;
9909                 case "bl":
9910                     wrap.setSize(0, 0);
9911                     wrap.setY(b.y+b.height);
9912                     st.right = st.top = "0";
9913                     a = {width: bw, height: bh, points: pt};
9914                 break;
9915                 case "br":
9916                     wrap.setSize(0, 0);
9917                     wrap.setXY([b.right, b.bottom]);
9918                     st.left = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "tr":
9922                     wrap.setSize(0, 0);
9923                     wrap.setX(b.x+b.width);
9924                     st.left = st.bottom = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927             }
9928             this.dom.style.visibility = "visible";
9929             wrap.show();
9930
9931             arguments.callee.anim = wrap.fxanim(a,
9932                 o,
9933                 'motion',
9934                 .5,
9935                 'easeOut', after);
9936         });
9937         return this;
9938     },
9939     
9940         /**
9941          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9942          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9943          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9944          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9945          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9946          * Usage:
9947          *<pre><code>
9948 // default: slide the element out to the top
9949 el.slideOut();
9950
9951 // custom: slide the element out to the right with a 2-second duration
9952 el.slideOut('r', { duration: 2 });
9953
9954 // common config options shown with default values
9955 el.slideOut('t', {
9956     easing: 'easeOut',
9957     duration: .5,
9958     remove: false,
9959     useDisplay: false
9960 });
9961 </code></pre>
9962          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9963          * @param {Object} options (optional) Object literal with any of the Fx config options
9964          * @return {Roo.Element} The Element
9965          */
9966     slideOut : function(anchor, o){
9967         var el = this.getFxEl();
9968         o = o || {};
9969
9970         el.queueFx(o, function(){
9971
9972             anchor = anchor || "t";
9973
9974             // restore values after effect
9975             var r = this.getFxRestore();
9976             
9977             var b = this.getBox();
9978             // fixed size for slide
9979             this.setSize(b);
9980
9981             // wrap if needed
9982             var wrap = this.fxWrap(r.pos, o, "visible");
9983
9984             var st = this.dom.style;
9985             st.visibility = "visible";
9986             st.position = "absolute";
9987
9988             wrap.setSize(b);
9989
9990             var after = function(){
9991                 if(o.useDisplay){
9992                     el.setDisplayed(false);
9993                 }else{
9994                     el.hide();
9995                 }
9996
9997                 el.fxUnwrap(wrap, r.pos, o);
9998
9999                 st.width = r.width;
10000                 st.height = r.height;
10001
10002                 el.afterFx(o);
10003             };
10004
10005             var a, zero = {to: 0};
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     st.left = st.bottom = "0";
10009                     a = {height: zero};
10010                 break;
10011                 case "l":
10012                     st.right = st.top = "0";
10013                     a = {width: zero};
10014                 break;
10015                 case "r":
10016                     st.left = st.top = "0";
10017                     a = {width: zero, points: {to:[b.right, b.y]}};
10018                 break;
10019                 case "b":
10020                     st.left = st.top = "0";
10021                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10022                 break;
10023                 case "tl":
10024                     st.right = st.bottom = "0";
10025                     a = {width: zero, height: zero};
10026                 break;
10027                 case "bl":
10028                     st.right = st.top = "0";
10029                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10030                 break;
10031                 case "br":
10032                     st.left = st.top = "0";
10033                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10034                 break;
10035                 case "tr":
10036                     st.left = st.bottom = "0";
10037                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10038                 break;
10039             }
10040
10041             arguments.callee.anim = wrap.fxanim(a,
10042                 o,
10043                 'motion',
10044                 .5,
10045                 "easeOut", after);
10046         });
10047         return this;
10048     },
10049
10050         /**
10051          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10052          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10053          * The element must be removed from the DOM using the 'remove' config option if desired.
10054          * Usage:
10055          *<pre><code>
10056 // default
10057 el.puff();
10058
10059 // common config options shown with default values
10060 el.puff({
10061     easing: 'easeOut',
10062     duration: .5,
10063     remove: false,
10064     useDisplay: false
10065 });
10066 </code></pre>
10067          * @param {Object} options (optional) Object literal with any of the Fx config options
10068          * @return {Roo.Element} The Element
10069          */
10070     puff : function(o){
10071         var el = this.getFxEl();
10072         o = o || {};
10073
10074         el.queueFx(o, function(){
10075             this.clearOpacity();
10076             this.show();
10077
10078             // restore values after effect
10079             var r = this.getFxRestore();
10080             var st = this.dom.style;
10081
10082             var after = function(){
10083                 if(o.useDisplay){
10084                     el.setDisplayed(false);
10085                 }else{
10086                     el.hide();
10087                 }
10088
10089                 el.clearOpacity();
10090
10091                 el.setPositioning(r.pos);
10092                 st.width = r.width;
10093                 st.height = r.height;
10094                 st.fontSize = '';
10095                 el.afterFx(o);
10096             };
10097
10098             var width = this.getWidth();
10099             var height = this.getHeight();
10100
10101             arguments.callee.anim = this.fxanim({
10102                     width : {to: this.adjustWidth(width * 2)},
10103                     height : {to: this.adjustHeight(height * 2)},
10104                     points : {by: [-(width * .5), -(height * .5)]},
10105                     opacity : {to: 0},
10106                     fontSize: {to:200, unit: "%"}
10107                 },
10108                 o,
10109                 'motion',
10110                 .5,
10111                 "easeOut", after);
10112         });
10113         return this;
10114     },
10115
10116         /**
10117          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10118          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10119          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10120          * Usage:
10121          *<pre><code>
10122 // default
10123 el.switchOff();
10124
10125 // all config options shown with default values
10126 el.switchOff({
10127     easing: 'easeIn',
10128     duration: .3,
10129     remove: false,
10130     useDisplay: false
10131 });
10132 </code></pre>
10133          * @param {Object} options (optional) Object literal with any of the Fx config options
10134          * @return {Roo.Element} The Element
10135          */
10136     switchOff : function(o){
10137         var el = this.getFxEl();
10138         o = o || {};
10139
10140         el.queueFx(o, function(){
10141             this.clearOpacity();
10142             this.clip();
10143
10144             // restore values after effect
10145             var r = this.getFxRestore();
10146             var st = this.dom.style;
10147
10148             var after = function(){
10149                 if(o.useDisplay){
10150                     el.setDisplayed(false);
10151                 }else{
10152                     el.hide();
10153                 }
10154
10155                 el.clearOpacity();
10156                 el.setPositioning(r.pos);
10157                 st.width = r.width;
10158                 st.height = r.height;
10159
10160                 el.afterFx(o);
10161             };
10162
10163             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10164                 this.clearOpacity();
10165                 (function(){
10166                     this.fxanim({
10167                         height:{to:1},
10168                         points:{by:[0, this.getHeight() * .5]}
10169                     }, o, 'motion', 0.3, 'easeIn', after);
10170                 }).defer(100, this);
10171             });
10172         });
10173         return this;
10174     },
10175
10176     /**
10177      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10178      * changed using the "attr" config option) and then fading back to the original color. If no original
10179      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10180      * Usage:
10181 <pre><code>
10182 // default: highlight background to yellow
10183 el.highlight();
10184
10185 // custom: highlight foreground text to blue for 2 seconds
10186 el.highlight("0000ff", { attr: 'color', duration: 2 });
10187
10188 // common config options shown with default values
10189 el.highlight("ffff9c", {
10190     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10191     endColor: (current color) or "ffffff",
10192     easing: 'easeIn',
10193     duration: 1
10194 });
10195 </code></pre>
10196      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10197      * @param {Object} options (optional) Object literal with any of the Fx config options
10198      * @return {Roo.Element} The Element
10199      */ 
10200     highlight : function(color, o){
10201         var el = this.getFxEl();
10202         o = o || {};
10203
10204         el.queueFx(o, function(){
10205             color = color || "ffff9c";
10206             attr = o.attr || "backgroundColor";
10207
10208             this.clearOpacity();
10209             this.show();
10210
10211             var origColor = this.getColor(attr);
10212             var restoreColor = this.dom.style[attr];
10213             endColor = (o.endColor || origColor) || "ffffff";
10214
10215             var after = function(){
10216                 el.dom.style[attr] = restoreColor;
10217                 el.afterFx(o);
10218             };
10219
10220             var a = {};
10221             a[attr] = {from: color, to: endColor};
10222             arguments.callee.anim = this.fxanim(a,
10223                 o,
10224                 'color',
10225                 1,
10226                 'easeIn', after);
10227         });
10228         return this;
10229     },
10230
10231    /**
10232     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10233     * Usage:
10234 <pre><code>
10235 // default: a single light blue ripple
10236 el.frame();
10237
10238 // custom: 3 red ripples lasting 3 seconds total
10239 el.frame("ff0000", 3, { duration: 3 });
10240
10241 // common config options shown with default values
10242 el.frame("C3DAF9", 1, {
10243     duration: 1 //duration of entire animation (not each individual ripple)
10244     // Note: Easing is not configurable and will be ignored if included
10245 });
10246 </code></pre>
10247     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10248     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10249     * @param {Object} options (optional) Object literal with any of the Fx config options
10250     * @return {Roo.Element} The Element
10251     */
10252     frame : function(color, count, o){
10253         var el = this.getFxEl();
10254         o = o || {};
10255
10256         el.queueFx(o, function(){
10257             color = color || "#C3DAF9";
10258             if(color.length == 6){
10259                 color = "#" + color;
10260             }
10261             count = count || 1;
10262             duration = o.duration || 1;
10263             this.show();
10264
10265             var b = this.getBox();
10266             var animFn = function(){
10267                 var proxy = this.createProxy({
10268
10269                      style:{
10270                         visbility:"hidden",
10271                         position:"absolute",
10272                         "z-index":"35000", // yee haw
10273                         border:"0px solid " + color
10274                      }
10275                   });
10276                 var scale = Roo.isBorderBox ? 2 : 1;
10277                 proxy.animate({
10278                     top:{from:b.y, to:b.y - 20},
10279                     left:{from:b.x, to:b.x - 20},
10280                     borderWidth:{from:0, to:10},
10281                     opacity:{from:1, to:0},
10282                     height:{from:b.height, to:(b.height + (20*scale))},
10283                     width:{from:b.width, to:(b.width + (20*scale))}
10284                 }, duration, function(){
10285                     proxy.remove();
10286                 });
10287                 if(--count > 0){
10288                      animFn.defer((duration/2)*1000, this);
10289                 }else{
10290                     el.afterFx(o);
10291                 }
10292             };
10293             animFn.call(this);
10294         });
10295         return this;
10296     },
10297
10298    /**
10299     * Creates a pause before any subsequent queued effects begin.  If there are
10300     * no effects queued after the pause it will have no effect.
10301     * Usage:
10302 <pre><code>
10303 el.pause(1);
10304 </code></pre>
10305     * @param {Number} seconds The length of time to pause (in seconds)
10306     * @return {Roo.Element} The Element
10307     */
10308     pause : function(seconds){
10309         var el = this.getFxEl();
10310         var o = {};
10311
10312         el.queueFx(o, function(){
10313             setTimeout(function(){
10314                 el.afterFx(o);
10315             }, seconds * 1000);
10316         });
10317         return this;
10318     },
10319
10320    /**
10321     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10322     * using the "endOpacity" config option.
10323     * Usage:
10324 <pre><code>
10325 // default: fade in from opacity 0 to 100%
10326 el.fadeIn();
10327
10328 // custom: fade in from opacity 0 to 75% over 2 seconds
10329 el.fadeIn({ endOpacity: .75, duration: 2});
10330
10331 // common config options shown with default values
10332 el.fadeIn({
10333     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10334     easing: 'easeOut',
10335     duration: .5
10336 });
10337 </code></pre>
10338     * @param {Object} options (optional) Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     fadeIn : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             this.setOpacity(0);
10346             this.fixDisplay();
10347             this.dom.style.visibility = 'visible';
10348             var to = o.endOpacity || 1;
10349             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10350                 o, null, .5, "easeOut", function(){
10351                 if(to == 1){
10352                     this.clearOpacity();
10353                 }
10354                 el.afterFx(o);
10355             });
10356         });
10357         return this;
10358     },
10359
10360    /**
10361     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10362     * using the "endOpacity" config option.
10363     * Usage:
10364 <pre><code>
10365 // default: fade out from the element's current opacity to 0
10366 el.fadeOut();
10367
10368 // custom: fade out from the element's current opacity to 25% over 2 seconds
10369 el.fadeOut({ endOpacity: .25, duration: 2});
10370
10371 // common config options shown with default values
10372 el.fadeOut({
10373     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10374     easing: 'easeOut',
10375     duration: .5
10376     remove: false,
10377     useDisplay: false
10378 });
10379 </code></pre>
10380     * @param {Object} options (optional) Object literal with any of the Fx config options
10381     * @return {Roo.Element} The Element
10382     */
10383     fadeOut : function(o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386         el.queueFx(o, function(){
10387             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10388                 o, null, .5, "easeOut", function(){
10389                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10390                      this.dom.style.display = "none";
10391                 }else{
10392                      this.dom.style.visibility = "hidden";
10393                 }
10394                 this.clearOpacity();
10395                 el.afterFx(o);
10396             });
10397         });
10398         return this;
10399     },
10400
10401    /**
10402     * Animates the transition of an element's dimensions from a starting height/width
10403     * to an ending height/width.
10404     * Usage:
10405 <pre><code>
10406 // change height and width to 100x100 pixels
10407 el.scale(100, 100);
10408
10409 // common config options shown with default values.  The height and width will default to
10410 // the element's existing values if passed as null.
10411 el.scale(
10412     [element's width],
10413     [element's height], {
10414     easing: 'easeOut',
10415     duration: .35
10416 });
10417 </code></pre>
10418     * @param {Number} width  The new width (pass undefined to keep the original width)
10419     * @param {Number} height  The new height (pass undefined to keep the original height)
10420     * @param {Object} options (optional) Object literal with any of the Fx config options
10421     * @return {Roo.Element} The Element
10422     */
10423     scale : function(w, h, o){
10424         this.shift(Roo.apply({}, o, {
10425             width: w,
10426             height: h
10427         }));
10428         return this;
10429     },
10430
10431    /**
10432     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10433     * Any of these properties not specified in the config object will not be changed.  This effect 
10434     * requires that at least one new dimension, position or opacity setting must be passed in on
10435     * the config object in order for the function to have any effect.
10436     * Usage:
10437 <pre><code>
10438 // slide the element horizontally to x position 200 while changing the height and opacity
10439 el.shift({ x: 200, height: 50, opacity: .8 });
10440
10441 // common config options shown with default values.
10442 el.shift({
10443     width: [element's width],
10444     height: [element's height],
10445     x: [element's x position],
10446     y: [element's y position],
10447     opacity: [element's opacity],
10448     easing: 'easeOut',
10449     duration: .35
10450 });
10451 </code></pre>
10452     * @param {Object} options  Object literal with any of the Fx config options
10453     * @return {Roo.Element} The Element
10454     */
10455     shift : function(o){
10456         var el = this.getFxEl();
10457         o = o || {};
10458         el.queueFx(o, function(){
10459             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10460             if(w !== undefined){
10461                 a.width = {to: this.adjustWidth(w)};
10462             }
10463             if(h !== undefined){
10464                 a.height = {to: this.adjustHeight(h)};
10465             }
10466             if(x !== undefined || y !== undefined){
10467                 a.points = {to: [
10468                     x !== undefined ? x : this.getX(),
10469                     y !== undefined ? y : this.getY()
10470                 ]};
10471             }
10472             if(op !== undefined){
10473                 a.opacity = {to: op};
10474             }
10475             if(o.xy !== undefined){
10476                 a.points = {to: o.xy};
10477             }
10478             arguments.callee.anim = this.fxanim(a,
10479                 o, 'motion', .35, "easeOut", function(){
10480                 el.afterFx(o);
10481             });
10482         });
10483         return this;
10484     },
10485
10486         /**
10487          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10488          * ending point of the effect.
10489          * Usage:
10490          *<pre><code>
10491 // default: slide the element downward while fading out
10492 el.ghost();
10493
10494 // custom: slide the element out to the right with a 2-second duration
10495 el.ghost('r', { duration: 2 });
10496
10497 // common config options shown with default values
10498 el.ghost('b', {
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10506          * @param {Object} options (optional) Object literal with any of the Fx config options
10507          * @return {Roo.Element} The Element
10508          */
10509     ghost : function(anchor, o){
10510         var el = this.getFxEl();
10511         o = o || {};
10512
10513         el.queueFx(o, function(){
10514             anchor = anchor || "b";
10515
10516             // restore values after effect
10517             var r = this.getFxRestore();
10518             var w = this.getWidth(),
10519                 h = this.getHeight();
10520
10521             var st = this.dom.style;
10522
10523             var after = function(){
10524                 if(o.useDisplay){
10525                     el.setDisplayed(false);
10526                 }else{
10527                     el.hide();
10528                 }
10529
10530                 el.clearOpacity();
10531                 el.setPositioning(r.pos);
10532                 st.width = r.width;
10533                 st.height = r.height;
10534
10535                 el.afterFx(o);
10536             };
10537
10538             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10539             switch(anchor.toLowerCase()){
10540                 case "t":
10541                     pt.by = [0, -h];
10542                 break;
10543                 case "l":
10544                     pt.by = [-w, 0];
10545                 break;
10546                 case "r":
10547                     pt.by = [w, 0];
10548                 break;
10549                 case "b":
10550                     pt.by = [0, h];
10551                 break;
10552                 case "tl":
10553                     pt.by = [-w, -h];
10554                 break;
10555                 case "bl":
10556                     pt.by = [-w, h];
10557                 break;
10558                 case "br":
10559                     pt.by = [w, h];
10560                 break;
10561                 case "tr":
10562                     pt.by = [w, -h];
10563                 break;
10564             }
10565
10566             arguments.callee.anim = this.fxanim(a,
10567                 o,
10568                 'motion',
10569                 .5,
10570                 "easeOut", after);
10571         });
10572         return this;
10573     },
10574
10575         /**
10576          * Ensures that all effects queued after syncFx is called on the element are
10577          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10578          * @return {Roo.Element} The Element
10579          */
10580     syncFx : function(){
10581         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10582             block : false,
10583             concurrent : true,
10584             stopFx : false
10585         });
10586         return this;
10587     },
10588
10589         /**
10590          * Ensures that all effects queued after sequenceFx is called on the element are
10591          * run in sequence.  This is the opposite of {@link #syncFx}.
10592          * @return {Roo.Element} The Element
10593          */
10594     sequenceFx : function(){
10595         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10596             block : false,
10597             concurrent : false,
10598             stopFx : false
10599         });
10600         return this;
10601     },
10602
10603         /* @private */
10604     nextFx : function(){
10605         var ef = this.fxQueue[0];
10606         if(ef){
10607             ef.call(this);
10608         }
10609     },
10610
10611         /**
10612          * Returns true if the element has any effects actively running or queued, else returns false.
10613          * @return {Boolean} True if element has active effects, else false
10614          */
10615     hasActiveFx : function(){
10616         return this.fxQueue && this.fxQueue[0];
10617     },
10618
10619         /**
10620          * Stops any running effects and clears the element's internal effects queue if it contains
10621          * any additional effects that haven't started yet.
10622          * @return {Roo.Element} The Element
10623          */
10624     stopFx : function(){
10625         if(this.hasActiveFx()){
10626             var cur = this.fxQueue[0];
10627             if(cur && cur.anim && cur.anim.isAnimated()){
10628                 this.fxQueue = [cur]; // clear out others
10629                 cur.anim.stop(true);
10630             }
10631         }
10632         return this;
10633     },
10634
10635         /* @private */
10636     beforeFx : function(o){
10637         if(this.hasActiveFx() && !o.concurrent){
10638            if(o.stopFx){
10639                this.stopFx();
10640                return true;
10641            }
10642            return false;
10643         }
10644         return true;
10645     },
10646
10647         /**
10648          * Returns true if the element is currently blocking so that no other effect can be queued
10649          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10650          * used to ensure that an effect initiated by a user action runs to completion prior to the
10651          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10652          * @return {Boolean} True if blocking, else false
10653          */
10654     hasFxBlock : function(){
10655         var q = this.fxQueue;
10656         return q && q[0] && q[0].block;
10657     },
10658
10659         /* @private */
10660     queueFx : function(o, fn){
10661         if(!this.fxQueue){
10662             this.fxQueue = [];
10663         }
10664         if(!this.hasFxBlock()){
10665             Roo.applyIf(o, this.fxDefaults);
10666             if(!o.concurrent){
10667                 var run = this.beforeFx(o);
10668                 fn.block = o.block;
10669                 this.fxQueue.push(fn);
10670                 if(run){
10671                     this.nextFx();
10672                 }
10673             }else{
10674                 fn.call(this);
10675             }
10676         }
10677         return this;
10678     },
10679
10680         /* @private */
10681     fxWrap : function(pos, o, vis){
10682         var wrap;
10683         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10684             var wrapXY;
10685             if(o.fixPosition){
10686                 wrapXY = this.getXY();
10687             }
10688             var div = document.createElement("div");
10689             div.style.visibility = vis;
10690             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10691             wrap.setPositioning(pos);
10692             if(wrap.getStyle("position") == "static"){
10693                 wrap.position("relative");
10694             }
10695             this.clearPositioning('auto');
10696             wrap.clip();
10697             wrap.dom.appendChild(this.dom);
10698             if(wrapXY){
10699                 wrap.setXY(wrapXY);
10700             }
10701         }
10702         return wrap;
10703     },
10704
10705         /* @private */
10706     fxUnwrap : function(wrap, pos, o){
10707         this.clearPositioning();
10708         this.setPositioning(pos);
10709         if(!o.wrap){
10710             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10711             wrap.remove();
10712         }
10713     },
10714
10715         /* @private */
10716     getFxRestore : function(){
10717         var st = this.dom.style;
10718         return {pos: this.getPositioning(), width: st.width, height : st.height};
10719     },
10720
10721         /* @private */
10722     afterFx : function(o){
10723         if(o.afterStyle){
10724             this.applyStyles(o.afterStyle);
10725         }
10726         if(o.afterCls){
10727             this.addClass(o.afterCls);
10728         }
10729         if(o.remove === true){
10730             this.remove();
10731         }
10732         Roo.callback(o.callback, o.scope, [this]);
10733         if(!o.concurrent){
10734             this.fxQueue.shift();
10735             this.nextFx();
10736         }
10737     },
10738
10739         /* @private */
10740     getFxEl : function(){ // support for composite element fx
10741         return Roo.get(this.dom);
10742     },
10743
10744         /* @private */
10745     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10746         animType = animType || 'run';
10747         opt = opt || {};
10748         var anim = Roo.lib.Anim[animType](
10749             this.dom, args,
10750             (opt.duration || defaultDur) || .35,
10751             (opt.easing || defaultEase) || 'easeOut',
10752             function(){
10753                 Roo.callback(cb, this);
10754             },
10755             this
10756         );
10757         opt.anim = anim;
10758         return anim;
10759     }
10760 };
10761
10762 // backwords compat
10763 Roo.Fx.resize = Roo.Fx.scale;
10764
10765 //When included, Roo.Fx is automatically applied to Element so that all basic
10766 //effects are available directly via the Element API
10767 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10768  * Based on:
10769  * Ext JS Library 1.1.1
10770  * Copyright(c) 2006-2007, Ext JS, LLC.
10771  *
10772  * Originally Released Under LGPL - original licence link has changed is not relivant.
10773  *
10774  * Fork - LGPL
10775  * <script type="text/javascript">
10776  */
10777
10778
10779 /**
10780  * @class Roo.CompositeElement
10781  * Standard composite class. Creates a Roo.Element for every element in the collection.
10782  * <br><br>
10783  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10784  * actions will be performed on all the elements in this collection.</b>
10785  * <br><br>
10786  * All methods return <i>this</i> and can be chained.
10787  <pre><code>
10788  var els = Roo.select("#some-el div.some-class", true);
10789  // or select directly from an existing element
10790  var el = Roo.get('some-el');
10791  el.select('div.some-class', true);
10792
10793  els.setWidth(100); // all elements become 100 width
10794  els.hide(true); // all elements fade out and hide
10795  // or
10796  els.setWidth(100).hide(true);
10797  </code></pre>
10798  */
10799 Roo.CompositeElement = function(els){
10800     this.elements = [];
10801     this.addElements(els);
10802 };
10803 Roo.CompositeElement.prototype = {
10804     isComposite: true,
10805     addElements : function(els){
10806         if(!els) return this;
10807         if(typeof els == "string"){
10808             els = Roo.Element.selectorFunction(els);
10809         }
10810         var yels = this.elements;
10811         var index = yels.length-1;
10812         for(var i = 0, len = els.length; i < len; i++) {
10813                 yels[++index] = Roo.get(els[i]);
10814         }
10815         return this;
10816     },
10817
10818     /**
10819     * Clears this composite and adds the elements returned by the passed selector.
10820     * @param {String/Array} els A string CSS selector, an array of elements or an element
10821     * @return {CompositeElement} this
10822     */
10823     fill : function(els){
10824         this.elements = [];
10825         this.add(els);
10826         return this;
10827     },
10828
10829     /**
10830     * Filters this composite to only elements that match the passed selector.
10831     * @param {String} selector A string CSS selector
10832     * @return {CompositeElement} this
10833     */
10834     filter : function(selector){
10835         var els = [];
10836         this.each(function(el){
10837             if(el.is(selector)){
10838                 els[els.length] = el.dom;
10839             }
10840         });
10841         this.fill(els);
10842         return this;
10843     },
10844
10845     invoke : function(fn, args){
10846         var els = this.elements;
10847         for(var i = 0, len = els.length; i < len; i++) {
10848                 Roo.Element.prototype[fn].apply(els[i], args);
10849         }
10850         return this;
10851     },
10852     /**
10853     * Adds elements to this composite.
10854     * @param {String/Array} els A string CSS selector, an array of elements or an element
10855     * @return {CompositeElement} this
10856     */
10857     add : function(els){
10858         if(typeof els == "string"){
10859             this.addElements(Roo.Element.selectorFunction(els));
10860         }else if(els.length !== undefined){
10861             this.addElements(els);
10862         }else{
10863             this.addElements([els]);
10864         }
10865         return this;
10866     },
10867     /**
10868     * Calls the passed function passing (el, this, index) for each element in this composite.
10869     * @param {Function} fn The function to call
10870     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10871     * @return {CompositeElement} this
10872     */
10873     each : function(fn, scope){
10874         var els = this.elements;
10875         for(var i = 0, len = els.length; i < len; i++){
10876             if(fn.call(scope || els[i], els[i], this, i) === false) {
10877                 break;
10878             }
10879         }
10880         return this;
10881     },
10882
10883     /**
10884      * Returns the Element object at the specified index
10885      * @param {Number} index
10886      * @return {Roo.Element}
10887      */
10888     item : function(index){
10889         return this.elements[index] || null;
10890     },
10891
10892     /**
10893      * Returns the first Element
10894      * @return {Roo.Element}
10895      */
10896     first : function(){
10897         return this.item(0);
10898     },
10899
10900     /**
10901      * Returns the last Element
10902      * @return {Roo.Element}
10903      */
10904     last : function(){
10905         return this.item(this.elements.length-1);
10906     },
10907
10908     /**
10909      * Returns the number of elements in this composite
10910      * @return Number
10911      */
10912     getCount : function(){
10913         return this.elements.length;
10914     },
10915
10916     /**
10917      * Returns true if this composite contains the passed element
10918      * @return Boolean
10919      */
10920     contains : function(el){
10921         return this.indexOf(el) !== -1;
10922     },
10923
10924     /**
10925      * Returns true if this composite contains the passed element
10926      * @return Boolean
10927      */
10928     indexOf : function(el){
10929         return this.elements.indexOf(Roo.get(el));
10930     },
10931
10932
10933     /**
10934     * Removes the specified element(s).
10935     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10936     * or an array of any of those.
10937     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10938     * @return {CompositeElement} this
10939     */
10940     removeElement : function(el, removeDom){
10941         if(el instanceof Array){
10942             for(var i = 0, len = el.length; i < len; i++){
10943                 this.removeElement(el[i]);
10944             }
10945             return this;
10946         }
10947         var index = typeof el == 'number' ? el : this.indexOf(el);
10948         if(index !== -1){
10949             if(removeDom){
10950                 var d = this.elements[index];
10951                 if(d.dom){
10952                     d.remove();
10953                 }else{
10954                     d.parentNode.removeChild(d);
10955                 }
10956             }
10957             this.elements.splice(index, 1);
10958         }
10959         return this;
10960     },
10961
10962     /**
10963     * Replaces the specified element with the passed element.
10964     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10965     * to replace.
10966     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10967     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10968     * @return {CompositeElement} this
10969     */
10970     replaceElement : function(el, replacement, domReplace){
10971         var index = typeof el == 'number' ? el : this.indexOf(el);
10972         if(index !== -1){
10973             if(domReplace){
10974                 this.elements[index].replaceWith(replacement);
10975             }else{
10976                 this.elements.splice(index, 1, Roo.get(replacement))
10977             }
10978         }
10979         return this;
10980     },
10981
10982     /**
10983      * Removes all elements.
10984      */
10985     clear : function(){
10986         this.elements = [];
10987     }
10988 };
10989 (function(){
10990     Roo.CompositeElement.createCall = function(proto, fnName){
10991         if(!proto[fnName]){
10992             proto[fnName] = function(){
10993                 return this.invoke(fnName, arguments);
10994             };
10995         }
10996     };
10997     for(var fnName in Roo.Element.prototype){
10998         if(typeof Roo.Element.prototype[fnName] == "function"){
10999             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11000         }
11001     };
11002 })();
11003 /*
11004  * Based on:
11005  * Ext JS Library 1.1.1
11006  * Copyright(c) 2006-2007, Ext JS, LLC.
11007  *
11008  * Originally Released Under LGPL - original licence link has changed is not relivant.
11009  *
11010  * Fork - LGPL
11011  * <script type="text/javascript">
11012  */
11013
11014 /**
11015  * @class Roo.CompositeElementLite
11016  * @extends Roo.CompositeElement
11017  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11018  <pre><code>
11019  var els = Roo.select("#some-el div.some-class");
11020  // or select directly from an existing element
11021  var el = Roo.get('some-el');
11022  el.select('div.some-class');
11023
11024  els.setWidth(100); // all elements become 100 width
11025  els.hide(true); // all elements fade out and hide
11026  // or
11027  els.setWidth(100).hide(true);
11028  </code></pre><br><br>
11029  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11030  * actions will be performed on all the elements in this collection.</b>
11031  */
11032 Roo.CompositeElementLite = function(els){
11033     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11034     this.el = new Roo.Element.Flyweight();
11035 };
11036 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11037     addElements : function(els){
11038         if(els){
11039             if(els instanceof Array){
11040                 this.elements = this.elements.concat(els);
11041             }else{
11042                 var yels = this.elements;
11043                 var index = yels.length-1;
11044                 for(var i = 0, len = els.length; i < len; i++) {
11045                     yels[++index] = els[i];
11046                 }
11047             }
11048         }
11049         return this;
11050     },
11051     invoke : function(fn, args){
11052         var els = this.elements;
11053         var el = this.el;
11054         for(var i = 0, len = els.length; i < len; i++) {
11055             el.dom = els[i];
11056                 Roo.Element.prototype[fn].apply(el, args);
11057         }
11058         return this;
11059     },
11060     /**
11061      * Returns a flyweight Element of the dom element object at the specified index
11062      * @param {Number} index
11063      * @return {Roo.Element}
11064      */
11065     item : function(index){
11066         if(!this.elements[index]){
11067             return null;
11068         }
11069         this.el.dom = this.elements[index];
11070         return this.el;
11071     },
11072
11073     // fixes scope with flyweight
11074     addListener : function(eventName, handler, scope, opt){
11075         var els = this.elements;
11076         for(var i = 0, len = els.length; i < len; i++) {
11077             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11078         }
11079         return this;
11080     },
11081
11082     /**
11083     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11084     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11085     * a reference to the dom node, use el.dom.</b>
11086     * @param {Function} fn The function to call
11087     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11088     * @return {CompositeElement} this
11089     */
11090     each : function(fn, scope){
11091         var els = this.elements;
11092         var el = this.el;
11093         for(var i = 0, len = els.length; i < len; i++){
11094             el.dom = els[i];
11095                 if(fn.call(scope || el, el, this, i) === false){
11096                 break;
11097             }
11098         }
11099         return this;
11100     },
11101
11102     indexOf : function(el){
11103         return this.elements.indexOf(Roo.getDom(el));
11104     },
11105
11106     replaceElement : function(el, replacement, domReplace){
11107         var index = typeof el == 'number' ? el : this.indexOf(el);
11108         if(index !== -1){
11109             replacement = Roo.getDom(replacement);
11110             if(domReplace){
11111                 var d = this.elements[index];
11112                 d.parentNode.insertBefore(replacement, d);
11113                 d.parentNode.removeChild(d);
11114             }
11115             this.elements.splice(index, 1, replacement);
11116         }
11117         return this;
11118     }
11119 });
11120 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11121
11122 /*
11123  * Based on:
11124  * Ext JS Library 1.1.1
11125  * Copyright(c) 2006-2007, Ext JS, LLC.
11126  *
11127  * Originally Released Under LGPL - original licence link has changed is not relivant.
11128  *
11129  * Fork - LGPL
11130  * <script type="text/javascript">
11131  */
11132
11133  
11134
11135 /**
11136  * @class Roo.data.Connection
11137  * @extends Roo.util.Observable
11138  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11139  * either to a configured URL, or to a URL specified at request time.<br><br>
11140  * <p>
11141  * Requests made by this class are asynchronous, and will return immediately. No data from
11142  * the server will be available to the statement immediately following the {@link #request} call.
11143  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11144  * <p>
11145  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11146  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11147  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11148  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11149  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11150  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11151  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11152  * standard DOM methods.
11153  * @constructor
11154  * @param {Object} config a configuration object.
11155  */
11156 Roo.data.Connection = function(config){
11157     Roo.apply(this, config);
11158     this.addEvents({
11159         /**
11160          * @event beforerequest
11161          * Fires before a network request is made to retrieve a data object.
11162          * @param {Connection} conn This Connection object.
11163          * @param {Object} options The options config object passed to the {@link #request} method.
11164          */
11165         "beforerequest" : true,
11166         /**
11167          * @event requestcomplete
11168          * Fires if the request was successfully completed.
11169          * @param {Connection} conn This Connection object.
11170          * @param {Object} response The XHR object containing the response data.
11171          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11172          * @param {Object} options The options config object passed to the {@link #request} method.
11173          */
11174         "requestcomplete" : true,
11175         /**
11176          * @event requestexception
11177          * Fires if an error HTTP status was returned from the server.
11178          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11179          * @param {Connection} conn This Connection object.
11180          * @param {Object} response The XHR object containing the response data.
11181          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11182          * @param {Object} options The options config object passed to the {@link #request} method.
11183          */
11184         "requestexception" : true
11185     });
11186     Roo.data.Connection.superclass.constructor.call(this);
11187 };
11188
11189 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11190     /**
11191      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11192      */
11193     /**
11194      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11195      * extra parameters to each request made by this object. (defaults to undefined)
11196      */
11197     /**
11198      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11199      *  to each request made by this object. (defaults to undefined)
11200      */
11201     /**
11202      * @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)
11203      */
11204     /**
11205      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11206      */
11207     timeout : 30000,
11208     /**
11209      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11210      * @type Boolean
11211      */
11212     autoAbort:false,
11213
11214     /**
11215      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11216      * @type Boolean
11217      */
11218     disableCaching: true,
11219
11220     /**
11221      * Sends an HTTP request to a remote server.
11222      * @param {Object} options An object which may contain the following properties:<ul>
11223      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11224      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11225      * request, a url encoded string or a function to call to get either.</li>
11226      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11227      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11228      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11229      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11230      * <li>options {Object} The parameter to the request call.</li>
11231      * <li>success {Boolean} True if the request succeeded.</li>
11232      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11233      * </ul></li>
11234      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11235      * The callback is passed the following parameters:<ul>
11236      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11237      * <li>options {Object} The parameter to the request call.</li>
11238      * </ul></li>
11239      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11240      * The callback is passed the following parameters:<ul>
11241      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11242      * <li>options {Object} The parameter to the request call.</li>
11243      * </ul></li>
11244      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11245      * for the callback function. Defaults to the browser window.</li>
11246      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11247      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11248      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11249      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11250      * params for the post data. Any params will be appended to the URL.</li>
11251      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11252      * </ul>
11253      * @return {Number} transactionId
11254      */
11255     request : function(o){
11256         if(this.fireEvent("beforerequest", this, o) !== false){
11257             var p = o.params;
11258
11259             if(typeof p == "function"){
11260                 p = p.call(o.scope||window, o);
11261             }
11262             if(typeof p == "object"){
11263                 p = Roo.urlEncode(o.params);
11264             }
11265             if(this.extraParams){
11266                 var extras = Roo.urlEncode(this.extraParams);
11267                 p = p ? (p + '&' + extras) : extras;
11268             }
11269
11270             var url = o.url || this.url;
11271             if(typeof url == 'function'){
11272                 url = url.call(o.scope||window, o);
11273             }
11274
11275             if(o.form){
11276                 var form = Roo.getDom(o.form);
11277                 url = url || form.action;
11278
11279                 var enctype = form.getAttribute("enctype");
11280                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11281                     return this.doFormUpload(o, p, url);
11282                 }
11283                 var f = Roo.lib.Ajax.serializeForm(form);
11284                 p = p ? (p + '&' + f) : f;
11285             }
11286
11287             var hs = o.headers;
11288             if(this.defaultHeaders){
11289                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11290                 if(!o.headers){
11291                     o.headers = hs;
11292                 }
11293             }
11294
11295             var cb = {
11296                 success: this.handleResponse,
11297                 failure: this.handleFailure,
11298                 scope: this,
11299                 argument: {options: o},
11300                 timeout : this.timeout
11301             };
11302
11303             var method = o.method||this.method||(p ? "POST" : "GET");
11304
11305             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11306                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11307             }
11308
11309             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11310                 if(o.autoAbort){
11311                     this.abort();
11312                 }
11313             }else if(this.autoAbort !== false){
11314                 this.abort();
11315             }
11316
11317             if((method == 'GET' && p) || o.xmlData){
11318                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11319                 p = '';
11320             }
11321             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11322             return this.transId;
11323         }else{
11324             Roo.callback(o.callback, o.scope, [o, null, null]);
11325             return null;
11326         }
11327     },
11328
11329     /**
11330      * Determine whether this object has a request outstanding.
11331      * @param {Number} transactionId (Optional) defaults to the last transaction
11332      * @return {Boolean} True if there is an outstanding request.
11333      */
11334     isLoading : function(transId){
11335         if(transId){
11336             return Roo.lib.Ajax.isCallInProgress(transId);
11337         }else{
11338             return this.transId ? true : false;
11339         }
11340     },
11341
11342     /**
11343      * Aborts any outstanding request.
11344      * @param {Number} transactionId (Optional) defaults to the last transaction
11345      */
11346     abort : function(transId){
11347         if(transId || this.isLoading()){
11348             Roo.lib.Ajax.abort(transId || this.transId);
11349         }
11350     },
11351
11352     // private
11353     handleResponse : function(response){
11354         this.transId = false;
11355         var options = response.argument.options;
11356         response.argument = options ? options.argument : null;
11357         this.fireEvent("requestcomplete", this, response, options);
11358         Roo.callback(options.success, options.scope, [response, options]);
11359         Roo.callback(options.callback, options.scope, [options, true, response]);
11360     },
11361
11362     // private
11363     handleFailure : function(response, e){
11364         this.transId = false;
11365         var options = response.argument.options;
11366         response.argument = options ? options.argument : null;
11367         this.fireEvent("requestexception", this, response, options, e);
11368         Roo.callback(options.failure, options.scope, [response, options]);
11369         Roo.callback(options.callback, options.scope, [options, false, response]);
11370     },
11371
11372     // private
11373     doFormUpload : function(o, ps, url){
11374         var id = Roo.id();
11375         var frame = document.createElement('iframe');
11376         frame.id = id;
11377         frame.name = id;
11378         frame.className = 'x-hidden';
11379         if(Roo.isIE){
11380             frame.src = Roo.SSL_SECURE_URL;
11381         }
11382         document.body.appendChild(frame);
11383
11384         if(Roo.isIE){
11385            document.frames[id].name = id;
11386         }
11387
11388         var form = Roo.getDom(o.form);
11389         form.target = id;
11390         form.method = 'POST';
11391         form.enctype = form.encoding = 'multipart/form-data';
11392         if(url){
11393             form.action = url;
11394         }
11395
11396         var hiddens, hd;
11397         if(ps){ // add dynamic params
11398             hiddens = [];
11399             ps = Roo.urlDecode(ps, false);
11400             for(var k in ps){
11401                 if(ps.hasOwnProperty(k)){
11402                     hd = document.createElement('input');
11403                     hd.type = 'hidden';
11404                     hd.name = k;
11405                     hd.value = ps[k];
11406                     form.appendChild(hd);
11407                     hiddens.push(hd);
11408                 }
11409             }
11410         }
11411
11412         function cb(){
11413             var r = {  // bogus response object
11414                 responseText : '',
11415                 responseXML : null
11416             };
11417
11418             r.argument = o ? o.argument : null;
11419
11420             try { //
11421                 var doc;
11422                 if(Roo.isIE){
11423                     doc = frame.contentWindow.document;
11424                 }else {
11425                     doc = (frame.contentDocument || window.frames[id].document);
11426                 }
11427                 if(doc && doc.body){
11428                     r.responseText = doc.body.innerHTML;
11429                 }
11430                 if(doc && doc.XMLDocument){
11431                     r.responseXML = doc.XMLDocument;
11432                 }else {
11433                     r.responseXML = doc;
11434                 }
11435             }
11436             catch(e) {
11437                 // ignore
11438             }
11439
11440             Roo.EventManager.removeListener(frame, 'load', cb, this);
11441
11442             this.fireEvent("requestcomplete", this, r, o);
11443             Roo.callback(o.success, o.scope, [r, o]);
11444             Roo.callback(o.callback, o.scope, [o, true, r]);
11445
11446             setTimeout(function(){document.body.removeChild(frame);}, 100);
11447         }
11448
11449         Roo.EventManager.on(frame, 'load', cb, this);
11450         form.submit();
11451
11452         if(hiddens){ // remove dynamic params
11453             for(var i = 0, len = hiddens.length; i < len; i++){
11454                 form.removeChild(hiddens[i]);
11455             }
11456         }
11457     }
11458 });
11459
11460 /**
11461  * @class Roo.Ajax
11462  * @extends Roo.data.Connection
11463  * Global Ajax request class.
11464  *
11465  * @singleton
11466  */
11467 Roo.Ajax = new Roo.data.Connection({
11468     // fix up the docs
11469    /**
11470      * @cfg {String} url @hide
11471      */
11472     /**
11473      * @cfg {Object} extraParams @hide
11474      */
11475     /**
11476      * @cfg {Object} defaultHeaders @hide
11477      */
11478     /**
11479      * @cfg {String} method (Optional) @hide
11480      */
11481     /**
11482      * @cfg {Number} timeout (Optional) @hide
11483      */
11484     /**
11485      * @cfg {Boolean} autoAbort (Optional) @hide
11486      */
11487
11488     /**
11489      * @cfg {Boolean} disableCaching (Optional) @hide
11490      */
11491
11492     /**
11493      * @property  disableCaching
11494      * True to add a unique cache-buster param to GET requests. (defaults to true)
11495      * @type Boolean
11496      */
11497     /**
11498      * @property  url
11499      * The default URL to be used for requests to the server. (defaults to undefined)
11500      * @type String
11501      */
11502     /**
11503      * @property  extraParams
11504      * An object containing properties which are used as
11505      * extra parameters to each request made by this object. (defaults to undefined)
11506      * @type Object
11507      */
11508     /**
11509      * @property  defaultHeaders
11510      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11511      * @type Object
11512      */
11513     /**
11514      * @property  method
11515      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11516      * @type String
11517      */
11518     /**
11519      * @property  timeout
11520      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11521      * @type Number
11522      */
11523
11524     /**
11525      * @property  autoAbort
11526      * Whether a new request should abort any pending requests. (defaults to false)
11527      * @type Boolean
11528      */
11529     autoAbort : false,
11530
11531     /**
11532      * Serialize the passed form into a url encoded string
11533      * @param {String/HTMLElement} form
11534      * @return {String}
11535      */
11536     serializeForm : function(form){
11537         return Roo.lib.Ajax.serializeForm(form);
11538     }
11539 });/*
11540  * Based on:
11541  * Ext JS Library 1.1.1
11542  * Copyright(c) 2006-2007, Ext JS, LLC.
11543  *
11544  * Originally Released Under LGPL - original licence link has changed is not relivant.
11545  *
11546  * Fork - LGPL
11547  * <script type="text/javascript">
11548  */
11549  
11550 /**
11551  * @class Roo.Ajax
11552  * @extends Roo.data.Connection
11553  * Global Ajax request class.
11554  *
11555  * @instanceOf  Roo.data.Connection
11556  */
11557 Roo.Ajax = new Roo.data.Connection({
11558     // fix up the docs
11559     
11560     /**
11561      * fix up scoping
11562      * @scope Roo.Ajax
11563      */
11564     
11565    /**
11566      * @cfg {String} url @hide
11567      */
11568     /**
11569      * @cfg {Object} extraParams @hide
11570      */
11571     /**
11572      * @cfg {Object} defaultHeaders @hide
11573      */
11574     /**
11575      * @cfg {String} method (Optional) @hide
11576      */
11577     /**
11578      * @cfg {Number} timeout (Optional) @hide
11579      */
11580     /**
11581      * @cfg {Boolean} autoAbort (Optional) @hide
11582      */
11583
11584     /**
11585      * @cfg {Boolean} disableCaching (Optional) @hide
11586      */
11587
11588     /**
11589      * @property  disableCaching
11590      * True to add a unique cache-buster param to GET requests. (defaults to true)
11591      * @type Boolean
11592      */
11593     /**
11594      * @property  url
11595      * The default URL to be used for requests to the server. (defaults to undefined)
11596      * @type String
11597      */
11598     /**
11599      * @property  extraParams
11600      * An object containing properties which are used as
11601      * extra parameters to each request made by this object. (defaults to undefined)
11602      * @type Object
11603      */
11604     /**
11605      * @property  defaultHeaders
11606      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11607      * @type Object
11608      */
11609     /**
11610      * @property  method
11611      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11612      * @type String
11613      */
11614     /**
11615      * @property  timeout
11616      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11617      * @type Number
11618      */
11619
11620     /**
11621      * @property  autoAbort
11622      * Whether a new request should abort any pending requests. (defaults to false)
11623      * @type Boolean
11624      */
11625     autoAbort : false,
11626
11627     /**
11628      * Serialize the passed form into a url encoded string
11629      * @param {String/HTMLElement} form
11630      * @return {String}
11631      */
11632     serializeForm : function(form){
11633         return Roo.lib.Ajax.serializeForm(form);
11634     }
11635 });/*
11636  * Based on:
11637  * Ext JS Library 1.1.1
11638  * Copyright(c) 2006-2007, Ext JS, LLC.
11639  *
11640  * Originally Released Under LGPL - original licence link has changed is not relivant.
11641  *
11642  * Fork - LGPL
11643  * <script type="text/javascript">
11644  */
11645
11646  
11647 /**
11648  * @class Roo.UpdateManager
11649  * @extends Roo.util.Observable
11650  * Provides AJAX-style update for Element object.<br><br>
11651  * Usage:<br>
11652  * <pre><code>
11653  * // Get it from a Roo.Element object
11654  * var el = Roo.get("foo");
11655  * var mgr = el.getUpdateManager();
11656  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11657  * ...
11658  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11659  * <br>
11660  * // or directly (returns the same UpdateManager instance)
11661  * var mgr = new Roo.UpdateManager("myElementId");
11662  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11663  * mgr.on("update", myFcnNeedsToKnow);
11664  * <br>
11665    // short handed call directly from the element object
11666    Roo.get("foo").load({
11667         url: "bar.php",
11668         scripts:true,
11669         params: "for=bar",
11670         text: "Loading Foo..."
11671    });
11672  * </code></pre>
11673  * @constructor
11674  * Create new UpdateManager directly.
11675  * @param {String/HTMLElement/Roo.Element} el The element to update
11676  * @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).
11677  */
11678 Roo.UpdateManager = function(el, forceNew){
11679     el = Roo.get(el);
11680     if(!forceNew && el.updateManager){
11681         return el.updateManager;
11682     }
11683     /**
11684      * The Element object
11685      * @type Roo.Element
11686      */
11687     this.el = el;
11688     /**
11689      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11690      * @type String
11691      */
11692     this.defaultUrl = null;
11693
11694     this.addEvents({
11695         /**
11696          * @event beforeupdate
11697          * Fired before an update is made, return false from your handler and the update is cancelled.
11698          * @param {Roo.Element} el
11699          * @param {String/Object/Function} url
11700          * @param {String/Object} params
11701          */
11702         "beforeupdate": true,
11703         /**
11704          * @event update
11705          * Fired after successful update is made.
11706          * @param {Roo.Element} el
11707          * @param {Object} oResponseObject The response Object
11708          */
11709         "update": true,
11710         /**
11711          * @event failure
11712          * Fired on update failure.
11713          * @param {Roo.Element} el
11714          * @param {Object} oResponseObject The response Object
11715          */
11716         "failure": true
11717     });
11718     var d = Roo.UpdateManager.defaults;
11719     /**
11720      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11721      * @type String
11722      */
11723     this.sslBlankUrl = d.sslBlankUrl;
11724     /**
11725      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11726      * @type Boolean
11727      */
11728     this.disableCaching = d.disableCaching;
11729     /**
11730      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11731      * @type String
11732      */
11733     this.indicatorText = d.indicatorText;
11734     /**
11735      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11736      * @type String
11737      */
11738     this.showLoadIndicator = d.showLoadIndicator;
11739     /**
11740      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11741      * @type Number
11742      */
11743     this.timeout = d.timeout;
11744
11745     /**
11746      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11747      * @type Boolean
11748      */
11749     this.loadScripts = d.loadScripts;
11750
11751     /**
11752      * Transaction object of current executing transaction
11753      */
11754     this.transaction = null;
11755
11756     /**
11757      * @private
11758      */
11759     this.autoRefreshProcId = null;
11760     /**
11761      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11762      * @type Function
11763      */
11764     this.refreshDelegate = this.refresh.createDelegate(this);
11765     /**
11766      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11767      * @type Function
11768      */
11769     this.updateDelegate = this.update.createDelegate(this);
11770     /**
11771      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11772      * @type Function
11773      */
11774     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11775     /**
11776      * @private
11777      */
11778     this.successDelegate = this.processSuccess.createDelegate(this);
11779     /**
11780      * @private
11781      */
11782     this.failureDelegate = this.processFailure.createDelegate(this);
11783
11784     if(!this.renderer){
11785      /**
11786       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11787       */
11788     this.renderer = new Roo.UpdateManager.BasicRenderer();
11789     }
11790     
11791     Roo.UpdateManager.superclass.constructor.call(this);
11792 };
11793
11794 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11795     /**
11796      * Get the Element this UpdateManager is bound to
11797      * @return {Roo.Element} The element
11798      */
11799     getEl : function(){
11800         return this.el;
11801     },
11802     /**
11803      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11804      * @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:
11805 <pre><code>
11806 um.update({<br/>
11807     url: "your-url.php",<br/>
11808     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11809     callback: yourFunction,<br/>
11810     scope: yourObject, //(optional scope)  <br/>
11811     discardUrl: false, <br/>
11812     nocache: false,<br/>
11813     text: "Loading...",<br/>
11814     timeout: 30,<br/>
11815     scripts: false<br/>
11816 });
11817 </code></pre>
11818      * The only required property is url. The optional properties nocache, text and scripts
11819      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11820      * @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}
11821      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11822      * @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.
11823      */
11824     update : function(url, params, callback, discardUrl){
11825         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11826             var method = this.method, cfg;
11827             if(typeof url == "object"){ // must be config object
11828                 cfg = url;
11829                 url = cfg.url;
11830                 params = params || cfg.params;
11831                 callback = callback || cfg.callback;
11832                 discardUrl = discardUrl || cfg.discardUrl;
11833                 if(callback && cfg.scope){
11834                     callback = callback.createDelegate(cfg.scope);
11835                 }
11836                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11837                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11838                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11839                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11840                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11841             }
11842             this.showLoading();
11843             if(!discardUrl){
11844                 this.defaultUrl = url;
11845             }
11846             if(typeof url == "function"){
11847                 url = url.call(this);
11848             }
11849
11850             method = method || (params ? "POST" : "GET");
11851             if(method == "GET"){
11852                 url = this.prepareUrl(url);
11853             }
11854
11855             var o = Roo.apply(cfg ||{}, {
11856                 url : url,
11857                 params: params,
11858                 success: this.successDelegate,
11859                 failure: this.failureDelegate,
11860                 callback: undefined,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11863             });
11864
11865             this.transaction = Roo.Ajax.request(o);
11866         }
11867     },
11868
11869     /**
11870      * 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.
11871      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11872      * @param {String/HTMLElement} form The form Id or form element
11873      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11874      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11875      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11876      */
11877     formUpdate : function(form, url, reset, callback){
11878         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11879             if(typeof url == "function"){
11880                 url = url.call(this);
11881             }
11882             form = Roo.getDom(form);
11883             this.transaction = Roo.Ajax.request({
11884                 form: form,
11885                 url:url,
11886                 success: this.successDelegate,
11887                 failure: this.failureDelegate,
11888                 timeout: (this.timeout*1000),
11889                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11890             });
11891             this.showLoading.defer(1, this);
11892         }
11893     },
11894
11895     /**
11896      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11897      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11898      */
11899     refresh : function(callback){
11900         if(this.defaultUrl == null){
11901             return;
11902         }
11903         this.update(this.defaultUrl, null, callback, true);
11904     },
11905
11906     /**
11907      * Set this element to auto refresh.
11908      * @param {Number} interval How often to update (in seconds).
11909      * @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)
11910      * @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}
11911      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11912      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11913      */
11914     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11915         if(refreshNow){
11916             this.update(url || this.defaultUrl, params, callback, true);
11917         }
11918         if(this.autoRefreshProcId){
11919             clearInterval(this.autoRefreshProcId);
11920         }
11921         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11922     },
11923
11924     /**
11925      * Stop auto refresh on this element.
11926      */
11927      stopAutoRefresh : function(){
11928         if(this.autoRefreshProcId){
11929             clearInterval(this.autoRefreshProcId);
11930             delete this.autoRefreshProcId;
11931         }
11932     },
11933
11934     isAutoRefreshing : function(){
11935        return this.autoRefreshProcId ? true : false;
11936     },
11937     /**
11938      * Called to update the element to "Loading" state. Override to perform custom action.
11939      */
11940     showLoading : function(){
11941         if(this.showLoadIndicator){
11942             this.el.update(this.indicatorText);
11943         }
11944     },
11945
11946     /**
11947      * Adds unique parameter to query string if disableCaching = true
11948      * @private
11949      */
11950     prepareUrl : function(url){
11951         if(this.disableCaching){
11952             var append = "_dc=" + (new Date().getTime());
11953             if(url.indexOf("?") !== -1){
11954                 url += "&" + append;
11955             }else{
11956                 url += "?" + append;
11957             }
11958         }
11959         return url;
11960     },
11961
11962     /**
11963      * @private
11964      */
11965     processSuccess : function(response){
11966         this.transaction = null;
11967         if(response.argument.form && response.argument.reset){
11968             try{ // put in try/catch since some older FF releases had problems with this
11969                 response.argument.form.reset();
11970             }catch(e){}
11971         }
11972         if(this.loadScripts){
11973             this.renderer.render(this.el, response, this,
11974                 this.updateComplete.createDelegate(this, [response]));
11975         }else{
11976             this.renderer.render(this.el, response, this);
11977             this.updateComplete(response);
11978         }
11979     },
11980
11981     updateComplete : function(response){
11982         this.fireEvent("update", this.el, response);
11983         if(typeof response.argument.callback == "function"){
11984             response.argument.callback(this.el, true, response);
11985         }
11986     },
11987
11988     /**
11989      * @private
11990      */
11991     processFailure : function(response){
11992         this.transaction = null;
11993         this.fireEvent("failure", this.el, response);
11994         if(typeof response.argument.callback == "function"){
11995             response.argument.callback(this.el, false, response);
11996         }
11997     },
11998
11999     /**
12000      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12001      * @param {Object} renderer The object implementing the render() method
12002      */
12003     setRenderer : function(renderer){
12004         this.renderer = renderer;
12005     },
12006
12007     getRenderer : function(){
12008        return this.renderer;
12009     },
12010
12011     /**
12012      * Set the defaultUrl used for updates
12013      * @param {String/Function} defaultUrl The url or a function to call to get the url
12014      */
12015     setDefaultUrl : function(defaultUrl){
12016         this.defaultUrl = defaultUrl;
12017     },
12018
12019     /**
12020      * Aborts the executing transaction
12021      */
12022     abort : function(){
12023         if(this.transaction){
12024             Roo.Ajax.abort(this.transaction);
12025         }
12026     },
12027
12028     /**
12029      * Returns true if an update is in progress
12030      * @return {Boolean}
12031      */
12032     isUpdating : function(){
12033         if(this.transaction){
12034             return Roo.Ajax.isLoading(this.transaction);
12035         }
12036         return false;
12037     }
12038 });
12039
12040 /**
12041  * @class Roo.UpdateManager.defaults
12042  * @static (not really - but it helps the doc tool)
12043  * The defaults collection enables customizing the default properties of UpdateManager
12044  */
12045    Roo.UpdateManager.defaults = {
12046        /**
12047          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12048          * @type Number
12049          */
12050          timeout : 30,
12051
12052          /**
12053          * True to process scripts by default (Defaults to false).
12054          * @type Boolean
12055          */
12056         loadScripts : false,
12057
12058         /**
12059         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12060         * @type String
12061         */
12062         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12063         /**
12064          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12065          * @type Boolean
12066          */
12067         disableCaching : false,
12068         /**
12069          * Whether to show indicatorText when loading (Defaults to true).
12070          * @type Boolean
12071          */
12072         showLoadIndicator : true,
12073         /**
12074          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12075          * @type String
12076          */
12077         indicatorText : '<div class="loading-indicator">Loading...</div>'
12078    };
12079
12080 /**
12081  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12082  *Usage:
12083  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12084  * @param {String/HTMLElement/Roo.Element} el The element to update
12085  * @param {String} url The url
12086  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12087  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12088  * @static
12089  * @deprecated
12090  * @member Roo.UpdateManager
12091  */
12092 Roo.UpdateManager.updateElement = function(el, url, params, options){
12093     var um = Roo.get(el, true).getUpdateManager();
12094     Roo.apply(um, options);
12095     um.update(url, params, options ? options.callback : null);
12096 };
12097 // alias for backwards compat
12098 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12099 /**
12100  * @class Roo.UpdateManager.BasicRenderer
12101  * Default Content renderer. Updates the elements innerHTML with the responseText.
12102  */
12103 Roo.UpdateManager.BasicRenderer = function(){};
12104
12105 Roo.UpdateManager.BasicRenderer.prototype = {
12106     /**
12107      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12108      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12109      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12110      * @param {Roo.Element} el The element being rendered
12111      * @param {Object} response The YUI Connect response object
12112      * @param {UpdateManager} updateManager The calling update manager
12113      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12114      */
12115      render : function(el, response, updateManager, callback){
12116         el.update(response.responseText, updateManager.loadScripts, callback);
12117     }
12118 };
12119 /*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129
12130 /**
12131  * @class Roo.util.DelayedTask
12132  * Provides a convenient method of performing setTimeout where a new
12133  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12134  * You can use this class to buffer
12135  * the keypress events for a certain number of milliseconds, and perform only if they stop
12136  * for that amount of time.
12137  * @constructor The parameters to this constructor serve as defaults and are not required.
12138  * @param {Function} fn (optional) The default function to timeout
12139  * @param {Object} scope (optional) The default scope of that timeout
12140  * @param {Array} args (optional) The default Array of arguments
12141  */
12142 Roo.util.DelayedTask = function(fn, scope, args){
12143     var id = null, d, t;
12144
12145     var call = function(){
12146         var now = new Date().getTime();
12147         if(now - t >= d){
12148             clearInterval(id);
12149             id = null;
12150             fn.apply(scope, args || []);
12151         }
12152     };
12153     /**
12154      * Cancels any pending timeout and queues a new one
12155      * @param {Number} delay The milliseconds to delay
12156      * @param {Function} newFn (optional) Overrides function passed to constructor
12157      * @param {Object} newScope (optional) Overrides scope passed to constructor
12158      * @param {Array} newArgs (optional) Overrides args passed to constructor
12159      */
12160     this.delay = function(delay, newFn, newScope, newArgs){
12161         if(id && delay != d){
12162             this.cancel();
12163         }
12164         d = delay;
12165         t = new Date().getTime();
12166         fn = newFn || fn;
12167         scope = newScope || scope;
12168         args = newArgs || args;
12169         if(!id){
12170             id = setInterval(call, d);
12171         }
12172     };
12173
12174     /**
12175      * Cancel the last queued timeout
12176      */
12177     this.cancel = function(){
12178         if(id){
12179             clearInterval(id);
12180             id = null;
12181         }
12182     };
12183 };/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193  
12194  
12195 Roo.util.TaskRunner = function(interval){
12196     interval = interval || 10;
12197     var tasks = [], removeQueue = [];
12198     var id = 0;
12199     var running = false;
12200
12201     var stopThread = function(){
12202         running = false;
12203         clearInterval(id);
12204         id = 0;
12205     };
12206
12207     var startThread = function(){
12208         if(!running){
12209             running = true;
12210             id = setInterval(runTasks, interval);
12211         }
12212     };
12213
12214     var removeTask = function(task){
12215         removeQueue.push(task);
12216         if(task.onStop){
12217             task.onStop();
12218         }
12219     };
12220
12221     var runTasks = function(){
12222         if(removeQueue.length > 0){
12223             for(var i = 0, len = removeQueue.length; i < len; i++){
12224                 tasks.remove(removeQueue[i]);
12225             }
12226             removeQueue = [];
12227             if(tasks.length < 1){
12228                 stopThread();
12229                 return;
12230             }
12231         }
12232         var now = new Date().getTime();
12233         for(var i = 0, len = tasks.length; i < len; ++i){
12234             var t = tasks[i];
12235             var itime = now - t.taskRunTime;
12236             if(t.interval <= itime){
12237                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12238                 t.taskRunTime = now;
12239                 if(rt === false || t.taskRunCount === t.repeat){
12240                     removeTask(t);
12241                     return;
12242                 }
12243             }
12244             if(t.duration && t.duration <= (now - t.taskStartTime)){
12245                 removeTask(t);
12246             }
12247         }
12248     };
12249
12250     /**
12251      * Queues a new task.
12252      * @param {Object} task
12253      */
12254     this.start = function(task){
12255         tasks.push(task);
12256         task.taskStartTime = new Date().getTime();
12257         task.taskRunTime = 0;
12258         task.taskRunCount = 0;
12259         startThread();
12260         return task;
12261     };
12262
12263     this.stop = function(task){
12264         removeTask(task);
12265         return task;
12266     };
12267
12268     this.stopAll = function(){
12269         stopThread();
12270         for(var i = 0, len = tasks.length; i < len; i++){
12271             if(tasks[i].onStop){
12272                 tasks[i].onStop();
12273             }
12274         }
12275         tasks = [];
12276         removeQueue = [];
12277     };
12278 };
12279
12280 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12281  * Based on:
12282  * Ext JS Library 1.1.1
12283  * Copyright(c) 2006-2007, Ext JS, LLC.
12284  *
12285  * Originally Released Under LGPL - original licence link has changed is not relivant.
12286  *
12287  * Fork - LGPL
12288  * <script type="text/javascript">
12289  */
12290
12291  
12292 /**
12293  * @class Roo.util.MixedCollection
12294  * @extends Roo.util.Observable
12295  * A Collection class that maintains both numeric indexes and keys and exposes events.
12296  * @constructor
12297  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12298  * collection (defaults to false)
12299  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12300  * and return the key value for that item.  This is used when available to look up the key on items that
12301  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12302  * equivalent to providing an implementation for the {@link #getKey} method.
12303  */
12304 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12305     this.items = [];
12306     this.map = {};
12307     this.keys = [];
12308     this.length = 0;
12309     this.addEvents({
12310         /**
12311          * @event clear
12312          * Fires when the collection is cleared.
12313          */
12314         "clear" : true,
12315         /**
12316          * @event add
12317          * Fires when an item is added to the collection.
12318          * @param {Number} index The index at which the item was added.
12319          * @param {Object} o The item added.
12320          * @param {String} key The key associated with the added item.
12321          */
12322         "add" : true,
12323         /**
12324          * @event replace
12325          * Fires when an item is replaced in the collection.
12326          * @param {String} key he key associated with the new added.
12327          * @param {Object} old The item being replaced.
12328          * @param {Object} new The new item.
12329          */
12330         "replace" : true,
12331         /**
12332          * @event remove
12333          * Fires when an item is removed from the collection.
12334          * @param {Object} o The item being removed.
12335          * @param {String} key (optional) The key associated with the removed item.
12336          */
12337         "remove" : true,
12338         "sort" : true
12339     });
12340     this.allowFunctions = allowFunctions === true;
12341     if(keyFn){
12342         this.getKey = keyFn;
12343     }
12344     Roo.util.MixedCollection.superclass.constructor.call(this);
12345 };
12346
12347 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12348     allowFunctions : false,
12349     
12350 /**
12351  * Adds an item to the collection.
12352  * @param {String} key The key to associate with the item
12353  * @param {Object} o The item to add.
12354  * @return {Object} The item added.
12355  */
12356     add : function(key, o){
12357         if(arguments.length == 1){
12358             o = arguments[0];
12359             key = this.getKey(o);
12360         }
12361         if(typeof key == "undefined" || key === null){
12362             this.length++;
12363             this.items.push(o);
12364             this.keys.push(null);
12365         }else{
12366             var old = this.map[key];
12367             if(old){
12368                 return this.replace(key, o);
12369             }
12370             this.length++;
12371             this.items.push(o);
12372             this.map[key] = o;
12373             this.keys.push(key);
12374         }
12375         this.fireEvent("add", this.length-1, o, key);
12376         return o;
12377     },
12378        
12379 /**
12380   * MixedCollection has a generic way to fetch keys if you implement getKey.
12381 <pre><code>
12382 // normal way
12383 var mc = new Roo.util.MixedCollection();
12384 mc.add(someEl.dom.id, someEl);
12385 mc.add(otherEl.dom.id, otherEl);
12386 //and so on
12387
12388 // using getKey
12389 var mc = new Roo.util.MixedCollection();
12390 mc.getKey = function(el){
12391    return el.dom.id;
12392 };
12393 mc.add(someEl);
12394 mc.add(otherEl);
12395
12396 // or via the constructor
12397 var mc = new Roo.util.MixedCollection(false, function(el){
12398    return el.dom.id;
12399 });
12400 mc.add(someEl);
12401 mc.add(otherEl);
12402 </code></pre>
12403  * @param o {Object} The item for which to find the key.
12404  * @return {Object} The key for the passed item.
12405  */
12406     getKey : function(o){
12407          return o.id; 
12408     },
12409    
12410 /**
12411  * Replaces an item in the collection.
12412  * @param {String} key The key associated with the item to replace, or the item to replace.
12413  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12414  * @return {Object}  The new item.
12415  */
12416     replace : function(key, o){
12417         if(arguments.length == 1){
12418             o = arguments[0];
12419             key = this.getKey(o);
12420         }
12421         var old = this.item(key);
12422         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12423              return this.add(key, o);
12424         }
12425         var index = this.indexOfKey(key);
12426         this.items[index] = o;
12427         this.map[key] = o;
12428         this.fireEvent("replace", key, old, o);
12429         return o;
12430     },
12431    
12432 /**
12433  * Adds all elements of an Array or an Object to the collection.
12434  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12435  * an Array of values, each of which are added to the collection.
12436  */
12437     addAll : function(objs){
12438         if(arguments.length > 1 || objs instanceof Array){
12439             var args = arguments.length > 1 ? arguments : objs;
12440             for(var i = 0, len = args.length; i < len; i++){
12441                 this.add(args[i]);
12442             }
12443         }else{
12444             for(var key in objs){
12445                 if(this.allowFunctions || typeof objs[key] != "function"){
12446                     this.add(key, objs[key]);
12447                 }
12448             }
12449         }
12450     },
12451    
12452 /**
12453  * Executes the specified function once for every item in the collection, passing each
12454  * item as the first and only parameter. returning false from the function will stop the iteration.
12455  * @param {Function} fn The function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  */
12458     each : function(fn, scope){
12459         var items = [].concat(this.items); // each safe for removal
12460         for(var i = 0, len = items.length; i < len; i++){
12461             if(fn.call(scope || items[i], items[i], i, len) === false){
12462                 break;
12463             }
12464         }
12465     },
12466    
12467 /**
12468  * Executes the specified function once for every key in the collection, passing each
12469  * key, and its associated item as the first two parameters.
12470  * @param {Function} fn The function to execute for each item.
12471  * @param {Object} scope (optional) The scope in which to execute the function.
12472  */
12473     eachKey : function(fn, scope){
12474         for(var i = 0, len = this.keys.length; i < len; i++){
12475             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12476         }
12477     },
12478    
12479 /**
12480  * Returns the first item in the collection which elicits a true return value from the
12481  * passed selection function.
12482  * @param {Function} fn The selection function to execute for each item.
12483  * @param {Object} scope (optional) The scope in which to execute the function.
12484  * @return {Object} The first item in the collection which returned true from the selection function.
12485  */
12486     find : function(fn, scope){
12487         for(var i = 0, len = this.items.length; i < len; i++){
12488             if(fn.call(scope || window, this.items[i], this.keys[i])){
12489                 return this.items[i];
12490             }
12491         }
12492         return null;
12493     },
12494    
12495 /**
12496  * Inserts an item at the specified index in the collection.
12497  * @param {Number} index The index to insert the item at.
12498  * @param {String} key The key to associate with the new item, or the item itself.
12499  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12500  * @return {Object} The item inserted.
12501  */
12502     insert : function(index, key, o){
12503         if(arguments.length == 2){
12504             o = arguments[1];
12505             key = this.getKey(o);
12506         }
12507         if(index >= this.length){
12508             return this.add(key, o);
12509         }
12510         this.length++;
12511         this.items.splice(index, 0, o);
12512         if(typeof key != "undefined" && key != null){
12513             this.map[key] = o;
12514         }
12515         this.keys.splice(index, 0, key);
12516         this.fireEvent("add", index, o, key);
12517         return o;
12518     },
12519    
12520 /**
12521  * Removed an item from the collection.
12522  * @param {Object} o The item to remove.
12523  * @return {Object} The item removed.
12524  */
12525     remove : function(o){
12526         return this.removeAt(this.indexOf(o));
12527     },
12528    
12529 /**
12530  * Remove an item from a specified index in the collection.
12531  * @param {Number} index The index within the collection of the item to remove.
12532  */
12533     removeAt : function(index){
12534         if(index < this.length && index >= 0){
12535             this.length--;
12536             var o = this.items[index];
12537             this.items.splice(index, 1);
12538             var key = this.keys[index];
12539             if(typeof key != "undefined"){
12540                 delete this.map[key];
12541             }
12542             this.keys.splice(index, 1);
12543             this.fireEvent("remove", o, key);
12544         }
12545     },
12546    
12547 /**
12548  * Removed an item associated with the passed key fom the collection.
12549  * @param {String} key The key of the item to remove.
12550  */
12551     removeKey : function(key){
12552         return this.removeAt(this.indexOfKey(key));
12553     },
12554    
12555 /**
12556  * Returns the number of items in the collection.
12557  * @return {Number} the number of items in the collection.
12558  */
12559     getCount : function(){
12560         return this.length; 
12561     },
12562    
12563 /**
12564  * Returns index within the collection of the passed Object.
12565  * @param {Object} o The item to find the index of.
12566  * @return {Number} index of the item.
12567  */
12568     indexOf : function(o){
12569         if(!this.items.indexOf){
12570             for(var i = 0, len = this.items.length; i < len; i++){
12571                 if(this.items[i] == o) return i;
12572             }
12573             return -1;
12574         }else{
12575             return this.items.indexOf(o);
12576         }
12577     },
12578    
12579 /**
12580  * Returns index within the collection of the passed key.
12581  * @param {String} key The key to find the index of.
12582  * @return {Number} index of the key.
12583  */
12584     indexOfKey : function(key){
12585         if(!this.keys.indexOf){
12586             for(var i = 0, len = this.keys.length; i < len; i++){
12587                 if(this.keys[i] == key) return i;
12588             }
12589             return -1;
12590         }else{
12591             return this.keys.indexOf(key);
12592         }
12593     },
12594    
12595 /**
12596  * Returns the item associated with the passed key OR index. Key has priority over index.
12597  * @param {String/Number} key The key or index of the item.
12598  * @return {Object} The item associated with the passed key.
12599  */
12600     item : function(key){
12601         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12602         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12603     },
12604     
12605 /**
12606  * Returns the item at the specified index.
12607  * @param {Number} index The index of the item.
12608  * @return {Object}
12609  */
12610     itemAt : function(index){
12611         return this.items[index];
12612     },
12613     
12614 /**
12615  * Returns the item associated with the passed key.
12616  * @param {String/Number} key The key of the item.
12617  * @return {Object} The item associated with the passed key.
12618  */
12619     key : function(key){
12620         return this.map[key];
12621     },
12622    
12623 /**
12624  * Returns true if the collection contains the passed Object as an item.
12625  * @param {Object} o  The Object to look for in the collection.
12626  * @return {Boolean} True if the collection contains the Object as an item.
12627  */
12628     contains : function(o){
12629         return this.indexOf(o) != -1;
12630     },
12631    
12632 /**
12633  * Returns true if the collection contains the passed Object as a key.
12634  * @param {String} key The key to look for in the collection.
12635  * @return {Boolean} True if the collection contains the Object as a key.
12636  */
12637     containsKey : function(key){
12638         return typeof this.map[key] != "undefined";
12639     },
12640    
12641 /**
12642  * Removes all items from the collection.
12643  */
12644     clear : function(){
12645         this.length = 0;
12646         this.items = [];
12647         this.keys = [];
12648         this.map = {};
12649         this.fireEvent("clear");
12650     },
12651    
12652 /**
12653  * Returns the first item in the collection.
12654  * @return {Object} the first item in the collection..
12655  */
12656     first : function(){
12657         return this.items[0]; 
12658     },
12659    
12660 /**
12661  * Returns the last item in the collection.
12662  * @return {Object} the last item in the collection..
12663  */
12664     last : function(){
12665         return this.items[this.length-1];   
12666     },
12667     
12668     _sort : function(property, dir, fn){
12669         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12670         fn = fn || function(a, b){
12671             return a-b;
12672         };
12673         var c = [], k = this.keys, items = this.items;
12674         for(var i = 0, len = items.length; i < len; i++){
12675             c[c.length] = {key: k[i], value: items[i], index: i};
12676         }
12677         c.sort(function(a, b){
12678             var v = fn(a[property], b[property]) * dsc;
12679             if(v == 0){
12680                 v = (a.index < b.index ? -1 : 1);
12681             }
12682             return v;
12683         });
12684         for(var i = 0, len = c.length; i < len; i++){
12685             items[i] = c[i].value;
12686             k[i] = c[i].key;
12687         }
12688         this.fireEvent("sort", this);
12689     },
12690     
12691     /**
12692      * Sorts this collection with the passed comparison function
12693      * @param {String} direction (optional) "ASC" or "DESC"
12694      * @param {Function} fn (optional) comparison function
12695      */
12696     sort : function(dir, fn){
12697         this._sort("value", dir, fn);
12698     },
12699     
12700     /**
12701      * Sorts this collection by keys
12702      * @param {String} direction (optional) "ASC" or "DESC"
12703      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12704      */
12705     keySort : function(dir, fn){
12706         this._sort("key", dir, fn || function(a, b){
12707             return String(a).toUpperCase()-String(b).toUpperCase();
12708         });
12709     },
12710     
12711     /**
12712      * Returns a range of items in this collection
12713      * @param {Number} startIndex (optional) defaults to 0
12714      * @param {Number} endIndex (optional) default to the last item
12715      * @return {Array} An array of items
12716      */
12717     getRange : function(start, end){
12718         var items = this.items;
12719         if(items.length < 1){
12720             return [];
12721         }
12722         start = start || 0;
12723         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12724         var r = [];
12725         if(start <= end){
12726             for(var i = start; i <= end; i++) {
12727                     r[r.length] = items[i];
12728             }
12729         }else{
12730             for(var i = start; i >= end; i--) {
12731                     r[r.length] = items[i];
12732             }
12733         }
12734         return r;
12735     },
12736         
12737     /**
12738      * Filter the <i>objects</i> in this collection by a specific property. 
12739      * Returns a new collection that has been filtered.
12740      * @param {String} property A property on your objects
12741      * @param {String/RegExp} value Either string that the property values 
12742      * should start with or a RegExp to test against the property
12743      * @return {MixedCollection} The new filtered collection
12744      */
12745     filter : function(property, value){
12746         if(!value.exec){ // not a regex
12747             value = String(value);
12748             if(value.length == 0){
12749                 return this.clone();
12750             }
12751             value = new RegExp("^" + Roo.escapeRe(value), "i");
12752         }
12753         return this.filterBy(function(o){
12754             return o && value.test(o[property]);
12755         });
12756         },
12757     
12758     /**
12759      * Filter by a function. * Returns a new collection that has been filtered.
12760      * The passed function will be called with each 
12761      * object in the collection. If the function returns true, the value is included 
12762      * otherwise it is filtered.
12763      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12764      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12765      * @return {MixedCollection} The new filtered collection
12766      */
12767     filterBy : function(fn, scope){
12768         var r = new Roo.util.MixedCollection();
12769         r.getKey = this.getKey;
12770         var k = this.keys, it = this.items;
12771         for(var i = 0, len = it.length; i < len; i++){
12772             if(fn.call(scope||this, it[i], k[i])){
12773                                 r.add(k[i], it[i]);
12774                         }
12775         }
12776         return r;
12777     },
12778     
12779     /**
12780      * Creates a duplicate of this collection
12781      * @return {MixedCollection}
12782      */
12783     clone : function(){
12784         var r = new Roo.util.MixedCollection();
12785         var k = this.keys, it = this.items;
12786         for(var i = 0, len = it.length; i < len; i++){
12787             r.add(k[i], it[i]);
12788         }
12789         r.getKey = this.getKey;
12790         return r;
12791     }
12792 });
12793 /**
12794  * Returns the item associated with the passed key or index.
12795  * @method
12796  * @param {String/Number} key The key or index of the item.
12797  * @return {Object} The item associated with the passed key.
12798  */
12799 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809 /**
12810  * @class Roo.util.JSON
12811  * Modified version of Douglas Crockford"s json.js that doesn"t
12812  * mess with the Object prototype 
12813  * http://www.json.org/js.html
12814  * @singleton
12815  */
12816 Roo.util.JSON = new (function(){
12817     var useHasOwn = {}.hasOwnProperty ? true : false;
12818     
12819     // crashes Safari in some instances
12820     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12821     
12822     var pad = function(n) {
12823         return n < 10 ? "0" + n : n;
12824     };
12825     
12826     var m = {
12827         "\b": '\\b',
12828         "\t": '\\t',
12829         "\n": '\\n',
12830         "\f": '\\f',
12831         "\r": '\\r',
12832         '"' : '\\"',
12833         "\\": '\\\\'
12834     };
12835
12836     var encodeString = function(s){
12837         if (/["\\\x00-\x1f]/.test(s)) {
12838             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12839                 var c = m[b];
12840                 if(c){
12841                     return c;
12842                 }
12843                 c = b.charCodeAt();
12844                 return "\\u00" +
12845                     Math.floor(c / 16).toString(16) +
12846                     (c % 16).toString(16);
12847             }) + '"';
12848         }
12849         return '"' + s + '"';
12850     };
12851     
12852     var encodeArray = function(o){
12853         var a = ["["], b, i, l = o.length, v;
12854             for (i = 0; i < l; i += 1) {
12855                 v = o[i];
12856                 switch (typeof v) {
12857                     case "undefined":
12858                     case "function":
12859                     case "unknown":
12860                         break;
12861                     default:
12862                         if (b) {
12863                             a.push(',');
12864                         }
12865                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12866                         b = true;
12867                 }
12868             }
12869             a.push("]");
12870             return a.join("");
12871     };
12872     
12873     var encodeDate = function(o){
12874         return '"' + o.getFullYear() + "-" +
12875                 pad(o.getMonth() + 1) + "-" +
12876                 pad(o.getDate()) + "T" +
12877                 pad(o.getHours()) + ":" +
12878                 pad(o.getMinutes()) + ":" +
12879                 pad(o.getSeconds()) + '"';
12880     };
12881     
12882     /**
12883      * Encodes an Object, Array or other value
12884      * @param {Mixed} o The variable to encode
12885      * @return {String} The JSON string
12886      */
12887     this.encode = function(o)
12888     {
12889         // should this be extended to fully wrap stringify..
12890         
12891         if(typeof o == "undefined" || o === null){
12892             return "null";
12893         }else if(o instanceof Array){
12894             return encodeArray(o);
12895         }else if(o instanceof Date){
12896             return encodeDate(o);
12897         }else if(typeof o == "string"){
12898             return encodeString(o);
12899         }else if(typeof o == "number"){
12900             return isFinite(o) ? String(o) : "null";
12901         }else if(typeof o == "boolean"){
12902             return String(o);
12903         }else {
12904             var a = ["{"], b, i, v;
12905             for (i in o) {
12906                 if(!useHasOwn || o.hasOwnProperty(i)) {
12907                     v = o[i];
12908                     switch (typeof v) {
12909                     case "undefined":
12910                     case "function":
12911                     case "unknown":
12912                         break;
12913                     default:
12914                         if(b){
12915                             a.push(',');
12916                         }
12917                         a.push(this.encode(i), ":",
12918                                 v === null ? "null" : this.encode(v));
12919                         b = true;
12920                     }
12921                 }
12922             }
12923             a.push("}");
12924             return a.join("");
12925         }
12926     };
12927     
12928     /**
12929      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12930      * @param {String} json The JSON string
12931      * @return {Object} The resulting object
12932      */
12933     this.decode = function(json){
12934         
12935         return  /** eval:var:json */ eval("(" + json + ')');
12936     };
12937 })();
12938 /** 
12939  * Shorthand for {@link Roo.util.JSON#encode}
12940  * @member Roo encode 
12941  * @method */
12942 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12943 /** 
12944  * Shorthand for {@link Roo.util.JSON#decode}
12945  * @member Roo decode 
12946  * @method */
12947 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12948 /*
12949  * Based on:
12950  * Ext JS Library 1.1.1
12951  * Copyright(c) 2006-2007, Ext JS, LLC.
12952  *
12953  * Originally Released Under LGPL - original licence link has changed is not relivant.
12954  *
12955  * Fork - LGPL
12956  * <script type="text/javascript">
12957  */
12958  
12959 /**
12960  * @class Roo.util.Format
12961  * Reusable data formatting functions
12962  * @singleton
12963  */
12964 Roo.util.Format = function(){
12965     var trimRe = /^\s+|\s+$/g;
12966     return {
12967         /**
12968          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12969          * @param {String} value The string to truncate
12970          * @param {Number} length The maximum length to allow before truncating
12971          * @return {String} The converted text
12972          */
12973         ellipsis : function(value, len){
12974             if(value && value.length > len){
12975                 return value.substr(0, len-3)+"...";
12976             }
12977             return value;
12978         },
12979
12980         /**
12981          * Checks a reference and converts it to empty string if it is undefined
12982          * @param {Mixed} value Reference to check
12983          * @return {Mixed} Empty string if converted, otherwise the original value
12984          */
12985         undef : function(value){
12986             return typeof value != "undefined" ? value : "";
12987         },
12988
12989         /**
12990          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12991          * @param {String} value The string to encode
12992          * @return {String} The encoded text
12993          */
12994         htmlEncode : function(value){
12995             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12996         },
12997
12998         /**
12999          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13000          * @param {String} value The string to decode
13001          * @return {String} The decoded text
13002          */
13003         htmlDecode : function(value){
13004             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13005         },
13006
13007         /**
13008          * Trims any whitespace from either side of a string
13009          * @param {String} value The text to trim
13010          * @return {String} The trimmed text
13011          */
13012         trim : function(value){
13013             return String(value).replace(trimRe, "");
13014         },
13015
13016         /**
13017          * Returns a substring from within an original string
13018          * @param {String} value The original text
13019          * @param {Number} start The start index of the substring
13020          * @param {Number} length The length of the substring
13021          * @return {String} The substring
13022          */
13023         substr : function(value, start, length){
13024             return String(value).substr(start, length);
13025         },
13026
13027         /**
13028          * Converts a string to all lower case letters
13029          * @param {String} value The text to convert
13030          * @return {String} The converted text
13031          */
13032         lowercase : function(value){
13033             return String(value).toLowerCase();
13034         },
13035
13036         /**
13037          * Converts a string to all upper case letters
13038          * @param {String} value The text to convert
13039          * @return {String} The converted text
13040          */
13041         uppercase : function(value){
13042             return String(value).toUpperCase();
13043         },
13044
13045         /**
13046          * Converts the first character only of a string to upper case
13047          * @param {String} value The text to convert
13048          * @return {String} The converted text
13049          */
13050         capitalize : function(value){
13051             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13052         },
13053
13054         // private
13055         call : function(value, fn){
13056             if(arguments.length > 2){
13057                 var args = Array.prototype.slice.call(arguments, 2);
13058                 args.unshift(value);
13059                  
13060                 return /** eval:var:value */  eval(fn).apply(window, args);
13061             }else{
13062                 /** eval:var:value */
13063                 return /** eval:var:value */ eval(fn).call(window, value);
13064             }
13065         },
13066
13067        
13068         /**
13069          * safer version of Math.toFixed..??/
13070          * @param {Number/String} value The numeric value to format
13071          * @param {Number/String} value Decimal places 
13072          * @return {String} The formatted currency string
13073          */
13074         toFixed : function(v, n)
13075         {
13076             // why not use to fixed - precision is buggered???
13077             if (!n) {
13078                 return Math.round(v-0);
13079             }
13080             var fact = Math.pow(10,n+1);
13081             v = (Math.round((v-0)*fact))/fact;
13082             var z = (''+fact).substring(2);
13083             if (v == Math.floor(v)) {
13084                 return Math.floor(v) + '.' + z;
13085             }
13086             
13087             // now just padd decimals..
13088             var ps = String(v).split('.');
13089             var fd = (ps[1] + z);
13090             var r = fd.substring(0,n); 
13091             var rm = fd.substring(n); 
13092             if (rm < 5) {
13093                 return ps[0] + '.' + r;
13094             }
13095             r*=1; // turn it into a number;
13096             r++;
13097             if (String(r).length != n) {
13098                 ps[0]*=1;
13099                 ps[0]++;
13100                 r = String(r).substring(1); // chop the end off.
13101             }
13102             
13103             return ps[0] + '.' + r;
13104              
13105         },
13106         
13107         /**
13108          * Format a number as US currency
13109          * @param {Number/String} value The numeric value to format
13110          * @return {String} The formatted currency string
13111          */
13112         usMoney : function(v){
13113             v = (Math.round((v-0)*100))/100;
13114             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13115             v = String(v);
13116             var ps = v.split('.');
13117             var whole = ps[0];
13118             var sub = ps[1] ? '.'+ ps[1] : '.00';
13119             var r = /(\d+)(\d{3})/;
13120             while (r.test(whole)) {
13121                 whole = whole.replace(r, '$1' + ',' + '$2');
13122             }
13123             return "$" + whole + sub ;
13124         },
13125         
13126         /**
13127          * Parse a value into a formatted date using the specified format pattern.
13128          * @param {Mixed} value The value to format
13129          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13130          * @return {String} The formatted date string
13131          */
13132         date : function(v, format){
13133             if(!v){
13134                 return "";
13135             }
13136             if(!(v instanceof Date)){
13137                 v = new Date(Date.parse(v));
13138             }
13139             return v.dateFormat(format || "m/d/Y");
13140         },
13141
13142         /**
13143          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13144          * @param {String} format Any valid date format string
13145          * @return {Function} The date formatting function
13146          */
13147         dateRenderer : function(format){
13148             return function(v){
13149                 return Roo.util.Format.date(v, format);  
13150             };
13151         },
13152
13153         // private
13154         stripTagsRE : /<\/?[^>]+>/gi,
13155         
13156         /**
13157          * Strips all HTML tags
13158          * @param {Mixed} value The text from which to strip tags
13159          * @return {String} The stripped text
13160          */
13161         stripTags : function(v){
13162             return !v ? v : String(v).replace(this.stripTagsRE, "");
13163         }
13164     };
13165 }();/*
13166  * Based on:
13167  * Ext JS Library 1.1.1
13168  * Copyright(c) 2006-2007, Ext JS, LLC.
13169  *
13170  * Originally Released Under LGPL - original licence link has changed is not relivant.
13171  *
13172  * Fork - LGPL
13173  * <script type="text/javascript">
13174  */
13175
13176
13177  
13178
13179 /**
13180  * @class Roo.MasterTemplate
13181  * @extends Roo.Template
13182  * Provides a template that can have child templates. The syntax is:
13183 <pre><code>
13184 var t = new Roo.MasterTemplate(
13185         '&lt;select name="{name}"&gt;',
13186                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13187         '&lt;/select&gt;'
13188 );
13189 t.add('options', {value: 'foo', text: 'bar'});
13190 // or you can add multiple child elements in one shot
13191 t.addAll('options', [
13192     {value: 'foo', text: 'bar'},
13193     {value: 'foo2', text: 'bar2'},
13194     {value: 'foo3', text: 'bar3'}
13195 ]);
13196 // then append, applying the master template values
13197 t.append('my-form', {name: 'my-select'});
13198 </code></pre>
13199 * A name attribute for the child template is not required if you have only one child
13200 * template or you want to refer to them by index.
13201  */
13202 Roo.MasterTemplate = function(){
13203     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13204     this.originalHtml = this.html;
13205     var st = {};
13206     var m, re = this.subTemplateRe;
13207     re.lastIndex = 0;
13208     var subIndex = 0;
13209     while(m = re.exec(this.html)){
13210         var name = m[1], content = m[2];
13211         st[subIndex] = {
13212             name: name,
13213             index: subIndex,
13214             buffer: [],
13215             tpl : new Roo.Template(content)
13216         };
13217         if(name){
13218             st[name] = st[subIndex];
13219         }
13220         st[subIndex].tpl.compile();
13221         st[subIndex].tpl.call = this.call.createDelegate(this);
13222         subIndex++;
13223     }
13224     this.subCount = subIndex;
13225     this.subs = st;
13226 };
13227 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13228     /**
13229     * The regular expression used to match sub templates
13230     * @type RegExp
13231     * @property
13232     */
13233     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13234
13235     /**
13236      * Applies the passed values to a child template.
13237      * @param {String/Number} name (optional) The name or index of the child template
13238      * @param {Array/Object} values The values to be applied to the template
13239      * @return {MasterTemplate} this
13240      */
13241      add : function(name, values){
13242         if(arguments.length == 1){
13243             values = arguments[0];
13244             name = 0;
13245         }
13246         var s = this.subs[name];
13247         s.buffer[s.buffer.length] = s.tpl.apply(values);
13248         return this;
13249     },
13250
13251     /**
13252      * Applies all the passed values to a child template.
13253      * @param {String/Number} name (optional) The name or index of the child template
13254      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13255      * @param {Boolean} reset (optional) True to reset the template first
13256      * @return {MasterTemplate} this
13257      */
13258     fill : function(name, values, reset){
13259         var a = arguments;
13260         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13261             values = a[0];
13262             name = 0;
13263             reset = a[1];
13264         }
13265         if(reset){
13266             this.reset();
13267         }
13268         for(var i = 0, len = values.length; i < len; i++){
13269             this.add(name, values[i]);
13270         }
13271         return this;
13272     },
13273
13274     /**
13275      * Resets the template for reuse
13276      * @return {MasterTemplate} this
13277      */
13278      reset : function(){
13279         var s = this.subs;
13280         for(var i = 0; i < this.subCount; i++){
13281             s[i].buffer = [];
13282         }
13283         return this;
13284     },
13285
13286     applyTemplate : function(values){
13287         var s = this.subs;
13288         var replaceIndex = -1;
13289         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13290             return s[++replaceIndex].buffer.join("");
13291         });
13292         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13293     },
13294
13295     apply : function(){
13296         return this.applyTemplate.apply(this, arguments);
13297     },
13298
13299     compile : function(){return this;}
13300 });
13301
13302 /**
13303  * Alias for fill().
13304  * @method
13305  */
13306 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13307  /**
13308  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13309  * var tpl = Roo.MasterTemplate.from('element-id');
13310  * @param {String/HTMLElement} el
13311  * @param {Object} config
13312  * @static
13313  */
13314 Roo.MasterTemplate.from = function(el, config){
13315     el = Roo.getDom(el);
13316     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13317 };/*
13318  * Based on:
13319  * Ext JS Library 1.1.1
13320  * Copyright(c) 2006-2007, Ext JS, LLC.
13321  *
13322  * Originally Released Under LGPL - original licence link has changed is not relivant.
13323  *
13324  * Fork - LGPL
13325  * <script type="text/javascript">
13326  */
13327
13328  
13329 /**
13330  * @class Roo.util.CSS
13331  * Utility class for manipulating CSS rules
13332  * @singleton
13333  */
13334 Roo.util.CSS = function(){
13335         var rules = null;
13336         var doc = document;
13337
13338     var camelRe = /(-[a-z])/gi;
13339     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13340
13341    return {
13342    /**
13343     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13344     * tag and appended to the HEAD of the document.
13345     * @param {String|Object} cssText The text containing the css rules
13346     * @param {String} id An id to add to the stylesheet for later removal
13347     * @return {StyleSheet}
13348     */
13349     createStyleSheet : function(cssText, id){
13350         var ss;
13351         var head = doc.getElementsByTagName("head")[0];
13352         var nrules = doc.createElement("style");
13353         nrules.setAttribute("type", "text/css");
13354         if(id){
13355             nrules.setAttribute("id", id);
13356         }
13357         if (typeof(cssText) != 'string') {
13358             // support object maps..
13359             // not sure if this a good idea.. 
13360             // perhaps it should be merged with the general css handling
13361             // and handle js style props.
13362             var cssTextNew = [];
13363             for(var n in cssText) {
13364                 var citems = [];
13365                 for(var k in cssText[n]) {
13366                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13367                 }
13368                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13369                 
13370             }
13371             cssText = cssTextNew.join("\n");
13372             
13373         }
13374        
13375        
13376        if(Roo.isIE){
13377            head.appendChild(nrules);
13378            ss = nrules.styleSheet;
13379            ss.cssText = cssText;
13380        }else{
13381            try{
13382                 nrules.appendChild(doc.createTextNode(cssText));
13383            }catch(e){
13384                nrules.cssText = cssText; 
13385            }
13386            head.appendChild(nrules);
13387            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13388        }
13389        this.cacheStyleSheet(ss);
13390        return ss;
13391    },
13392
13393    /**
13394     * Removes a style or link tag by id
13395     * @param {String} id The id of the tag
13396     */
13397    removeStyleSheet : function(id){
13398        var existing = doc.getElementById(id);
13399        if(existing){
13400            existing.parentNode.removeChild(existing);
13401        }
13402    },
13403
13404    /**
13405     * Dynamically swaps an existing stylesheet reference for a new one
13406     * @param {String} id The id of an existing link tag to remove
13407     * @param {String} url The href of the new stylesheet to include
13408     */
13409    swapStyleSheet : function(id, url){
13410        this.removeStyleSheet(id);
13411        var ss = doc.createElement("link");
13412        ss.setAttribute("rel", "stylesheet");
13413        ss.setAttribute("type", "text/css");
13414        ss.setAttribute("id", id);
13415        ss.setAttribute("href", url);
13416        doc.getElementsByTagName("head")[0].appendChild(ss);
13417    },
13418    
13419    /**
13420     * Refresh the rule cache if you have dynamically added stylesheets
13421     * @return {Object} An object (hash) of rules indexed by selector
13422     */
13423    refreshCache : function(){
13424        return this.getRules(true);
13425    },
13426
13427    // private
13428    cacheStyleSheet : function(stylesheet){
13429        if(!rules){
13430            rules = {};
13431        }
13432        try{// try catch for cross domain access issue
13433            var ssRules = stylesheet.cssRules || stylesheet.rules;
13434            for(var j = ssRules.length-1; j >= 0; --j){
13435                rules[ssRules[j].selectorText] = ssRules[j];
13436            }
13437        }catch(e){}
13438    },
13439    
13440    /**
13441     * Gets all css rules for the document
13442     * @param {Boolean} refreshCache true to refresh the internal cache
13443     * @return {Object} An object (hash) of rules indexed by selector
13444     */
13445    getRules : function(refreshCache){
13446                 if(rules == null || refreshCache){
13447                         rules = {};
13448                         var ds = doc.styleSheets;
13449                         for(var i =0, len = ds.length; i < len; i++){
13450                             try{
13451                         this.cacheStyleSheet(ds[i]);
13452                     }catch(e){} 
13453                 }
13454                 }
13455                 return rules;
13456         },
13457         
13458         /**
13459     * Gets an an individual CSS rule by selector(s)
13460     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13461     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13462     * @return {CSSRule} The CSS rule or null if one is not found
13463     */
13464    getRule : function(selector, refreshCache){
13465                 var rs = this.getRules(refreshCache);
13466                 if(!(selector instanceof Array)){
13467                     return rs[selector];
13468                 }
13469                 for(var i = 0; i < selector.length; i++){
13470                         if(rs[selector[i]]){
13471                                 return rs[selector[i]];
13472                         }
13473                 }
13474                 return null;
13475         },
13476         
13477         
13478         /**
13479     * Updates a rule property
13480     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13481     * @param {String} property The css property
13482     * @param {String} value The new value for the property
13483     * @return {Boolean} true If a rule was found and updated
13484     */
13485    updateRule : function(selector, property, value){
13486                 if(!(selector instanceof Array)){
13487                         var rule = this.getRule(selector);
13488                         if(rule){
13489                                 rule.style[property.replace(camelRe, camelFn)] = value;
13490                                 return true;
13491                         }
13492                 }else{
13493                         for(var i = 0; i < selector.length; i++){
13494                                 if(this.updateRule(selector[i], property, value)){
13495                                         return true;
13496                                 }
13497                         }
13498                 }
13499                 return false;
13500         }
13501    };   
13502 }();/*
13503  * Based on:
13504  * Ext JS Library 1.1.1
13505  * Copyright(c) 2006-2007, Ext JS, LLC.
13506  *
13507  * Originally Released Under LGPL - original licence link has changed is not relivant.
13508  *
13509  * Fork - LGPL
13510  * <script type="text/javascript">
13511  */
13512
13513  
13514
13515 /**
13516  * @class Roo.util.ClickRepeater
13517  * @extends Roo.util.Observable
13518  * 
13519  * A wrapper class which can be applied to any element. Fires a "click" event while the
13520  * mouse is pressed. The interval between firings may be specified in the config but
13521  * defaults to 10 milliseconds.
13522  * 
13523  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13524  * 
13525  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13526  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13527  * Similar to an autorepeat key delay.
13528  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13529  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13530  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13531  *           "interval" and "delay" are ignored. "immediate" is honored.
13532  * @cfg {Boolean} preventDefault True to prevent the default click event
13533  * @cfg {Boolean} stopDefault True to stop the default click event
13534  * 
13535  * @history
13536  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13537  *     2007-02-02 jvs Renamed to ClickRepeater
13538  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13539  *
13540  *  @constructor
13541  * @param {String/HTMLElement/Element} el The element to listen on
13542  * @param {Object} config
13543  **/
13544 Roo.util.ClickRepeater = function(el, config)
13545 {
13546     this.el = Roo.get(el);
13547     this.el.unselectable();
13548
13549     Roo.apply(this, config);
13550
13551     this.addEvents({
13552     /**
13553      * @event mousedown
13554      * Fires when the mouse button is depressed.
13555      * @param {Roo.util.ClickRepeater} this
13556      */
13557         "mousedown" : true,
13558     /**
13559      * @event click
13560      * Fires on a specified interval during the time the element is pressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "click" : true,
13564     /**
13565      * @event mouseup
13566      * Fires when the mouse key is released.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "mouseup" : true
13570     });
13571
13572     this.el.on("mousedown", this.handleMouseDown, this);
13573     if(this.preventDefault || this.stopDefault){
13574         this.el.on("click", function(e){
13575             if(this.preventDefault){
13576                 e.preventDefault();
13577             }
13578             if(this.stopDefault){
13579                 e.stopEvent();
13580             }
13581         }, this);
13582     }
13583
13584     // allow inline handler
13585     if(this.handler){
13586         this.on("click", this.handler,  this.scope || this);
13587     }
13588
13589     Roo.util.ClickRepeater.superclass.constructor.call(this);
13590 };
13591
13592 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13593     interval : 20,
13594     delay: 250,
13595     preventDefault : true,
13596     stopDefault : false,
13597     timer : 0,
13598
13599     // private
13600     handleMouseDown : function(){
13601         clearTimeout(this.timer);
13602         this.el.blur();
13603         if(this.pressClass){
13604             this.el.addClass(this.pressClass);
13605         }
13606         this.mousedownTime = new Date();
13607
13608         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13609         this.el.on("mouseout", this.handleMouseOut, this);
13610
13611         this.fireEvent("mousedown", this);
13612         this.fireEvent("click", this);
13613         
13614         this.timer = this.click.defer(this.delay || this.interval, this);
13615     },
13616
13617     // private
13618     click : function(){
13619         this.fireEvent("click", this);
13620         this.timer = this.click.defer(this.getInterval(), this);
13621     },
13622
13623     // private
13624     getInterval: function(){
13625         if(!this.accelerate){
13626             return this.interval;
13627         }
13628         var pressTime = this.mousedownTime.getElapsed();
13629         if(pressTime < 500){
13630             return 400;
13631         }else if(pressTime < 1700){
13632             return 320;
13633         }else if(pressTime < 2600){
13634             return 250;
13635         }else if(pressTime < 3500){
13636             return 180;
13637         }else if(pressTime < 4400){
13638             return 140;
13639         }else if(pressTime < 5300){
13640             return 80;
13641         }else if(pressTime < 6200){
13642             return 50;
13643         }else{
13644             return 10;
13645         }
13646     },
13647
13648     // private
13649     handleMouseOut : function(){
13650         clearTimeout(this.timer);
13651         if(this.pressClass){
13652             this.el.removeClass(this.pressClass);
13653         }
13654         this.el.on("mouseover", this.handleMouseReturn, this);
13655     },
13656
13657     // private
13658     handleMouseReturn : function(){
13659         this.el.un("mouseover", this.handleMouseReturn);
13660         if(this.pressClass){
13661             this.el.addClass(this.pressClass);
13662         }
13663         this.click();
13664     },
13665
13666     // private
13667     handleMouseUp : function(){
13668         clearTimeout(this.timer);
13669         this.el.un("mouseover", this.handleMouseReturn);
13670         this.el.un("mouseout", this.handleMouseOut);
13671         Roo.get(document).un("mouseup", this.handleMouseUp);
13672         this.el.removeClass(this.pressClass);
13673         this.fireEvent("mouseup", this);
13674     }
13675 });/*
13676  * Based on:
13677  * Ext JS Library 1.1.1
13678  * Copyright(c) 2006-2007, Ext JS, LLC.
13679  *
13680  * Originally Released Under LGPL - original licence link has changed is not relivant.
13681  *
13682  * Fork - LGPL
13683  * <script type="text/javascript">
13684  */
13685
13686  
13687 /**
13688  * @class Roo.KeyNav
13689  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13690  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13691  * way to implement custom navigation schemes for any UI component.</p>
13692  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13693  * pageUp, pageDown, del, home, end.  Usage:</p>
13694  <pre><code>
13695 var nav = new Roo.KeyNav("my-element", {
13696     "left" : function(e){
13697         this.moveLeft(e.ctrlKey);
13698     },
13699     "right" : function(e){
13700         this.moveRight(e.ctrlKey);
13701     },
13702     "enter" : function(e){
13703         this.save();
13704     },
13705     scope : this
13706 });
13707 </code></pre>
13708  * @constructor
13709  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13710  * @param {Object} config The config
13711  */
13712 Roo.KeyNav = function(el, config){
13713     this.el = Roo.get(el);
13714     Roo.apply(this, config);
13715     if(!this.disabled){
13716         this.disabled = true;
13717         this.enable();
13718     }
13719 };
13720
13721 Roo.KeyNav.prototype = {
13722     /**
13723      * @cfg {Boolean} disabled
13724      * True to disable this KeyNav instance (defaults to false)
13725      */
13726     disabled : false,
13727     /**
13728      * @cfg {String} defaultEventAction
13729      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13730      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13731      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13732      */
13733     defaultEventAction: "stopEvent",
13734     /**
13735      * @cfg {Boolean} forceKeyDown
13736      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13737      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13738      * handle keydown instead of keypress.
13739      */
13740     forceKeyDown : false,
13741
13742     // private
13743     prepareEvent : function(e){
13744         var k = e.getKey();
13745         var h = this.keyToHandler[k];
13746         //if(h && this[h]){
13747         //    e.stopPropagation();
13748         //}
13749         if(Roo.isSafari && h && k >= 37 && k <= 40){
13750             e.stopEvent();
13751         }
13752     },
13753
13754     // private
13755     relay : function(e){
13756         var k = e.getKey();
13757         var h = this.keyToHandler[k];
13758         if(h && this[h]){
13759             if(this.doRelay(e, this[h], h) !== true){
13760                 e[this.defaultEventAction]();
13761             }
13762         }
13763     },
13764
13765     // private
13766     doRelay : function(e, h, hname){
13767         return h.call(this.scope || this, e);
13768     },
13769
13770     // possible handlers
13771     enter : false,
13772     left : false,
13773     right : false,
13774     up : false,
13775     down : false,
13776     tab : false,
13777     esc : false,
13778     pageUp : false,
13779     pageDown : false,
13780     del : false,
13781     home : false,
13782     end : false,
13783
13784     // quick lookup hash
13785     keyToHandler : {
13786         37 : "left",
13787         39 : "right",
13788         38 : "up",
13789         40 : "down",
13790         33 : "pageUp",
13791         34 : "pageDown",
13792         46 : "del",
13793         36 : "home",
13794         35 : "end",
13795         13 : "enter",
13796         27 : "esc",
13797         9  : "tab"
13798     },
13799
13800         /**
13801          * Enable this KeyNav
13802          */
13803         enable: function(){
13804                 if(this.disabled){
13805             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13806             // the EventObject will normalize Safari automatically
13807             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13808                 this.el.on("keydown", this.relay,  this);
13809             }else{
13810                 this.el.on("keydown", this.prepareEvent,  this);
13811                 this.el.on("keypress", this.relay,  this);
13812             }
13813                     this.disabled = false;
13814                 }
13815         },
13816
13817         /**
13818          * Disable this KeyNav
13819          */
13820         disable: function(){
13821                 if(!this.disabled){
13822                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13823                 this.el.un("keydown", this.relay);
13824             }else{
13825                 this.el.un("keydown", this.prepareEvent);
13826                 this.el.un("keypress", this.relay);
13827             }
13828                     this.disabled = true;
13829                 }
13830         }
13831 };/*
13832  * Based on:
13833  * Ext JS Library 1.1.1
13834  * Copyright(c) 2006-2007, Ext JS, LLC.
13835  *
13836  * Originally Released Under LGPL - original licence link has changed is not relivant.
13837  *
13838  * Fork - LGPL
13839  * <script type="text/javascript">
13840  */
13841
13842  
13843 /**
13844  * @class Roo.KeyMap
13845  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13846  * The constructor accepts the same config object as defined by {@link #addBinding}.
13847  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13848  * combination it will call the function with this signature (if the match is a multi-key
13849  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13850  * A KeyMap can also handle a string representation of keys.<br />
13851  * Usage:
13852  <pre><code>
13853 // map one key by key code
13854 var map = new Roo.KeyMap("my-element", {
13855     key: 13, // or Roo.EventObject.ENTER
13856     fn: myHandler,
13857     scope: myObject
13858 });
13859
13860 // map multiple keys to one action by string
13861 var map = new Roo.KeyMap("my-element", {
13862     key: "a\r\n\t",
13863     fn: myHandler,
13864     scope: myObject
13865 });
13866
13867 // map multiple keys to multiple actions by strings and array of codes
13868 var map = new Roo.KeyMap("my-element", [
13869     {
13870         key: [10,13],
13871         fn: function(){ alert("Return was pressed"); }
13872     }, {
13873         key: "abc",
13874         fn: function(){ alert('a, b or c was pressed'); }
13875     }, {
13876         key: "\t",
13877         ctrl:true,
13878         shift:true,
13879         fn: function(){ alert('Control + shift + tab was pressed.'); }
13880     }
13881 ]);
13882 </code></pre>
13883  * <b>Note: A KeyMap starts enabled</b>
13884  * @constructor
13885  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13886  * @param {Object} config The config (see {@link #addBinding})
13887  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13888  */
13889 Roo.KeyMap = function(el, config, eventName){
13890     this.el  = Roo.get(el);
13891     this.eventName = eventName || "keydown";
13892     this.bindings = [];
13893     if(config){
13894         this.addBinding(config);
13895     }
13896     this.enable();
13897 };
13898
13899 Roo.KeyMap.prototype = {
13900     /**
13901      * True to stop the event from bubbling and prevent the default browser action if the
13902      * key was handled by the KeyMap (defaults to false)
13903      * @type Boolean
13904      */
13905     stopEvent : false,
13906
13907     /**
13908      * Add a new binding to this KeyMap. The following config object properties are supported:
13909      * <pre>
13910 Property    Type             Description
13911 ----------  ---------------  ----------------------------------------------------------------------
13912 key         String/Array     A single keycode or an array of keycodes to handle
13913 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13914 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13915 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13916 fn          Function         The function to call when KeyMap finds the expected key combination
13917 scope       Object           The scope of the callback function
13918 </pre>
13919      *
13920      * Usage:
13921      * <pre><code>
13922 // Create a KeyMap
13923 var map = new Roo.KeyMap(document, {
13924     key: Roo.EventObject.ENTER,
13925     fn: handleKey,
13926     scope: this
13927 });
13928
13929 //Add a new binding to the existing KeyMap later
13930 map.addBinding({
13931     key: 'abc',
13932     shift: true,
13933     fn: handleKey,
13934     scope: this
13935 });
13936 </code></pre>
13937      * @param {Object/Array} config A single KeyMap config or an array of configs
13938      */
13939         addBinding : function(config){
13940         if(config instanceof Array){
13941             for(var i = 0, len = config.length; i < len; i++){
13942                 this.addBinding(config[i]);
13943             }
13944             return;
13945         }
13946         var keyCode = config.key,
13947             shift = config.shift, 
13948             ctrl = config.ctrl, 
13949             alt = config.alt,
13950             fn = config.fn,
13951             scope = config.scope;
13952         if(typeof keyCode == "string"){
13953             var ks = [];
13954             var keyString = keyCode.toUpperCase();
13955             for(var j = 0, len = keyString.length; j < len; j++){
13956                 ks.push(keyString.charCodeAt(j));
13957             }
13958             keyCode = ks;
13959         }
13960         var keyArray = keyCode instanceof Array;
13961         var handler = function(e){
13962             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13963                 var k = e.getKey();
13964                 if(keyArray){
13965                     for(var i = 0, len = keyCode.length; i < len; i++){
13966                         if(keyCode[i] == k){
13967                           if(this.stopEvent){
13968                               e.stopEvent();
13969                           }
13970                           fn.call(scope || window, k, e);
13971                           return;
13972                         }
13973                     }
13974                 }else{
13975                     if(k == keyCode){
13976                         if(this.stopEvent){
13977                            e.stopEvent();
13978                         }
13979                         fn.call(scope || window, k, e);
13980                     }
13981                 }
13982             }
13983         };
13984         this.bindings.push(handler);  
13985         },
13986
13987     /**
13988      * Shorthand for adding a single key listener
13989      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13990      * following options:
13991      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13992      * @param {Function} fn The function to call
13993      * @param {Object} scope (optional) The scope of the function
13994      */
13995     on : function(key, fn, scope){
13996         var keyCode, shift, ctrl, alt;
13997         if(typeof key == "object" && !(key instanceof Array)){
13998             keyCode = key.key;
13999             shift = key.shift;
14000             ctrl = key.ctrl;
14001             alt = key.alt;
14002         }else{
14003             keyCode = key;
14004         }
14005         this.addBinding({
14006             key: keyCode,
14007             shift: shift,
14008             ctrl: ctrl,
14009             alt: alt,
14010             fn: fn,
14011             scope: scope
14012         })
14013     },
14014
14015     // private
14016     handleKeyDown : function(e){
14017             if(this.enabled){ //just in case
14018             var b = this.bindings;
14019             for(var i = 0, len = b.length; i < len; i++){
14020                 b[i].call(this, e);
14021             }
14022             }
14023         },
14024         
14025         /**
14026          * Returns true if this KeyMap is enabled
14027          * @return {Boolean} 
14028          */
14029         isEnabled : function(){
14030             return this.enabled;  
14031         },
14032         
14033         /**
14034          * Enables this KeyMap
14035          */
14036         enable: function(){
14037                 if(!this.enabled){
14038                     this.el.on(this.eventName, this.handleKeyDown, this);
14039                     this.enabled = true;
14040                 }
14041         },
14042
14043         /**
14044          * Disable this KeyMap
14045          */
14046         disable: function(){
14047                 if(this.enabled){
14048                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14049                     this.enabled = false;
14050                 }
14051         }
14052 };/*
14053  * Based on:
14054  * Ext JS Library 1.1.1
14055  * Copyright(c) 2006-2007, Ext JS, LLC.
14056  *
14057  * Originally Released Under LGPL - original licence link has changed is not relivant.
14058  *
14059  * Fork - LGPL
14060  * <script type="text/javascript">
14061  */
14062
14063  
14064 /**
14065  * @class Roo.util.TextMetrics
14066  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14067  * wide, in pixels, a given block of text will be.
14068  * @singleton
14069  */
14070 Roo.util.TextMetrics = function(){
14071     var shared;
14072     return {
14073         /**
14074          * Measures the size of the specified text
14075          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14076          * that can affect the size of the rendered text
14077          * @param {String} text The text to measure
14078          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14079          * in order to accurately measure the text height
14080          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14081          */
14082         measure : function(el, text, fixedWidth){
14083             if(!shared){
14084                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14085             }
14086             shared.bind(el);
14087             shared.setFixedWidth(fixedWidth || 'auto');
14088             return shared.getSize(text);
14089         },
14090
14091         /**
14092          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14093          * the overhead of multiple calls to initialize the style properties on each measurement.
14094          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14095          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14096          * in order to accurately measure the text height
14097          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14098          */
14099         createInstance : function(el, fixedWidth){
14100             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14101         }
14102     };
14103 }();
14104
14105  
14106
14107 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14108     var ml = new Roo.Element(document.createElement('div'));
14109     document.body.appendChild(ml.dom);
14110     ml.position('absolute');
14111     ml.setLeftTop(-1000, -1000);
14112     ml.hide();
14113
14114     if(fixedWidth){
14115         ml.setWidth(fixedWidth);
14116     }
14117      
14118     var instance = {
14119         /**
14120          * Returns the size of the specified text based on the internal element's style and width properties
14121          * @memberOf Roo.util.TextMetrics.Instance#
14122          * @param {String} text The text to measure
14123          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14124          */
14125         getSize : function(text){
14126             ml.update(text);
14127             var s = ml.getSize();
14128             ml.update('');
14129             return s;
14130         },
14131
14132         /**
14133          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14134          * that can affect the size of the rendered text
14135          * @memberOf Roo.util.TextMetrics.Instance#
14136          * @param {String/HTMLElement} el The element, dom node or id
14137          */
14138         bind : function(el){
14139             ml.setStyle(
14140                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14141             );
14142         },
14143
14144         /**
14145          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14146          * to set a fixed width in order to accurately measure the text height.
14147          * @memberOf Roo.util.TextMetrics.Instance#
14148          * @param {Number} width The width to set on the element
14149          */
14150         setFixedWidth : function(width){
14151             ml.setWidth(width);
14152         },
14153
14154         /**
14155          * Returns the measured width of the specified text
14156          * @memberOf Roo.util.TextMetrics.Instance#
14157          * @param {String} text The text to measure
14158          * @return {Number} width The width in pixels
14159          */
14160         getWidth : function(text){
14161             ml.dom.style.width = 'auto';
14162             return this.getSize(text).width;
14163         },
14164
14165         /**
14166          * Returns the measured height of the specified text.  For multiline text, be sure to call
14167          * {@link #setFixedWidth} if necessary.
14168          * @memberOf Roo.util.TextMetrics.Instance#
14169          * @param {String} text The text to measure
14170          * @return {Number} height The height in pixels
14171          */
14172         getHeight : function(text){
14173             return this.getSize(text).height;
14174         }
14175     };
14176
14177     instance.bind(bindTo);
14178
14179     return instance;
14180 };
14181
14182 // backwards compat
14183 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14184  * Based on:
14185  * Ext JS Library 1.1.1
14186  * Copyright(c) 2006-2007, Ext JS, LLC.
14187  *
14188  * Originally Released Under LGPL - original licence link has changed is not relivant.
14189  *
14190  * Fork - LGPL
14191  * <script type="text/javascript">
14192  */
14193
14194 /**
14195  * @class Roo.state.Provider
14196  * Abstract base class for state provider implementations. This class provides methods
14197  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14198  * Provider interface.
14199  */
14200 Roo.state.Provider = function(){
14201     /**
14202      * @event statechange
14203      * Fires when a state change occurs.
14204      * @param {Provider} this This state provider
14205      * @param {String} key The state key which was changed
14206      * @param {String} value The encoded value for the state
14207      */
14208     this.addEvents({
14209         "statechange": true
14210     });
14211     this.state = {};
14212     Roo.state.Provider.superclass.constructor.call(this);
14213 };
14214 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14215     /**
14216      * Returns the current value for a key
14217      * @param {String} name The key name
14218      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14219      * @return {Mixed} The state data
14220      */
14221     get : function(name, defaultValue){
14222         return typeof this.state[name] == "undefined" ?
14223             defaultValue : this.state[name];
14224     },
14225     
14226     /**
14227      * Clears a value from the state
14228      * @param {String} name The key name
14229      */
14230     clear : function(name){
14231         delete this.state[name];
14232         this.fireEvent("statechange", this, name, null);
14233     },
14234     
14235     /**
14236      * Sets the value for a key
14237      * @param {String} name The key name
14238      * @param {Mixed} value The value to set
14239      */
14240     set : function(name, value){
14241         this.state[name] = value;
14242         this.fireEvent("statechange", this, name, value);
14243     },
14244     
14245     /**
14246      * Decodes a string previously encoded with {@link #encodeValue}.
14247      * @param {String} value The value to decode
14248      * @return {Mixed} The decoded value
14249      */
14250     decodeValue : function(cookie){
14251         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14252         var matches = re.exec(unescape(cookie));
14253         if(!matches || !matches[1]) return; // non state cookie
14254         var type = matches[1];
14255         var v = matches[2];
14256         switch(type){
14257             case "n":
14258                 return parseFloat(v);
14259             case "d":
14260                 return new Date(Date.parse(v));
14261             case "b":
14262                 return (v == "1");
14263             case "a":
14264                 var all = [];
14265                 var values = v.split("^");
14266                 for(var i = 0, len = values.length; i < len; i++){
14267                     all.push(this.decodeValue(values[i]));
14268                 }
14269                 return all;
14270            case "o":
14271                 var all = {};
14272                 var values = v.split("^");
14273                 for(var i = 0, len = values.length; i < len; i++){
14274                     var kv = values[i].split("=");
14275                     all[kv[0]] = this.decodeValue(kv[1]);
14276                 }
14277                 return all;
14278            default:
14279                 return v;
14280         }
14281     },
14282     
14283     /**
14284      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14285      * @param {Mixed} value The value to encode
14286      * @return {String} The encoded value
14287      */
14288     encodeValue : function(v){
14289         var enc;
14290         if(typeof v == "number"){
14291             enc = "n:" + v;
14292         }else if(typeof v == "boolean"){
14293             enc = "b:" + (v ? "1" : "0");
14294         }else if(v instanceof Date){
14295             enc = "d:" + v.toGMTString();
14296         }else if(v instanceof Array){
14297             var flat = "";
14298             for(var i = 0, len = v.length; i < len; i++){
14299                 flat += this.encodeValue(v[i]);
14300                 if(i != len-1) flat += "^";
14301             }
14302             enc = "a:" + flat;
14303         }else if(typeof v == "object"){
14304             var flat = "";
14305             for(var key in v){
14306                 if(typeof v[key] != "function"){
14307                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14308                 }
14309             }
14310             enc = "o:" + flat.substring(0, flat.length-1);
14311         }else{
14312             enc = "s:" + v;
14313         }
14314         return escape(enc);        
14315     }
14316 });
14317
14318 /*
14319  * Based on:
14320  * Ext JS Library 1.1.1
14321  * Copyright(c) 2006-2007, Ext JS, LLC.
14322  *
14323  * Originally Released Under LGPL - original licence link has changed is not relivant.
14324  *
14325  * Fork - LGPL
14326  * <script type="text/javascript">
14327  */
14328 /**
14329  * @class Roo.state.Manager
14330  * This is the global state manager. By default all components that are "state aware" check this class
14331  * for state information if you don't pass them a custom state provider. In order for this class
14332  * to be useful, it must be initialized with a provider when your application initializes.
14333  <pre><code>
14334 // in your initialization function
14335 init : function(){
14336    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14337    ...
14338    // supposed you have a {@link Roo.BorderLayout}
14339    var layout = new Roo.BorderLayout(...);
14340    layout.restoreState();
14341    // or a {Roo.BasicDialog}
14342    var dialog = new Roo.BasicDialog(...);
14343    dialog.restoreState();
14344  </code></pre>
14345  * @singleton
14346  */
14347 Roo.state.Manager = function(){
14348     var provider = new Roo.state.Provider();
14349     
14350     return {
14351         /**
14352          * Configures the default state provider for your application
14353          * @param {Provider} stateProvider The state provider to set
14354          */
14355         setProvider : function(stateProvider){
14356             provider = stateProvider;
14357         },
14358         
14359         /**
14360          * Returns the current value for a key
14361          * @param {String} name The key name
14362          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14363          * @return {Mixed} The state data
14364          */
14365         get : function(key, defaultValue){
14366             return provider.get(key, defaultValue);
14367         },
14368         
14369         /**
14370          * Sets the value for a key
14371          * @param {String} name The key name
14372          * @param {Mixed} value The state data
14373          */
14374          set : function(key, value){
14375             provider.set(key, value);
14376         },
14377         
14378         /**
14379          * Clears a value from the state
14380          * @param {String} name The key name
14381          */
14382         clear : function(key){
14383             provider.clear(key);
14384         },
14385         
14386         /**
14387          * Gets the currently configured state provider
14388          * @return {Provider} The state provider
14389          */
14390         getProvider : function(){
14391             return provider;
14392         }
14393     };
14394 }();
14395 /*
14396  * Based on:
14397  * Ext JS Library 1.1.1
14398  * Copyright(c) 2006-2007, Ext JS, LLC.
14399  *
14400  * Originally Released Under LGPL - original licence link has changed is not relivant.
14401  *
14402  * Fork - LGPL
14403  * <script type="text/javascript">
14404  */
14405 /**
14406  * @class Roo.state.CookieProvider
14407  * @extends Roo.state.Provider
14408  * The default Provider implementation which saves state via cookies.
14409  * <br />Usage:
14410  <pre><code>
14411    var cp = new Roo.state.CookieProvider({
14412        path: "/cgi-bin/",
14413        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14414        domain: "roojs.com"
14415    })
14416    Roo.state.Manager.setProvider(cp);
14417  </code></pre>
14418  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14419  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14420  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14421  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14422  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14423  * domain the page is running on including the 'www' like 'www.roojs.com')
14424  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14425  * @constructor
14426  * Create a new CookieProvider
14427  * @param {Object} config The configuration object
14428  */
14429 Roo.state.CookieProvider = function(config){
14430     Roo.state.CookieProvider.superclass.constructor.call(this);
14431     this.path = "/";
14432     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14433     this.domain = null;
14434     this.secure = false;
14435     Roo.apply(this, config);
14436     this.state = this.readCookies();
14437 };
14438
14439 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14440     // private
14441     set : function(name, value){
14442         if(typeof value == "undefined" || value === null){
14443             this.clear(name);
14444             return;
14445         }
14446         this.setCookie(name, value);
14447         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14448     },
14449
14450     // private
14451     clear : function(name){
14452         this.clearCookie(name);
14453         Roo.state.CookieProvider.superclass.clear.call(this, name);
14454     },
14455
14456     // private
14457     readCookies : function(){
14458         var cookies = {};
14459         var c = document.cookie + ";";
14460         var re = /\s?(.*?)=(.*?);/g;
14461         var matches;
14462         while((matches = re.exec(c)) != null){
14463             var name = matches[1];
14464             var value = matches[2];
14465             if(name && name.substring(0,3) == "ys-"){
14466                 cookies[name.substr(3)] = this.decodeValue(value);
14467             }
14468         }
14469         return cookies;
14470     },
14471
14472     // private
14473     setCookie : function(name, value){
14474         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14475            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14476            ((this.path == null) ? "" : ("; path=" + this.path)) +
14477            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14478            ((this.secure == true) ? "; secure" : "");
14479     },
14480
14481     // private
14482     clearCookie : function(name){
14483         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14484            ((this.path == null) ? "" : ("; path=" + this.path)) +
14485            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14486            ((this.secure == true) ? "; secure" : "");
14487     }
14488 });/*
14489  * Based on:
14490  * Ext JS Library 1.1.1
14491  * Copyright(c) 2006-2007, Ext JS, LLC.
14492  *
14493  * Originally Released Under LGPL - original licence link has changed is not relivant.
14494  *
14495  * Fork - LGPL
14496  * <script type="text/javascript">
14497  */
14498
14499
14500
14501 /*
14502  * These classes are derivatives of the similarly named classes in the YUI Library.
14503  * The original license:
14504  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14505  * Code licensed under the BSD License:
14506  * http://developer.yahoo.net/yui/license.txt
14507  */
14508
14509 (function() {
14510
14511 var Event=Roo.EventManager;
14512 var Dom=Roo.lib.Dom;
14513
14514 /**
14515  * @class Roo.dd.DragDrop
14516  * @extends Roo.util.Observable
14517  * Defines the interface and base operation of items that that can be
14518  * dragged or can be drop targets.  It was designed to be extended, overriding
14519  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14520  * Up to three html elements can be associated with a DragDrop instance:
14521  * <ul>
14522  * <li>linked element: the element that is passed into the constructor.
14523  * This is the element which defines the boundaries for interaction with
14524  * other DragDrop objects.</li>
14525  * <li>handle element(s): The drag operation only occurs if the element that
14526  * was clicked matches a handle element.  By default this is the linked
14527  * element, but there are times that you will want only a portion of the
14528  * linked element to initiate the drag operation, and the setHandleElId()
14529  * method provides a way to define this.</li>
14530  * <li>drag element: this represents the element that would be moved along
14531  * with the cursor during a drag operation.  By default, this is the linked
14532  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14533  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14534  * </li>
14535  * </ul>
14536  * This class should not be instantiated until the onload event to ensure that
14537  * the associated elements are available.
14538  * The following would define a DragDrop obj that would interact with any
14539  * other DragDrop obj in the "group1" group:
14540  * <pre>
14541  *  dd = new Roo.dd.DragDrop("div1", "group1");
14542  * </pre>
14543  * Since none of the event handlers have been implemented, nothing would
14544  * actually happen if you were to run the code above.  Normally you would
14545  * override this class or one of the default implementations, but you can
14546  * also override the methods you want on an instance of the class...
14547  * <pre>
14548  *  dd.onDragDrop = function(e, id) {
14549  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14550  *  }
14551  * </pre>
14552  * @constructor
14553  * @param {String} id of the element that is linked to this instance
14554  * @param {String} sGroup the group of related DragDrop objects
14555  * @param {object} config an object containing configurable attributes
14556  *                Valid properties for DragDrop:
14557  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14558  */
14559 Roo.dd.DragDrop = function(id, sGroup, config) {
14560     if (id) {
14561         this.init(id, sGroup, config);
14562     }
14563     
14564 };
14565
14566 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14567
14568     /**
14569      * The id of the element associated with this object.  This is what we
14570      * refer to as the "linked element" because the size and position of
14571      * this element is used to determine when the drag and drop objects have
14572      * interacted.
14573      * @property id
14574      * @type String
14575      */
14576     id: null,
14577
14578     /**
14579      * Configuration attributes passed into the constructor
14580      * @property config
14581      * @type object
14582      */
14583     config: null,
14584
14585     /**
14586      * The id of the element that will be dragged.  By default this is same
14587      * as the linked element , but could be changed to another element. Ex:
14588      * Roo.dd.DDProxy
14589      * @property dragElId
14590      * @type String
14591      * @private
14592      */
14593     dragElId: null,
14594
14595     /**
14596      * the id of the element that initiates the drag operation.  By default
14597      * this is the linked element, but could be changed to be a child of this
14598      * element.  This lets us do things like only starting the drag when the
14599      * header element within the linked html element is clicked.
14600      * @property handleElId
14601      * @type String
14602      * @private
14603      */
14604     handleElId: null,
14605
14606     /**
14607      * An associative array of HTML tags that will be ignored if clicked.
14608      * @property invalidHandleTypes
14609      * @type {string: string}
14610      */
14611     invalidHandleTypes: null,
14612
14613     /**
14614      * An associative array of ids for elements that will be ignored if clicked
14615      * @property invalidHandleIds
14616      * @type {string: string}
14617      */
14618     invalidHandleIds: null,
14619
14620     /**
14621      * An indexted array of css class names for elements that will be ignored
14622      * if clicked.
14623      * @property invalidHandleClasses
14624      * @type string[]
14625      */
14626     invalidHandleClasses: null,
14627
14628     /**
14629      * The linked element's absolute X position at the time the drag was
14630      * started
14631      * @property startPageX
14632      * @type int
14633      * @private
14634      */
14635     startPageX: 0,
14636
14637     /**
14638      * The linked element's absolute X position at the time the drag was
14639      * started
14640      * @property startPageY
14641      * @type int
14642      * @private
14643      */
14644     startPageY: 0,
14645
14646     /**
14647      * The group defines a logical collection of DragDrop objects that are
14648      * related.  Instances only get events when interacting with other
14649      * DragDrop object in the same group.  This lets us define multiple
14650      * groups using a single DragDrop subclass if we want.
14651      * @property groups
14652      * @type {string: string}
14653      */
14654     groups: null,
14655
14656     /**
14657      * Individual drag/drop instances can be locked.  This will prevent
14658      * onmousedown start drag.
14659      * @property locked
14660      * @type boolean
14661      * @private
14662      */
14663     locked: false,
14664
14665     /**
14666      * Lock this instance
14667      * @method lock
14668      */
14669     lock: function() { this.locked = true; },
14670
14671     /**
14672      * Unlock this instace
14673      * @method unlock
14674      */
14675     unlock: function() { this.locked = false; },
14676
14677     /**
14678      * By default, all insances can be a drop target.  This can be disabled by
14679      * setting isTarget to false.
14680      * @method isTarget
14681      * @type boolean
14682      */
14683     isTarget: true,
14684
14685     /**
14686      * The padding configured for this drag and drop object for calculating
14687      * the drop zone intersection with this object.
14688      * @method padding
14689      * @type int[]
14690      */
14691     padding: null,
14692
14693     /**
14694      * Cached reference to the linked element
14695      * @property _domRef
14696      * @private
14697      */
14698     _domRef: null,
14699
14700     /**
14701      * Internal typeof flag
14702      * @property __ygDragDrop
14703      * @private
14704      */
14705     __ygDragDrop: true,
14706
14707     /**
14708      * Set to true when horizontal contraints are applied
14709      * @property constrainX
14710      * @type boolean
14711      * @private
14712      */
14713     constrainX: false,
14714
14715     /**
14716      * Set to true when vertical contraints are applied
14717      * @property constrainY
14718      * @type boolean
14719      * @private
14720      */
14721     constrainY: false,
14722
14723     /**
14724      * The left constraint
14725      * @property minX
14726      * @type int
14727      * @private
14728      */
14729     minX: 0,
14730
14731     /**
14732      * The right constraint
14733      * @property maxX
14734      * @type int
14735      * @private
14736      */
14737     maxX: 0,
14738
14739     /**
14740      * The up constraint
14741      * @property minY
14742      * @type int
14743      * @type int
14744      * @private
14745      */
14746     minY: 0,
14747
14748     /**
14749      * The down constraint
14750      * @property maxY
14751      * @type int
14752      * @private
14753      */
14754     maxY: 0,
14755
14756     /**
14757      * Maintain offsets when we resetconstraints.  Set to true when you want
14758      * the position of the element relative to its parent to stay the same
14759      * when the page changes
14760      *
14761      * @property maintainOffset
14762      * @type boolean
14763      */
14764     maintainOffset: false,
14765
14766     /**
14767      * Array of pixel locations the element will snap to if we specified a
14768      * horizontal graduation/interval.  This array is generated automatically
14769      * when you define a tick interval.
14770      * @property xTicks
14771      * @type int[]
14772      */
14773     xTicks: null,
14774
14775     /**
14776      * Array of pixel locations the element will snap to if we specified a
14777      * vertical graduation/interval.  This array is generated automatically
14778      * when you define a tick interval.
14779      * @property yTicks
14780      * @type int[]
14781      */
14782     yTicks: null,
14783
14784     /**
14785      * By default the drag and drop instance will only respond to the primary
14786      * button click (left button for a right-handed mouse).  Set to true to
14787      * allow drag and drop to start with any mouse click that is propogated
14788      * by the browser
14789      * @property primaryButtonOnly
14790      * @type boolean
14791      */
14792     primaryButtonOnly: true,
14793
14794     /**
14795      * The availabe property is false until the linked dom element is accessible.
14796      * @property available
14797      * @type boolean
14798      */
14799     available: false,
14800
14801     /**
14802      * By default, drags can only be initiated if the mousedown occurs in the
14803      * region the linked element is.  This is done in part to work around a
14804      * bug in some browsers that mis-report the mousedown if the previous
14805      * mouseup happened outside of the window.  This property is set to true
14806      * if outer handles are defined.
14807      *
14808      * @property hasOuterHandles
14809      * @type boolean
14810      * @default false
14811      */
14812     hasOuterHandles: false,
14813
14814     /**
14815      * Code that executes immediately before the startDrag event
14816      * @method b4StartDrag
14817      * @private
14818      */
14819     b4StartDrag: function(x, y) { },
14820
14821     /**
14822      * Abstract method called after a drag/drop object is clicked
14823      * and the drag or mousedown time thresholds have beeen met.
14824      * @method startDrag
14825      * @param {int} X click location
14826      * @param {int} Y click location
14827      */
14828     startDrag: function(x, y) { /* override this */ },
14829
14830     /**
14831      * Code that executes immediately before the onDrag event
14832      * @method b4Drag
14833      * @private
14834      */
14835     b4Drag: function(e) { },
14836
14837     /**
14838      * Abstract method called during the onMouseMove event while dragging an
14839      * object.
14840      * @method onDrag
14841      * @param {Event} e the mousemove event
14842      */
14843     onDrag: function(e) { /* override this */ },
14844
14845     /**
14846      * Abstract method called when this element fist begins hovering over
14847      * another DragDrop obj
14848      * @method onDragEnter
14849      * @param {Event} e the mousemove event
14850      * @param {String|DragDrop[]} id In POINT mode, the element
14851      * id this is hovering over.  In INTERSECT mode, an array of one or more
14852      * dragdrop items being hovered over.
14853      */
14854     onDragEnter: function(e, id) { /* override this */ },
14855
14856     /**
14857      * Code that executes immediately before the onDragOver event
14858      * @method b4DragOver
14859      * @private
14860      */
14861     b4DragOver: function(e) { },
14862
14863     /**
14864      * Abstract method called when this element is hovering over another
14865      * DragDrop obj
14866      * @method onDragOver
14867      * @param {Event} e the mousemove event
14868      * @param {String|DragDrop[]} id In POINT mode, the element
14869      * id this is hovering over.  In INTERSECT mode, an array of dd items
14870      * being hovered over.
14871      */
14872     onDragOver: function(e, id) { /* override this */ },
14873
14874     /**
14875      * Code that executes immediately before the onDragOut event
14876      * @method b4DragOut
14877      * @private
14878      */
14879     b4DragOut: function(e) { },
14880
14881     /**
14882      * Abstract method called when we are no longer hovering over an element
14883      * @method onDragOut
14884      * @param {Event} e the mousemove event
14885      * @param {String|DragDrop[]} id In POINT mode, the element
14886      * id this was hovering over.  In INTERSECT mode, an array of dd items
14887      * that the mouse is no longer over.
14888      */
14889     onDragOut: function(e, id) { /* override this */ },
14890
14891     /**
14892      * Code that executes immediately before the onDragDrop event
14893      * @method b4DragDrop
14894      * @private
14895      */
14896     b4DragDrop: function(e) { },
14897
14898     /**
14899      * Abstract method called when this item is dropped on another DragDrop
14900      * obj
14901      * @method onDragDrop
14902      * @param {Event} e the mouseup event
14903      * @param {String|DragDrop[]} id In POINT mode, the element
14904      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14905      * was dropped on.
14906      */
14907     onDragDrop: function(e, id) { /* override this */ },
14908
14909     /**
14910      * Abstract method called when this item is dropped on an area with no
14911      * drop target
14912      * @method onInvalidDrop
14913      * @param {Event} e the mouseup event
14914      */
14915     onInvalidDrop: function(e) { /* override this */ },
14916
14917     /**
14918      * Code that executes immediately before the endDrag event
14919      * @method b4EndDrag
14920      * @private
14921      */
14922     b4EndDrag: function(e) { },
14923
14924     /**
14925      * Fired when we are done dragging the object
14926      * @method endDrag
14927      * @param {Event} e the mouseup event
14928      */
14929     endDrag: function(e) { /* override this */ },
14930
14931     /**
14932      * Code executed immediately before the onMouseDown event
14933      * @method b4MouseDown
14934      * @param {Event} e the mousedown event
14935      * @private
14936      */
14937     b4MouseDown: function(e) {  },
14938
14939     /**
14940      * Event handler that fires when a drag/drop obj gets a mousedown
14941      * @method onMouseDown
14942      * @param {Event} e the mousedown event
14943      */
14944     onMouseDown: function(e) { /* override this */ },
14945
14946     /**
14947      * Event handler that fires when a drag/drop obj gets a mouseup
14948      * @method onMouseUp
14949      * @param {Event} e the mouseup event
14950      */
14951     onMouseUp: function(e) { /* override this */ },
14952
14953     /**
14954      * Override the onAvailable method to do what is needed after the initial
14955      * position was determined.
14956      * @method onAvailable
14957      */
14958     onAvailable: function () {
14959     },
14960
14961     /*
14962      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14963      * @type Object
14964      */
14965     defaultPadding : {left:0, right:0, top:0, bottom:0},
14966
14967     /*
14968      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14969  *
14970  * Usage:
14971  <pre><code>
14972  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14973                 { dragElId: "existingProxyDiv" });
14974  dd.startDrag = function(){
14975      this.constrainTo("parent-id");
14976  };
14977  </code></pre>
14978  * Or you can initalize it using the {@link Roo.Element} object:
14979  <pre><code>
14980  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14981      startDrag : function(){
14982          this.constrainTo("parent-id");
14983      }
14984  });
14985  </code></pre>
14986      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14987      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14988      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14989      * an object containing the sides to pad. For example: {right:10, bottom:10}
14990      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14991      */
14992     constrainTo : function(constrainTo, pad, inContent){
14993         if(typeof pad == "number"){
14994             pad = {left: pad, right:pad, top:pad, bottom:pad};
14995         }
14996         pad = pad || this.defaultPadding;
14997         var b = Roo.get(this.getEl()).getBox();
14998         var ce = Roo.get(constrainTo);
14999         var s = ce.getScroll();
15000         var c, cd = ce.dom;
15001         if(cd == document.body){
15002             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15003         }else{
15004             xy = ce.getXY();
15005             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15006         }
15007
15008
15009         var topSpace = b.y - c.y;
15010         var leftSpace = b.x - c.x;
15011
15012         this.resetConstraints();
15013         this.setXConstraint(leftSpace - (pad.left||0), // left
15014                 c.width - leftSpace - b.width - (pad.right||0) //right
15015         );
15016         this.setYConstraint(topSpace - (pad.top||0), //top
15017                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15018         );
15019     },
15020
15021     /**
15022      * Returns a reference to the linked element
15023      * @method getEl
15024      * @return {HTMLElement} the html element
15025      */
15026     getEl: function() {
15027         if (!this._domRef) {
15028             this._domRef = Roo.getDom(this.id);
15029         }
15030
15031         return this._domRef;
15032     },
15033
15034     /**
15035      * Returns a reference to the actual element to drag.  By default this is
15036      * the same as the html element, but it can be assigned to another
15037      * element. An example of this can be found in Roo.dd.DDProxy
15038      * @method getDragEl
15039      * @return {HTMLElement} the html element
15040      */
15041     getDragEl: function() {
15042         return Roo.getDom(this.dragElId);
15043     },
15044
15045     /**
15046      * Sets up the DragDrop object.  Must be called in the constructor of any
15047      * Roo.dd.DragDrop subclass
15048      * @method init
15049      * @param id the id of the linked element
15050      * @param {String} sGroup the group of related items
15051      * @param {object} config configuration attributes
15052      */
15053     init: function(id, sGroup, config) {
15054         this.initTarget(id, sGroup, config);
15055         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15056         // Event.on(this.id, "selectstart", Event.preventDefault);
15057     },
15058
15059     /**
15060      * Initializes Targeting functionality only... the object does not
15061      * get a mousedown handler.
15062      * @method initTarget
15063      * @param id the id of the linked element
15064      * @param {String} sGroup the group of related items
15065      * @param {object} config configuration attributes
15066      */
15067     initTarget: function(id, sGroup, config) {
15068
15069         // configuration attributes
15070         this.config = config || {};
15071
15072         // create a local reference to the drag and drop manager
15073         this.DDM = Roo.dd.DDM;
15074         // initialize the groups array
15075         this.groups = {};
15076
15077         // assume that we have an element reference instead of an id if the
15078         // parameter is not a string
15079         if (typeof id !== "string") {
15080             id = Roo.id(id);
15081         }
15082
15083         // set the id
15084         this.id = id;
15085
15086         // add to an interaction group
15087         this.addToGroup((sGroup) ? sGroup : "default");
15088
15089         // We don't want to register this as the handle with the manager
15090         // so we just set the id rather than calling the setter.
15091         this.handleElId = id;
15092
15093         // the linked element is the element that gets dragged by default
15094         this.setDragElId(id);
15095
15096         // by default, clicked anchors will not start drag operations.
15097         this.invalidHandleTypes = { A: "A" };
15098         this.invalidHandleIds = {};
15099         this.invalidHandleClasses = [];
15100
15101         this.applyConfig();
15102
15103         this.handleOnAvailable();
15104     },
15105
15106     /**
15107      * Applies the configuration parameters that were passed into the constructor.
15108      * This is supposed to happen at each level through the inheritance chain.  So
15109      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15110      * DragDrop in order to get all of the parameters that are available in
15111      * each object.
15112      * @method applyConfig
15113      */
15114     applyConfig: function() {
15115
15116         // configurable properties:
15117         //    padding, isTarget, maintainOffset, primaryButtonOnly
15118         this.padding           = this.config.padding || [0, 0, 0, 0];
15119         this.isTarget          = (this.config.isTarget !== false);
15120         this.maintainOffset    = (this.config.maintainOffset);
15121         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15122
15123     },
15124
15125     /**
15126      * Executed when the linked element is available
15127      * @method handleOnAvailable
15128      * @private
15129      */
15130     handleOnAvailable: function() {
15131         this.available = true;
15132         this.resetConstraints();
15133         this.onAvailable();
15134     },
15135
15136      /**
15137      * Configures the padding for the target zone in px.  Effectively expands
15138      * (or reduces) the virtual object size for targeting calculations.
15139      * Supports css-style shorthand; if only one parameter is passed, all sides
15140      * will have that padding, and if only two are passed, the top and bottom
15141      * will have the first param, the left and right the second.
15142      * @method setPadding
15143      * @param {int} iTop    Top pad
15144      * @param {int} iRight  Right pad
15145      * @param {int} iBot    Bot pad
15146      * @param {int} iLeft   Left pad
15147      */
15148     setPadding: function(iTop, iRight, iBot, iLeft) {
15149         // this.padding = [iLeft, iRight, iTop, iBot];
15150         if (!iRight && 0 !== iRight) {
15151             this.padding = [iTop, iTop, iTop, iTop];
15152         } else if (!iBot && 0 !== iBot) {
15153             this.padding = [iTop, iRight, iTop, iRight];
15154         } else {
15155             this.padding = [iTop, iRight, iBot, iLeft];
15156         }
15157     },
15158
15159     /**
15160      * Stores the initial placement of the linked element.
15161      * @method setInitialPosition
15162      * @param {int} diffX   the X offset, default 0
15163      * @param {int} diffY   the Y offset, default 0
15164      */
15165     setInitPosition: function(diffX, diffY) {
15166         var el = this.getEl();
15167
15168         if (!this.DDM.verifyEl(el)) {
15169             return;
15170         }
15171
15172         var dx = diffX || 0;
15173         var dy = diffY || 0;
15174
15175         var p = Dom.getXY( el );
15176
15177         this.initPageX = p[0] - dx;
15178         this.initPageY = p[1] - dy;
15179
15180         this.lastPageX = p[0];
15181         this.lastPageY = p[1];
15182
15183
15184         this.setStartPosition(p);
15185     },
15186
15187     /**
15188      * Sets the start position of the element.  This is set when the obj
15189      * is initialized, the reset when a drag is started.
15190      * @method setStartPosition
15191      * @param pos current position (from previous lookup)
15192      * @private
15193      */
15194     setStartPosition: function(pos) {
15195         var p = pos || Dom.getXY( this.getEl() );
15196         this.deltaSetXY = null;
15197
15198         this.startPageX = p[0];
15199         this.startPageY = p[1];
15200     },
15201
15202     /**
15203      * Add this instance to a group of related drag/drop objects.  All
15204      * instances belong to at least one group, and can belong to as many
15205      * groups as needed.
15206      * @method addToGroup
15207      * @param sGroup {string} the name of the group
15208      */
15209     addToGroup: function(sGroup) {
15210         this.groups[sGroup] = true;
15211         this.DDM.regDragDrop(this, sGroup);
15212     },
15213
15214     /**
15215      * Remove's this instance from the supplied interaction group
15216      * @method removeFromGroup
15217      * @param {string}  sGroup  The group to drop
15218      */
15219     removeFromGroup: function(sGroup) {
15220         if (this.groups[sGroup]) {
15221             delete this.groups[sGroup];
15222         }
15223
15224         this.DDM.removeDDFromGroup(this, sGroup);
15225     },
15226
15227     /**
15228      * Allows you to specify that an element other than the linked element
15229      * will be moved with the cursor during a drag
15230      * @method setDragElId
15231      * @param id {string} the id of the element that will be used to initiate the drag
15232      */
15233     setDragElId: function(id) {
15234         this.dragElId = id;
15235     },
15236
15237     /**
15238      * Allows you to specify a child of the linked element that should be
15239      * used to initiate the drag operation.  An example of this would be if
15240      * you have a content div with text and links.  Clicking anywhere in the
15241      * content area would normally start the drag operation.  Use this method
15242      * to specify that an element inside of the content div is the element
15243      * that starts the drag operation.
15244      * @method setHandleElId
15245      * @param id {string} the id of the element that will be used to
15246      * initiate the drag.
15247      */
15248     setHandleElId: function(id) {
15249         if (typeof id !== "string") {
15250             id = Roo.id(id);
15251         }
15252         this.handleElId = id;
15253         this.DDM.regHandle(this.id, id);
15254     },
15255
15256     /**
15257      * Allows you to set an element outside of the linked element as a drag
15258      * handle
15259      * @method setOuterHandleElId
15260      * @param id the id of the element that will be used to initiate the drag
15261      */
15262     setOuterHandleElId: function(id) {
15263         if (typeof id !== "string") {
15264             id = Roo.id(id);
15265         }
15266         Event.on(id, "mousedown",
15267                 this.handleMouseDown, this);
15268         this.setHandleElId(id);
15269
15270         this.hasOuterHandles = true;
15271     },
15272
15273     /**
15274      * Remove all drag and drop hooks for this element
15275      * @method unreg
15276      */
15277     unreg: function() {
15278         Event.un(this.id, "mousedown",
15279                 this.handleMouseDown);
15280         this._domRef = null;
15281         this.DDM._remove(this);
15282     },
15283
15284     destroy : function(){
15285         this.unreg();
15286     },
15287
15288     /**
15289      * Returns true if this instance is locked, or the drag drop mgr is locked
15290      * (meaning that all drag/drop is disabled on the page.)
15291      * @method isLocked
15292      * @return {boolean} true if this obj or all drag/drop is locked, else
15293      * false
15294      */
15295     isLocked: function() {
15296         return (this.DDM.isLocked() || this.locked);
15297     },
15298
15299     /**
15300      * Fired when this object is clicked
15301      * @method handleMouseDown
15302      * @param {Event} e
15303      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15304      * @private
15305      */
15306     handleMouseDown: function(e, oDD){
15307         if (this.primaryButtonOnly && e.button != 0) {
15308             return;
15309         }
15310
15311         if (this.isLocked()) {
15312             return;
15313         }
15314
15315         this.DDM.refreshCache(this.groups);
15316
15317         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15318         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15319         } else {
15320             if (this.clickValidator(e)) {
15321
15322                 // set the initial element position
15323                 this.setStartPosition();
15324
15325
15326                 this.b4MouseDown(e);
15327                 this.onMouseDown(e);
15328
15329                 this.DDM.handleMouseDown(e, this);
15330
15331                 this.DDM.stopEvent(e);
15332             } else {
15333
15334
15335             }
15336         }
15337     },
15338
15339     clickValidator: function(e) {
15340         var target = e.getTarget();
15341         return ( this.isValidHandleChild(target) &&
15342                     (this.id == this.handleElId ||
15343                         this.DDM.handleWasClicked(target, this.id)) );
15344     },
15345
15346     /**
15347      * Allows you to specify a tag name that should not start a drag operation
15348      * when clicked.  This is designed to facilitate embedding links within a
15349      * drag handle that do something other than start the drag.
15350      * @method addInvalidHandleType
15351      * @param {string} tagName the type of element to exclude
15352      */
15353     addInvalidHandleType: function(tagName) {
15354         var type = tagName.toUpperCase();
15355         this.invalidHandleTypes[type] = type;
15356     },
15357
15358     /**
15359      * Lets you to specify an element id for a child of a drag handle
15360      * that should not initiate a drag
15361      * @method addInvalidHandleId
15362      * @param {string} id the element id of the element you wish to ignore
15363      */
15364     addInvalidHandleId: function(id) {
15365         if (typeof id !== "string") {
15366             id = Roo.id(id);
15367         }
15368         this.invalidHandleIds[id] = id;
15369     },
15370
15371     /**
15372      * Lets you specify a css class of elements that will not initiate a drag
15373      * @method addInvalidHandleClass
15374      * @param {string} cssClass the class of the elements you wish to ignore
15375      */
15376     addInvalidHandleClass: function(cssClass) {
15377         this.invalidHandleClasses.push(cssClass);
15378     },
15379
15380     /**
15381      * Unsets an excluded tag name set by addInvalidHandleType
15382      * @method removeInvalidHandleType
15383      * @param {string} tagName the type of element to unexclude
15384      */
15385     removeInvalidHandleType: function(tagName) {
15386         var type = tagName.toUpperCase();
15387         // this.invalidHandleTypes[type] = null;
15388         delete this.invalidHandleTypes[type];
15389     },
15390
15391     /**
15392      * Unsets an invalid handle id
15393      * @method removeInvalidHandleId
15394      * @param {string} id the id of the element to re-enable
15395      */
15396     removeInvalidHandleId: function(id) {
15397         if (typeof id !== "string") {
15398             id = Roo.id(id);
15399         }
15400         delete this.invalidHandleIds[id];
15401     },
15402
15403     /**
15404      * Unsets an invalid css class
15405      * @method removeInvalidHandleClass
15406      * @param {string} cssClass the class of the element(s) you wish to
15407      * re-enable
15408      */
15409     removeInvalidHandleClass: function(cssClass) {
15410         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15411             if (this.invalidHandleClasses[i] == cssClass) {
15412                 delete this.invalidHandleClasses[i];
15413             }
15414         }
15415     },
15416
15417     /**
15418      * Checks the tag exclusion list to see if this click should be ignored
15419      * @method isValidHandleChild
15420      * @param {HTMLElement} node the HTMLElement to evaluate
15421      * @return {boolean} true if this is a valid tag type, false if not
15422      */
15423     isValidHandleChild: function(node) {
15424
15425         var valid = true;
15426         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15427         var nodeName;
15428         try {
15429             nodeName = node.nodeName.toUpperCase();
15430         } catch(e) {
15431             nodeName = node.nodeName;
15432         }
15433         valid = valid && !this.invalidHandleTypes[nodeName];
15434         valid = valid && !this.invalidHandleIds[node.id];
15435
15436         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15437             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15438         }
15439
15440
15441         return valid;
15442
15443     },
15444
15445     /**
15446      * Create the array of horizontal tick marks if an interval was specified
15447      * in setXConstraint().
15448      * @method setXTicks
15449      * @private
15450      */
15451     setXTicks: function(iStartX, iTickSize) {
15452         this.xTicks = [];
15453         this.xTickSize = iTickSize;
15454
15455         var tickMap = {};
15456
15457         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15458             if (!tickMap[i]) {
15459                 this.xTicks[this.xTicks.length] = i;
15460                 tickMap[i] = true;
15461             }
15462         }
15463
15464         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15465             if (!tickMap[i]) {
15466                 this.xTicks[this.xTicks.length] = i;
15467                 tickMap[i] = true;
15468             }
15469         }
15470
15471         this.xTicks.sort(this.DDM.numericSort) ;
15472     },
15473
15474     /**
15475      * Create the array of vertical tick marks if an interval was specified in
15476      * setYConstraint().
15477      * @method setYTicks
15478      * @private
15479      */
15480     setYTicks: function(iStartY, iTickSize) {
15481         this.yTicks = [];
15482         this.yTickSize = iTickSize;
15483
15484         var tickMap = {};
15485
15486         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15487             if (!tickMap[i]) {
15488                 this.yTicks[this.yTicks.length] = i;
15489                 tickMap[i] = true;
15490             }
15491         }
15492
15493         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15494             if (!tickMap[i]) {
15495                 this.yTicks[this.yTicks.length] = i;
15496                 tickMap[i] = true;
15497             }
15498         }
15499
15500         this.yTicks.sort(this.DDM.numericSort) ;
15501     },
15502
15503     /**
15504      * By default, the element can be dragged any place on the screen.  Use
15505      * this method to limit the horizontal travel of the element.  Pass in
15506      * 0,0 for the parameters if you want to lock the drag to the y axis.
15507      * @method setXConstraint
15508      * @param {int} iLeft the number of pixels the element can move to the left
15509      * @param {int} iRight the number of pixels the element can move to the
15510      * right
15511      * @param {int} iTickSize optional parameter for specifying that the
15512      * element
15513      * should move iTickSize pixels at a time.
15514      */
15515     setXConstraint: function(iLeft, iRight, iTickSize) {
15516         this.leftConstraint = iLeft;
15517         this.rightConstraint = iRight;
15518
15519         this.minX = this.initPageX - iLeft;
15520         this.maxX = this.initPageX + iRight;
15521         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15522
15523         this.constrainX = true;
15524     },
15525
15526     /**
15527      * Clears any constraints applied to this instance.  Also clears ticks
15528      * since they can't exist independent of a constraint at this time.
15529      * @method clearConstraints
15530      */
15531     clearConstraints: function() {
15532         this.constrainX = false;
15533         this.constrainY = false;
15534         this.clearTicks();
15535     },
15536
15537     /**
15538      * Clears any tick interval defined for this instance
15539      * @method clearTicks
15540      */
15541     clearTicks: function() {
15542         this.xTicks = null;
15543         this.yTicks = null;
15544         this.xTickSize = 0;
15545         this.yTickSize = 0;
15546     },
15547
15548     /**
15549      * By default, the element can be dragged any place on the screen.  Set
15550      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15551      * parameters if you want to lock the drag to the x axis.
15552      * @method setYConstraint
15553      * @param {int} iUp the number of pixels the element can move up
15554      * @param {int} iDown the number of pixels the element can move down
15555      * @param {int} iTickSize optional parameter for specifying that the
15556      * element should move iTickSize pixels at a time.
15557      */
15558     setYConstraint: function(iUp, iDown, iTickSize) {
15559         this.topConstraint = iUp;
15560         this.bottomConstraint = iDown;
15561
15562         this.minY = this.initPageY - iUp;
15563         this.maxY = this.initPageY + iDown;
15564         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15565
15566         this.constrainY = true;
15567
15568     },
15569
15570     /**
15571      * resetConstraints must be called if you manually reposition a dd element.
15572      * @method resetConstraints
15573      * @param {boolean} maintainOffset
15574      */
15575     resetConstraints: function() {
15576
15577
15578         // Maintain offsets if necessary
15579         if (this.initPageX || this.initPageX === 0) {
15580             // figure out how much this thing has moved
15581             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15582             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15583
15584             this.setInitPosition(dx, dy);
15585
15586         // This is the first time we have detected the element's position
15587         } else {
15588             this.setInitPosition();
15589         }
15590
15591         if (this.constrainX) {
15592             this.setXConstraint( this.leftConstraint,
15593                                  this.rightConstraint,
15594                                  this.xTickSize        );
15595         }
15596
15597         if (this.constrainY) {
15598             this.setYConstraint( this.topConstraint,
15599                                  this.bottomConstraint,
15600                                  this.yTickSize         );
15601         }
15602     },
15603
15604     /**
15605      * Normally the drag element is moved pixel by pixel, but we can specify
15606      * that it move a number of pixels at a time.  This method resolves the
15607      * location when we have it set up like this.
15608      * @method getTick
15609      * @param {int} val where we want to place the object
15610      * @param {int[]} tickArray sorted array of valid points
15611      * @return {int} the closest tick
15612      * @private
15613      */
15614     getTick: function(val, tickArray) {
15615
15616         if (!tickArray) {
15617             // If tick interval is not defined, it is effectively 1 pixel,
15618             // so we return the value passed to us.
15619             return val;
15620         } else if (tickArray[0] >= val) {
15621             // The value is lower than the first tick, so we return the first
15622             // tick.
15623             return tickArray[0];
15624         } else {
15625             for (var i=0, len=tickArray.length; i<len; ++i) {
15626                 var next = i + 1;
15627                 if (tickArray[next] && tickArray[next] >= val) {
15628                     var diff1 = val - tickArray[i];
15629                     var diff2 = tickArray[next] - val;
15630                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15631                 }
15632             }
15633
15634             // The value is larger than the last tick, so we return the last
15635             // tick.
15636             return tickArray[tickArray.length - 1];
15637         }
15638     },
15639
15640     /**
15641      * toString method
15642      * @method toString
15643      * @return {string} string representation of the dd obj
15644      */
15645     toString: function() {
15646         return ("DragDrop " + this.id);
15647     }
15648
15649 });
15650
15651 })();
15652 /*
15653  * Based on:
15654  * Ext JS Library 1.1.1
15655  * Copyright(c) 2006-2007, Ext JS, LLC.
15656  *
15657  * Originally Released Under LGPL - original licence link has changed is not relivant.
15658  *
15659  * Fork - LGPL
15660  * <script type="text/javascript">
15661  */
15662
15663
15664 /**
15665  * The drag and drop utility provides a framework for building drag and drop
15666  * applications.  In addition to enabling drag and drop for specific elements,
15667  * the drag and drop elements are tracked by the manager class, and the
15668  * interactions between the various elements are tracked during the drag and
15669  * the implementing code is notified about these important moments.
15670  */
15671
15672 // Only load the library once.  Rewriting the manager class would orphan
15673 // existing drag and drop instances.
15674 if (!Roo.dd.DragDropMgr) {
15675
15676 /**
15677  * @class Roo.dd.DragDropMgr
15678  * DragDropMgr is a singleton that tracks the element interaction for
15679  * all DragDrop items in the window.  Generally, you will not call
15680  * this class directly, but it does have helper methods that could
15681  * be useful in your DragDrop implementations.
15682  * @singleton
15683  */
15684 Roo.dd.DragDropMgr = function() {
15685
15686     var Event = Roo.EventManager;
15687
15688     return {
15689
15690         /**
15691          * Two dimensional Array of registered DragDrop objects.  The first
15692          * dimension is the DragDrop item group, the second the DragDrop
15693          * object.
15694          * @property ids
15695          * @type {string: string}
15696          * @private
15697          * @static
15698          */
15699         ids: {},
15700
15701         /**
15702          * Array of element ids defined as drag handles.  Used to determine
15703          * if the element that generated the mousedown event is actually the
15704          * handle and not the html element itself.
15705          * @property handleIds
15706          * @type {string: string}
15707          * @private
15708          * @static
15709          */
15710         handleIds: {},
15711
15712         /**
15713          * the DragDrop object that is currently being dragged
15714          * @property dragCurrent
15715          * @type DragDrop
15716          * @private
15717          * @static
15718          **/
15719         dragCurrent: null,
15720
15721         /**
15722          * the DragDrop object(s) that are being hovered over
15723          * @property dragOvers
15724          * @type Array
15725          * @private
15726          * @static
15727          */
15728         dragOvers: {},
15729
15730         /**
15731          * the X distance between the cursor and the object being dragged
15732          * @property deltaX
15733          * @type int
15734          * @private
15735          * @static
15736          */
15737         deltaX: 0,
15738
15739         /**
15740          * the Y distance between the cursor and the object being dragged
15741          * @property deltaY
15742          * @type int
15743          * @private
15744          * @static
15745          */
15746         deltaY: 0,
15747
15748         /**
15749          * Flag to determine if we should prevent the default behavior of the
15750          * events we define. By default this is true, but this can be set to
15751          * false if you need the default behavior (not recommended)
15752          * @property preventDefault
15753          * @type boolean
15754          * @static
15755          */
15756         preventDefault: true,
15757
15758         /**
15759          * Flag to determine if we should stop the propagation of the events
15760          * we generate. This is true by default but you may want to set it to
15761          * false if the html element contains other features that require the
15762          * mouse click.
15763          * @property stopPropagation
15764          * @type boolean
15765          * @static
15766          */
15767         stopPropagation: true,
15768
15769         /**
15770          * Internal flag that is set to true when drag and drop has been
15771          * intialized
15772          * @property initialized
15773          * @private
15774          * @static
15775          */
15776         initalized: false,
15777
15778         /**
15779          * All drag and drop can be disabled.
15780          * @property locked
15781          * @private
15782          * @static
15783          */
15784         locked: false,
15785
15786         /**
15787          * Called the first time an element is registered.
15788          * @method init
15789          * @private
15790          * @static
15791          */
15792         init: function() {
15793             this.initialized = true;
15794         },
15795
15796         /**
15797          * In point mode, drag and drop interaction is defined by the
15798          * location of the cursor during the drag/drop
15799          * @property POINT
15800          * @type int
15801          * @static
15802          */
15803         POINT: 0,
15804
15805         /**
15806          * In intersect mode, drag and drop interactio nis defined by the
15807          * overlap of two or more drag and drop objects.
15808          * @property INTERSECT
15809          * @type int
15810          * @static
15811          */
15812         INTERSECT: 1,
15813
15814         /**
15815          * The current drag and drop mode.  Default: POINT
15816          * @property mode
15817          * @type int
15818          * @static
15819          */
15820         mode: 0,
15821
15822         /**
15823          * Runs method on all drag and drop objects
15824          * @method _execOnAll
15825          * @private
15826          * @static
15827          */
15828         _execOnAll: function(sMethod, args) {
15829             for (var i in this.ids) {
15830                 for (var j in this.ids[i]) {
15831                     var oDD = this.ids[i][j];
15832                     if (! this.isTypeOfDD(oDD)) {
15833                         continue;
15834                     }
15835                     oDD[sMethod].apply(oDD, args);
15836                 }
15837             }
15838         },
15839
15840         /**
15841          * Drag and drop initialization.  Sets up the global event handlers
15842          * @method _onLoad
15843          * @private
15844          * @static
15845          */
15846         _onLoad: function() {
15847
15848             this.init();
15849
15850
15851             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15852             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15853             Event.on(window,   "unload",    this._onUnload, this, true);
15854             Event.on(window,   "resize",    this._onResize, this, true);
15855             // Event.on(window,   "mouseout",    this._test);
15856
15857         },
15858
15859         /**
15860          * Reset constraints on all drag and drop objs
15861          * @method _onResize
15862          * @private
15863          * @static
15864          */
15865         _onResize: function(e) {
15866             this._execOnAll("resetConstraints", []);
15867         },
15868
15869         /**
15870          * Lock all drag and drop functionality
15871          * @method lock
15872          * @static
15873          */
15874         lock: function() { this.locked = true; },
15875
15876         /**
15877          * Unlock all drag and drop functionality
15878          * @method unlock
15879          * @static
15880          */
15881         unlock: function() { this.locked = false; },
15882
15883         /**
15884          * Is drag and drop locked?
15885          * @method isLocked
15886          * @return {boolean} True if drag and drop is locked, false otherwise.
15887          * @static
15888          */
15889         isLocked: function() { return this.locked; },
15890
15891         /**
15892          * Location cache that is set for all drag drop objects when a drag is
15893          * initiated, cleared when the drag is finished.
15894          * @property locationCache
15895          * @private
15896          * @static
15897          */
15898         locationCache: {},
15899
15900         /**
15901          * Set useCache to false if you want to force object the lookup of each
15902          * drag and drop linked element constantly during a drag.
15903          * @property useCache
15904          * @type boolean
15905          * @static
15906          */
15907         useCache: true,
15908
15909         /**
15910          * The number of pixels that the mouse needs to move after the
15911          * mousedown before the drag is initiated.  Default=3;
15912          * @property clickPixelThresh
15913          * @type int
15914          * @static
15915          */
15916         clickPixelThresh: 3,
15917
15918         /**
15919          * The number of milliseconds after the mousedown event to initiate the
15920          * drag if we don't get a mouseup event. Default=1000
15921          * @property clickTimeThresh
15922          * @type int
15923          * @static
15924          */
15925         clickTimeThresh: 350,
15926
15927         /**
15928          * Flag that indicates that either the drag pixel threshold or the
15929          * mousdown time threshold has been met
15930          * @property dragThreshMet
15931          * @type boolean
15932          * @private
15933          * @static
15934          */
15935         dragThreshMet: false,
15936
15937         /**
15938          * Timeout used for the click time threshold
15939          * @property clickTimeout
15940          * @type Object
15941          * @private
15942          * @static
15943          */
15944         clickTimeout: null,
15945
15946         /**
15947          * The X position of the mousedown event stored for later use when a
15948          * drag threshold is met.
15949          * @property startX
15950          * @type int
15951          * @private
15952          * @static
15953          */
15954         startX: 0,
15955
15956         /**
15957          * The Y position of the mousedown event stored for later use when a
15958          * drag threshold is met.
15959          * @property startY
15960          * @type int
15961          * @private
15962          * @static
15963          */
15964         startY: 0,
15965
15966         /**
15967          * Each DragDrop instance must be registered with the DragDropMgr.
15968          * This is executed in DragDrop.init()
15969          * @method regDragDrop
15970          * @param {DragDrop} oDD the DragDrop object to register
15971          * @param {String} sGroup the name of the group this element belongs to
15972          * @static
15973          */
15974         regDragDrop: function(oDD, sGroup) {
15975             if (!this.initialized) { this.init(); }
15976
15977             if (!this.ids[sGroup]) {
15978                 this.ids[sGroup] = {};
15979             }
15980             this.ids[sGroup][oDD.id] = oDD;
15981         },
15982
15983         /**
15984          * Removes the supplied dd instance from the supplied group. Executed
15985          * by DragDrop.removeFromGroup, so don't call this function directly.
15986          * @method removeDDFromGroup
15987          * @private
15988          * @static
15989          */
15990         removeDDFromGroup: function(oDD, sGroup) {
15991             if (!this.ids[sGroup]) {
15992                 this.ids[sGroup] = {};
15993             }
15994
15995             var obj = this.ids[sGroup];
15996             if (obj && obj[oDD.id]) {
15997                 delete obj[oDD.id];
15998             }
15999         },
16000
16001         /**
16002          * Unregisters a drag and drop item.  This is executed in
16003          * DragDrop.unreg, use that method instead of calling this directly.
16004          * @method _remove
16005          * @private
16006          * @static
16007          */
16008         _remove: function(oDD) {
16009             for (var g in oDD.groups) {
16010                 if (g && this.ids[g][oDD.id]) {
16011                     delete this.ids[g][oDD.id];
16012                 }
16013             }
16014             delete this.handleIds[oDD.id];
16015         },
16016
16017         /**
16018          * Each DragDrop handle element must be registered.  This is done
16019          * automatically when executing DragDrop.setHandleElId()
16020          * @method regHandle
16021          * @param {String} sDDId the DragDrop id this element is a handle for
16022          * @param {String} sHandleId the id of the element that is the drag
16023          * handle
16024          * @static
16025          */
16026         regHandle: function(sDDId, sHandleId) {
16027             if (!this.handleIds[sDDId]) {
16028                 this.handleIds[sDDId] = {};
16029             }
16030             this.handleIds[sDDId][sHandleId] = sHandleId;
16031         },
16032
16033         /**
16034          * Utility function to determine if a given element has been
16035          * registered as a drag drop item.
16036          * @method isDragDrop
16037          * @param {String} id the element id to check
16038          * @return {boolean} true if this element is a DragDrop item,
16039          * false otherwise
16040          * @static
16041          */
16042         isDragDrop: function(id) {
16043             return ( this.getDDById(id) ) ? true : false;
16044         },
16045
16046         /**
16047          * Returns the drag and drop instances that are in all groups the
16048          * passed in instance belongs to.
16049          * @method getRelated
16050          * @param {DragDrop} p_oDD the obj to get related data for
16051          * @param {boolean} bTargetsOnly if true, only return targetable objs
16052          * @return {DragDrop[]} the related instances
16053          * @static
16054          */
16055         getRelated: function(p_oDD, bTargetsOnly) {
16056             var oDDs = [];
16057             for (var i in p_oDD.groups) {
16058                 for (j in this.ids[i]) {
16059                     var dd = this.ids[i][j];
16060                     if (! this.isTypeOfDD(dd)) {
16061                         continue;
16062                     }
16063                     if (!bTargetsOnly || dd.isTarget) {
16064                         oDDs[oDDs.length] = dd;
16065                     }
16066                 }
16067             }
16068
16069             return oDDs;
16070         },
16071
16072         /**
16073          * Returns true if the specified dd target is a legal target for
16074          * the specifice drag obj
16075          * @method isLegalTarget
16076          * @param {DragDrop} the drag obj
16077          * @param {DragDrop} the target
16078          * @return {boolean} true if the target is a legal target for the
16079          * dd obj
16080          * @static
16081          */
16082         isLegalTarget: function (oDD, oTargetDD) {
16083             var targets = this.getRelated(oDD, true);
16084             for (var i=0, len=targets.length;i<len;++i) {
16085                 if (targets[i].id == oTargetDD.id) {
16086                     return true;
16087                 }
16088             }
16089
16090             return false;
16091         },
16092
16093         /**
16094          * My goal is to be able to transparently determine if an object is
16095          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16096          * returns "object", oDD.constructor.toString() always returns
16097          * "DragDrop" and not the name of the subclass.  So for now it just
16098          * evaluates a well-known variable in DragDrop.
16099          * @method isTypeOfDD
16100          * @param {Object} the object to evaluate
16101          * @return {boolean} true if typeof oDD = DragDrop
16102          * @static
16103          */
16104         isTypeOfDD: function (oDD) {
16105             return (oDD && oDD.__ygDragDrop);
16106         },
16107
16108         /**
16109          * Utility function to determine if a given element has been
16110          * registered as a drag drop handle for the given Drag Drop object.
16111          * @method isHandle
16112          * @param {String} id the element id to check
16113          * @return {boolean} true if this element is a DragDrop handle, false
16114          * otherwise
16115          * @static
16116          */
16117         isHandle: function(sDDId, sHandleId) {
16118             return ( this.handleIds[sDDId] &&
16119                             this.handleIds[sDDId][sHandleId] );
16120         },
16121
16122         /**
16123          * Returns the DragDrop instance for a given id
16124          * @method getDDById
16125          * @param {String} id the id of the DragDrop object
16126          * @return {DragDrop} the drag drop object, null if it is not found
16127          * @static
16128          */
16129         getDDById: function(id) {
16130             for (var i in this.ids) {
16131                 if (this.ids[i][id]) {
16132                     return this.ids[i][id];
16133                 }
16134             }
16135             return null;
16136         },
16137
16138         /**
16139          * Fired after a registered DragDrop object gets the mousedown event.
16140          * Sets up the events required to track the object being dragged
16141          * @method handleMouseDown
16142          * @param {Event} e the event
16143          * @param oDD the DragDrop object being dragged
16144          * @private
16145          * @static
16146          */
16147         handleMouseDown: function(e, oDD) {
16148             if(Roo.QuickTips){
16149                 Roo.QuickTips.disable();
16150             }
16151             this.currentTarget = e.getTarget();
16152
16153             this.dragCurrent = oDD;
16154
16155             var el = oDD.getEl();
16156
16157             // track start position
16158             this.startX = e.getPageX();
16159             this.startY = e.getPageY();
16160
16161             this.deltaX = this.startX - el.offsetLeft;
16162             this.deltaY = this.startY - el.offsetTop;
16163
16164             this.dragThreshMet = false;
16165
16166             this.clickTimeout = setTimeout(
16167                     function() {
16168                         var DDM = Roo.dd.DDM;
16169                         DDM.startDrag(DDM.startX, DDM.startY);
16170                     },
16171                     this.clickTimeThresh );
16172         },
16173
16174         /**
16175          * Fired when either the drag pixel threshol or the mousedown hold
16176          * time threshold has been met.
16177          * @method startDrag
16178          * @param x {int} the X position of the original mousedown
16179          * @param y {int} the Y position of the original mousedown
16180          * @static
16181          */
16182         startDrag: function(x, y) {
16183             clearTimeout(this.clickTimeout);
16184             if (this.dragCurrent) {
16185                 this.dragCurrent.b4StartDrag(x, y);
16186                 this.dragCurrent.startDrag(x, y);
16187             }
16188             this.dragThreshMet = true;
16189         },
16190
16191         /**
16192          * Internal function to handle the mouseup event.  Will be invoked
16193          * from the context of the document.
16194          * @method handleMouseUp
16195          * @param {Event} e the event
16196          * @private
16197          * @static
16198          */
16199         handleMouseUp: function(e) {
16200
16201             if(Roo.QuickTips){
16202                 Roo.QuickTips.enable();
16203             }
16204             if (! this.dragCurrent) {
16205                 return;
16206             }
16207
16208             clearTimeout(this.clickTimeout);
16209
16210             if (this.dragThreshMet) {
16211                 this.fireEvents(e, true);
16212             } else {
16213             }
16214
16215             this.stopDrag(e);
16216
16217             this.stopEvent(e);
16218         },
16219
16220         /**
16221          * Utility to stop event propagation and event default, if these
16222          * features are turned on.
16223          * @method stopEvent
16224          * @param {Event} e the event as returned by this.getEvent()
16225          * @static
16226          */
16227         stopEvent: function(e){
16228             if(this.stopPropagation) {
16229                 e.stopPropagation();
16230             }
16231
16232             if (this.preventDefault) {
16233                 e.preventDefault();
16234             }
16235         },
16236
16237         /**
16238          * Internal function to clean up event handlers after the drag
16239          * operation is complete
16240          * @method stopDrag
16241          * @param {Event} e the event
16242          * @private
16243          * @static
16244          */
16245         stopDrag: function(e) {
16246             // Fire the drag end event for the item that was dragged
16247             if (this.dragCurrent) {
16248                 if (this.dragThreshMet) {
16249                     this.dragCurrent.b4EndDrag(e);
16250                     this.dragCurrent.endDrag(e);
16251                 }
16252
16253                 this.dragCurrent.onMouseUp(e);
16254             }
16255
16256             this.dragCurrent = null;
16257             this.dragOvers = {};
16258         },
16259
16260         /**
16261          * Internal function to handle the mousemove event.  Will be invoked
16262          * from the context of the html element.
16263          *
16264          * @TODO figure out what we can do about mouse events lost when the
16265          * user drags objects beyond the window boundary.  Currently we can
16266          * detect this in internet explorer by verifying that the mouse is
16267          * down during the mousemove event.  Firefox doesn't give us the
16268          * button state on the mousemove event.
16269          * @method handleMouseMove
16270          * @param {Event} e the event
16271          * @private
16272          * @static
16273          */
16274         handleMouseMove: function(e) {
16275             if (! this.dragCurrent) {
16276                 return true;
16277             }
16278
16279             // var button = e.which || e.button;
16280
16281             // check for IE mouseup outside of page boundary
16282             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16283                 this.stopEvent(e);
16284                 return this.handleMouseUp(e);
16285             }
16286
16287             if (!this.dragThreshMet) {
16288                 var diffX = Math.abs(this.startX - e.getPageX());
16289                 var diffY = Math.abs(this.startY - e.getPageY());
16290                 if (diffX > this.clickPixelThresh ||
16291                             diffY > this.clickPixelThresh) {
16292                     this.startDrag(this.startX, this.startY);
16293                 }
16294             }
16295
16296             if (this.dragThreshMet) {
16297                 this.dragCurrent.b4Drag(e);
16298                 this.dragCurrent.onDrag(e);
16299                 if(!this.dragCurrent.moveOnly){
16300                     this.fireEvents(e, false);
16301                 }
16302             }
16303
16304             this.stopEvent(e);
16305
16306             return true;
16307         },
16308
16309         /**
16310          * Iterates over all of the DragDrop elements to find ones we are
16311          * hovering over or dropping on
16312          * @method fireEvents
16313          * @param {Event} e the event
16314          * @param {boolean} isDrop is this a drop op or a mouseover op?
16315          * @private
16316          * @static
16317          */
16318         fireEvents: function(e, isDrop) {
16319             var dc = this.dragCurrent;
16320
16321             // If the user did the mouse up outside of the window, we could
16322             // get here even though we have ended the drag.
16323             if (!dc || dc.isLocked()) {
16324                 return;
16325             }
16326
16327             var pt = e.getPoint();
16328
16329             // cache the previous dragOver array
16330             var oldOvers = [];
16331
16332             var outEvts   = [];
16333             var overEvts  = [];
16334             var dropEvts  = [];
16335             var enterEvts = [];
16336
16337             // Check to see if the object(s) we were hovering over is no longer
16338             // being hovered over so we can fire the onDragOut event
16339             for (var i in this.dragOvers) {
16340
16341                 var ddo = this.dragOvers[i];
16342
16343                 if (! this.isTypeOfDD(ddo)) {
16344                     continue;
16345                 }
16346
16347                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16348                     outEvts.push( ddo );
16349                 }
16350
16351                 oldOvers[i] = true;
16352                 delete this.dragOvers[i];
16353             }
16354
16355             for (var sGroup in dc.groups) {
16356
16357                 if ("string" != typeof sGroup) {
16358                     continue;
16359                 }
16360
16361                 for (i in this.ids[sGroup]) {
16362                     var oDD = this.ids[sGroup][i];
16363                     if (! this.isTypeOfDD(oDD)) {
16364                         continue;
16365                     }
16366
16367                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16368                         if (this.isOverTarget(pt, oDD, this.mode)) {
16369                             // look for drop interactions
16370                             if (isDrop) {
16371                                 dropEvts.push( oDD );
16372                             // look for drag enter and drag over interactions
16373                             } else {
16374
16375                                 // initial drag over: dragEnter fires
16376                                 if (!oldOvers[oDD.id]) {
16377                                     enterEvts.push( oDD );
16378                                 // subsequent drag overs: dragOver fires
16379                                 } else {
16380                                     overEvts.push( oDD );
16381                                 }
16382
16383                                 this.dragOvers[oDD.id] = oDD;
16384                             }
16385                         }
16386                     }
16387                 }
16388             }
16389
16390             if (this.mode) {
16391                 if (outEvts.length) {
16392                     dc.b4DragOut(e, outEvts);
16393                     dc.onDragOut(e, outEvts);
16394                 }
16395
16396                 if (enterEvts.length) {
16397                     dc.onDragEnter(e, enterEvts);
16398                 }
16399
16400                 if (overEvts.length) {
16401                     dc.b4DragOver(e, overEvts);
16402                     dc.onDragOver(e, overEvts);
16403                 }
16404
16405                 if (dropEvts.length) {
16406                     dc.b4DragDrop(e, dropEvts);
16407                     dc.onDragDrop(e, dropEvts);
16408                 }
16409
16410             } else {
16411                 // fire dragout events
16412                 var len = 0;
16413                 for (i=0, len=outEvts.length; i<len; ++i) {
16414                     dc.b4DragOut(e, outEvts[i].id);
16415                     dc.onDragOut(e, outEvts[i].id);
16416                 }
16417
16418                 // fire enter events
16419                 for (i=0,len=enterEvts.length; i<len; ++i) {
16420                     // dc.b4DragEnter(e, oDD.id);
16421                     dc.onDragEnter(e, enterEvts[i].id);
16422                 }
16423
16424                 // fire over events
16425                 for (i=0,len=overEvts.length; i<len; ++i) {
16426                     dc.b4DragOver(e, overEvts[i].id);
16427                     dc.onDragOver(e, overEvts[i].id);
16428                 }
16429
16430                 // fire drop events
16431                 for (i=0, len=dropEvts.length; i<len; ++i) {
16432                     dc.b4DragDrop(e, dropEvts[i].id);
16433                     dc.onDragDrop(e, dropEvts[i].id);
16434                 }
16435
16436             }
16437
16438             // notify about a drop that did not find a target
16439             if (isDrop && !dropEvts.length) {
16440                 dc.onInvalidDrop(e);
16441             }
16442
16443         },
16444
16445         /**
16446          * Helper function for getting the best match from the list of drag
16447          * and drop objects returned by the drag and drop events when we are
16448          * in INTERSECT mode.  It returns either the first object that the
16449          * cursor is over, or the object that has the greatest overlap with
16450          * the dragged element.
16451          * @method getBestMatch
16452          * @param  {DragDrop[]} dds The array of drag and drop objects
16453          * targeted
16454          * @return {DragDrop}       The best single match
16455          * @static
16456          */
16457         getBestMatch: function(dds) {
16458             var winner = null;
16459             // Return null if the input is not what we expect
16460             //if (!dds || !dds.length || dds.length == 0) {
16461                // winner = null;
16462             // If there is only one item, it wins
16463             //} else if (dds.length == 1) {
16464
16465             var len = dds.length;
16466
16467             if (len == 1) {
16468                 winner = dds[0];
16469             } else {
16470                 // Loop through the targeted items
16471                 for (var i=0; i<len; ++i) {
16472                     var dd = dds[i];
16473                     // If the cursor is over the object, it wins.  If the
16474                     // cursor is over multiple matches, the first one we come
16475                     // to wins.
16476                     if (dd.cursorIsOver) {
16477                         winner = dd;
16478                         break;
16479                     // Otherwise the object with the most overlap wins
16480                     } else {
16481                         if (!winner ||
16482                             winner.overlap.getArea() < dd.overlap.getArea()) {
16483                             winner = dd;
16484                         }
16485                     }
16486                 }
16487             }
16488
16489             return winner;
16490         },
16491
16492         /**
16493          * Refreshes the cache of the top-left and bottom-right points of the
16494          * drag and drop objects in the specified group(s).  This is in the
16495          * format that is stored in the drag and drop instance, so typical
16496          * usage is:
16497          * <code>
16498          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16499          * </code>
16500          * Alternatively:
16501          * <code>
16502          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16503          * </code>
16504          * @TODO this really should be an indexed array.  Alternatively this
16505          * method could accept both.
16506          * @method refreshCache
16507          * @param {Object} groups an associative array of groups to refresh
16508          * @static
16509          */
16510         refreshCache: function(groups) {
16511             for (var sGroup in groups) {
16512                 if ("string" != typeof sGroup) {
16513                     continue;
16514                 }
16515                 for (var i in this.ids[sGroup]) {
16516                     var oDD = this.ids[sGroup][i];
16517
16518                     if (this.isTypeOfDD(oDD)) {
16519                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16520                         var loc = this.getLocation(oDD);
16521                         if (loc) {
16522                             this.locationCache[oDD.id] = loc;
16523                         } else {
16524                             delete this.locationCache[oDD.id];
16525                             // this will unregister the drag and drop object if
16526                             // the element is not in a usable state
16527                             // oDD.unreg();
16528                         }
16529                     }
16530                 }
16531             }
16532         },
16533
16534         /**
16535          * This checks to make sure an element exists and is in the DOM.  The
16536          * main purpose is to handle cases where innerHTML is used to remove
16537          * drag and drop objects from the DOM.  IE provides an 'unspecified
16538          * error' when trying to access the offsetParent of such an element
16539          * @method verifyEl
16540          * @param {HTMLElement} el the element to check
16541          * @return {boolean} true if the element looks usable
16542          * @static
16543          */
16544         verifyEl: function(el) {
16545             if (el) {
16546                 var parent;
16547                 if(Roo.isIE){
16548                     try{
16549                         parent = el.offsetParent;
16550                     }catch(e){}
16551                 }else{
16552                     parent = el.offsetParent;
16553                 }
16554                 if (parent) {
16555                     return true;
16556                 }
16557             }
16558
16559             return false;
16560         },
16561
16562         /**
16563          * Returns a Region object containing the drag and drop element's position
16564          * and size, including the padding configured for it
16565          * @method getLocation
16566          * @param {DragDrop} oDD the drag and drop object to get the
16567          *                       location for
16568          * @return {Roo.lib.Region} a Region object representing the total area
16569          *                             the element occupies, including any padding
16570          *                             the instance is configured for.
16571          * @static
16572          */
16573         getLocation: function(oDD) {
16574             if (! this.isTypeOfDD(oDD)) {
16575                 return null;
16576             }
16577
16578             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16579
16580             try {
16581                 pos= Roo.lib.Dom.getXY(el);
16582             } catch (e) { }
16583
16584             if (!pos) {
16585                 return null;
16586             }
16587
16588             x1 = pos[0];
16589             x2 = x1 + el.offsetWidth;
16590             y1 = pos[1];
16591             y2 = y1 + el.offsetHeight;
16592
16593             t = y1 - oDD.padding[0];
16594             r = x2 + oDD.padding[1];
16595             b = y2 + oDD.padding[2];
16596             l = x1 - oDD.padding[3];
16597
16598             return new Roo.lib.Region( t, r, b, l );
16599         },
16600
16601         /**
16602          * Checks the cursor location to see if it over the target
16603          * @method isOverTarget
16604          * @param {Roo.lib.Point} pt The point to evaluate
16605          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16606          * @return {boolean} true if the mouse is over the target
16607          * @private
16608          * @static
16609          */
16610         isOverTarget: function(pt, oTarget, intersect) {
16611             // use cache if available
16612             var loc = this.locationCache[oTarget.id];
16613             if (!loc || !this.useCache) {
16614                 loc = this.getLocation(oTarget);
16615                 this.locationCache[oTarget.id] = loc;
16616
16617             }
16618
16619             if (!loc) {
16620                 return false;
16621             }
16622
16623             oTarget.cursorIsOver = loc.contains( pt );
16624
16625             // DragDrop is using this as a sanity check for the initial mousedown
16626             // in this case we are done.  In POINT mode, if the drag obj has no
16627             // contraints, we are also done. Otherwise we need to evaluate the
16628             // location of the target as related to the actual location of the
16629             // dragged element.
16630             var dc = this.dragCurrent;
16631             if (!dc || !dc.getTargetCoord ||
16632                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16633                 return oTarget.cursorIsOver;
16634             }
16635
16636             oTarget.overlap = null;
16637
16638             // Get the current location of the drag element, this is the
16639             // location of the mouse event less the delta that represents
16640             // where the original mousedown happened on the element.  We
16641             // need to consider constraints and ticks as well.
16642             var pos = dc.getTargetCoord(pt.x, pt.y);
16643
16644             var el = dc.getDragEl();
16645             var curRegion = new Roo.lib.Region( pos.y,
16646                                                    pos.x + el.offsetWidth,
16647                                                    pos.y + el.offsetHeight,
16648                                                    pos.x );
16649
16650             var overlap = curRegion.intersect(loc);
16651
16652             if (overlap) {
16653                 oTarget.overlap = overlap;
16654                 return (intersect) ? true : oTarget.cursorIsOver;
16655             } else {
16656                 return false;
16657             }
16658         },
16659
16660         /**
16661          * unload event handler
16662          * @method _onUnload
16663          * @private
16664          * @static
16665          */
16666         _onUnload: function(e, me) {
16667             Roo.dd.DragDropMgr.unregAll();
16668         },
16669
16670         /**
16671          * Cleans up the drag and drop events and objects.
16672          * @method unregAll
16673          * @private
16674          * @static
16675          */
16676         unregAll: function() {
16677
16678             if (this.dragCurrent) {
16679                 this.stopDrag();
16680                 this.dragCurrent = null;
16681             }
16682
16683             this._execOnAll("unreg", []);
16684
16685             for (i in this.elementCache) {
16686                 delete this.elementCache[i];
16687             }
16688
16689             this.elementCache = {};
16690             this.ids = {};
16691         },
16692
16693         /**
16694          * A cache of DOM elements
16695          * @property elementCache
16696          * @private
16697          * @static
16698          */
16699         elementCache: {},
16700
16701         /**
16702          * Get the wrapper for the DOM element specified
16703          * @method getElWrapper
16704          * @param {String} id the id of the element to get
16705          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16706          * @private
16707          * @deprecated This wrapper isn't that useful
16708          * @static
16709          */
16710         getElWrapper: function(id) {
16711             var oWrapper = this.elementCache[id];
16712             if (!oWrapper || !oWrapper.el) {
16713                 oWrapper = this.elementCache[id] =
16714                     new this.ElementWrapper(Roo.getDom(id));
16715             }
16716             return oWrapper;
16717         },
16718
16719         /**
16720          * Returns the actual DOM element
16721          * @method getElement
16722          * @param {String} id the id of the elment to get
16723          * @return {Object} The element
16724          * @deprecated use Roo.getDom instead
16725          * @static
16726          */
16727         getElement: function(id) {
16728             return Roo.getDom(id);
16729         },
16730
16731         /**
16732          * Returns the style property for the DOM element (i.e.,
16733          * document.getElById(id).style)
16734          * @method getCss
16735          * @param {String} id the id of the elment to get
16736          * @return {Object} The style property of the element
16737          * @deprecated use Roo.getDom instead
16738          * @static
16739          */
16740         getCss: function(id) {
16741             var el = Roo.getDom(id);
16742             return (el) ? el.style : null;
16743         },
16744
16745         /**
16746          * Inner class for cached elements
16747          * @class DragDropMgr.ElementWrapper
16748          * @for DragDropMgr
16749          * @private
16750          * @deprecated
16751          */
16752         ElementWrapper: function(el) {
16753                 /**
16754                  * The element
16755                  * @property el
16756                  */
16757                 this.el = el || null;
16758                 /**
16759                  * The element id
16760                  * @property id
16761                  */
16762                 this.id = this.el && el.id;
16763                 /**
16764                  * A reference to the style property
16765                  * @property css
16766                  */
16767                 this.css = this.el && el.style;
16768             },
16769
16770         /**
16771          * Returns the X position of an html element
16772          * @method getPosX
16773          * @param el the element for which to get the position
16774          * @return {int} the X coordinate
16775          * @for DragDropMgr
16776          * @deprecated use Roo.lib.Dom.getX instead
16777          * @static
16778          */
16779         getPosX: function(el) {
16780             return Roo.lib.Dom.getX(el);
16781         },
16782
16783         /**
16784          * Returns the Y position of an html element
16785          * @method getPosY
16786          * @param el the element for which to get the position
16787          * @return {int} the Y coordinate
16788          * @deprecated use Roo.lib.Dom.getY instead
16789          * @static
16790          */
16791         getPosY: function(el) {
16792             return Roo.lib.Dom.getY(el);
16793         },
16794
16795         /**
16796          * Swap two nodes.  In IE, we use the native method, for others we
16797          * emulate the IE behavior
16798          * @method swapNode
16799          * @param n1 the first node to swap
16800          * @param n2 the other node to swap
16801          * @static
16802          */
16803         swapNode: function(n1, n2) {
16804             if (n1.swapNode) {
16805                 n1.swapNode(n2);
16806             } else {
16807                 var p = n2.parentNode;
16808                 var s = n2.nextSibling;
16809
16810                 if (s == n1) {
16811                     p.insertBefore(n1, n2);
16812                 } else if (n2 == n1.nextSibling) {
16813                     p.insertBefore(n2, n1);
16814                 } else {
16815                     n1.parentNode.replaceChild(n2, n1);
16816                     p.insertBefore(n1, s);
16817                 }
16818             }
16819         },
16820
16821         /**
16822          * Returns the current scroll position
16823          * @method getScroll
16824          * @private
16825          * @static
16826          */
16827         getScroll: function () {
16828             var t, l, dde=document.documentElement, db=document.body;
16829             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16830                 t = dde.scrollTop;
16831                 l = dde.scrollLeft;
16832             } else if (db) {
16833                 t = db.scrollTop;
16834                 l = db.scrollLeft;
16835             } else {
16836
16837             }
16838             return { top: t, left: l };
16839         },
16840
16841         /**
16842          * Returns the specified element style property
16843          * @method getStyle
16844          * @param {HTMLElement} el          the element
16845          * @param {string}      styleProp   the style property
16846          * @return {string} The value of the style property
16847          * @deprecated use Roo.lib.Dom.getStyle
16848          * @static
16849          */
16850         getStyle: function(el, styleProp) {
16851             return Roo.fly(el).getStyle(styleProp);
16852         },
16853
16854         /**
16855          * Gets the scrollTop
16856          * @method getScrollTop
16857          * @return {int} the document's scrollTop
16858          * @static
16859          */
16860         getScrollTop: function () { return this.getScroll().top; },
16861
16862         /**
16863          * Gets the scrollLeft
16864          * @method getScrollLeft
16865          * @return {int} the document's scrollTop
16866          * @static
16867          */
16868         getScrollLeft: function () { return this.getScroll().left; },
16869
16870         /**
16871          * Sets the x/y position of an element to the location of the
16872          * target element.
16873          * @method moveToEl
16874          * @param {HTMLElement} moveEl      The element to move
16875          * @param {HTMLElement} targetEl    The position reference element
16876          * @static
16877          */
16878         moveToEl: function (moveEl, targetEl) {
16879             var aCoord = Roo.lib.Dom.getXY(targetEl);
16880             Roo.lib.Dom.setXY(moveEl, aCoord);
16881         },
16882
16883         /**
16884          * Numeric array sort function
16885          * @method numericSort
16886          * @static
16887          */
16888         numericSort: function(a, b) { return (a - b); },
16889
16890         /**
16891          * Internal counter
16892          * @property _timeoutCount
16893          * @private
16894          * @static
16895          */
16896         _timeoutCount: 0,
16897
16898         /**
16899          * Trying to make the load order less important.  Without this we get
16900          * an error if this file is loaded before the Event Utility.
16901          * @method _addListeners
16902          * @private
16903          * @static
16904          */
16905         _addListeners: function() {
16906             var DDM = Roo.dd.DDM;
16907             if ( Roo.lib.Event && document ) {
16908                 DDM._onLoad();
16909             } else {
16910                 if (DDM._timeoutCount > 2000) {
16911                 } else {
16912                     setTimeout(DDM._addListeners, 10);
16913                     if (document && document.body) {
16914                         DDM._timeoutCount += 1;
16915                     }
16916                 }
16917             }
16918         },
16919
16920         /**
16921          * Recursively searches the immediate parent and all child nodes for
16922          * the handle element in order to determine wheter or not it was
16923          * clicked.
16924          * @method handleWasClicked
16925          * @param node the html element to inspect
16926          * @static
16927          */
16928         handleWasClicked: function(node, id) {
16929             if (this.isHandle(id, node.id)) {
16930                 return true;
16931             } else {
16932                 // check to see if this is a text node child of the one we want
16933                 var p = node.parentNode;
16934
16935                 while (p) {
16936                     if (this.isHandle(id, p.id)) {
16937                         return true;
16938                     } else {
16939                         p = p.parentNode;
16940                     }
16941                 }
16942             }
16943
16944             return false;
16945         }
16946
16947     };
16948
16949 }();
16950
16951 // shorter alias, save a few bytes
16952 Roo.dd.DDM = Roo.dd.DragDropMgr;
16953 Roo.dd.DDM._addListeners();
16954
16955 }/*
16956  * Based on:
16957  * Ext JS Library 1.1.1
16958  * Copyright(c) 2006-2007, Ext JS, LLC.
16959  *
16960  * Originally Released Under LGPL - original licence link has changed is not relivant.
16961  *
16962  * Fork - LGPL
16963  * <script type="text/javascript">
16964  */
16965
16966 /**
16967  * @class Roo.dd.DD
16968  * A DragDrop implementation where the linked element follows the
16969  * mouse cursor during a drag.
16970  * @extends Roo.dd.DragDrop
16971  * @constructor
16972  * @param {String} id the id of the linked element
16973  * @param {String} sGroup the group of related DragDrop items
16974  * @param {object} config an object containing configurable attributes
16975  *                Valid properties for DD:
16976  *                    scroll
16977  */
16978 Roo.dd.DD = function(id, sGroup, config) {
16979     if (id) {
16980         this.init(id, sGroup, config);
16981     }
16982 };
16983
16984 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16985
16986     /**
16987      * When set to true, the utility automatically tries to scroll the browser
16988      * window wehn a drag and drop element is dragged near the viewport boundary.
16989      * Defaults to true.
16990      * @property scroll
16991      * @type boolean
16992      */
16993     scroll: true,
16994
16995     /**
16996      * Sets the pointer offset to the distance between the linked element's top
16997      * left corner and the location the element was clicked
16998      * @method autoOffset
16999      * @param {int} iPageX the X coordinate of the click
17000      * @param {int} iPageY the Y coordinate of the click
17001      */
17002     autoOffset: function(iPageX, iPageY) {
17003         var x = iPageX - this.startPageX;
17004         var y = iPageY - this.startPageY;
17005         this.setDelta(x, y);
17006     },
17007
17008     /**
17009      * Sets the pointer offset.  You can call this directly to force the
17010      * offset to be in a particular location (e.g., pass in 0,0 to set it
17011      * to the center of the object)
17012      * @method setDelta
17013      * @param {int} iDeltaX the distance from the left
17014      * @param {int} iDeltaY the distance from the top
17015      */
17016     setDelta: function(iDeltaX, iDeltaY) {
17017         this.deltaX = iDeltaX;
17018         this.deltaY = iDeltaY;
17019     },
17020
17021     /**
17022      * Sets the drag element to the location of the mousedown or click event,
17023      * maintaining the cursor location relative to the location on the element
17024      * that was clicked.  Override this if you want to place the element in a
17025      * location other than where the cursor is.
17026      * @method setDragElPos
17027      * @param {int} iPageX the X coordinate of the mousedown or drag event
17028      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17029      */
17030     setDragElPos: function(iPageX, iPageY) {
17031         // the first time we do this, we are going to check to make sure
17032         // the element has css positioning
17033
17034         var el = this.getDragEl();
17035         this.alignElWithMouse(el, iPageX, iPageY);
17036     },
17037
17038     /**
17039      * Sets the element to the location of the mousedown or click event,
17040      * maintaining the cursor location relative to the location on the element
17041      * that was clicked.  Override this if you want to place the element in a
17042      * location other than where the cursor is.
17043      * @method alignElWithMouse
17044      * @param {HTMLElement} el the element to move
17045      * @param {int} iPageX the X coordinate of the mousedown or drag event
17046      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17047      */
17048     alignElWithMouse: function(el, iPageX, iPageY) {
17049         var oCoord = this.getTargetCoord(iPageX, iPageY);
17050         var fly = el.dom ? el : Roo.fly(el);
17051         if (!this.deltaSetXY) {
17052             var aCoord = [oCoord.x, oCoord.y];
17053             fly.setXY(aCoord);
17054             var newLeft = fly.getLeft(true);
17055             var newTop  = fly.getTop(true);
17056             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17057         } else {
17058             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17059         }
17060
17061         this.cachePosition(oCoord.x, oCoord.y);
17062         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17063         return oCoord;
17064     },
17065
17066     /**
17067      * Saves the most recent position so that we can reset the constraints and
17068      * tick marks on-demand.  We need to know this so that we can calculate the
17069      * number of pixels the element is offset from its original position.
17070      * @method cachePosition
17071      * @param iPageX the current x position (optional, this just makes it so we
17072      * don't have to look it up again)
17073      * @param iPageY the current y position (optional, this just makes it so we
17074      * don't have to look it up again)
17075      */
17076     cachePosition: function(iPageX, iPageY) {
17077         if (iPageX) {
17078             this.lastPageX = iPageX;
17079             this.lastPageY = iPageY;
17080         } else {
17081             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17082             this.lastPageX = aCoord[0];
17083             this.lastPageY = aCoord[1];
17084         }
17085     },
17086
17087     /**
17088      * Auto-scroll the window if the dragged object has been moved beyond the
17089      * visible window boundary.
17090      * @method autoScroll
17091      * @param {int} x the drag element's x position
17092      * @param {int} y the drag element's y position
17093      * @param {int} h the height of the drag element
17094      * @param {int} w the width of the drag element
17095      * @private
17096      */
17097     autoScroll: function(x, y, h, w) {
17098
17099         if (this.scroll) {
17100             // The client height
17101             var clientH = Roo.lib.Dom.getViewWidth();
17102
17103             // The client width
17104             var clientW = Roo.lib.Dom.getViewHeight();
17105
17106             // The amt scrolled down
17107             var st = this.DDM.getScrollTop();
17108
17109             // The amt scrolled right
17110             var sl = this.DDM.getScrollLeft();
17111
17112             // Location of the bottom of the element
17113             var bot = h + y;
17114
17115             // Location of the right of the element
17116             var right = w + x;
17117
17118             // The distance from the cursor to the bottom of the visible area,
17119             // adjusted so that we don't scroll if the cursor is beyond the
17120             // element drag constraints
17121             var toBot = (clientH + st - y - this.deltaY);
17122
17123             // The distance from the cursor to the right of the visible area
17124             var toRight = (clientW + sl - x - this.deltaX);
17125
17126
17127             // How close to the edge the cursor must be before we scroll
17128             // var thresh = (document.all) ? 100 : 40;
17129             var thresh = 40;
17130
17131             // How many pixels to scroll per autoscroll op.  This helps to reduce
17132             // clunky scrolling. IE is more sensitive about this ... it needs this
17133             // value to be higher.
17134             var scrAmt = (document.all) ? 80 : 30;
17135
17136             // Scroll down if we are near the bottom of the visible page and the
17137             // obj extends below the crease
17138             if ( bot > clientH && toBot < thresh ) {
17139                 window.scrollTo(sl, st + scrAmt);
17140             }
17141
17142             // Scroll up if the window is scrolled down and the top of the object
17143             // goes above the top border
17144             if ( y < st && st > 0 && y - st < thresh ) {
17145                 window.scrollTo(sl, st - scrAmt);
17146             }
17147
17148             // Scroll right if the obj is beyond the right border and the cursor is
17149             // near the border.
17150             if ( right > clientW && toRight < thresh ) {
17151                 window.scrollTo(sl + scrAmt, st);
17152             }
17153
17154             // Scroll left if the window has been scrolled to the right and the obj
17155             // extends past the left border
17156             if ( x < sl && sl > 0 && x - sl < thresh ) {
17157                 window.scrollTo(sl - scrAmt, st);
17158             }
17159         }
17160     },
17161
17162     /**
17163      * Finds the location the element should be placed if we want to move
17164      * it to where the mouse location less the click offset would place us.
17165      * @method getTargetCoord
17166      * @param {int} iPageX the X coordinate of the click
17167      * @param {int} iPageY the Y coordinate of the click
17168      * @return an object that contains the coordinates (Object.x and Object.y)
17169      * @private
17170      */
17171     getTargetCoord: function(iPageX, iPageY) {
17172
17173
17174         var x = iPageX - this.deltaX;
17175         var y = iPageY - this.deltaY;
17176
17177         if (this.constrainX) {
17178             if (x < this.minX) { x = this.minX; }
17179             if (x > this.maxX) { x = this.maxX; }
17180         }
17181
17182         if (this.constrainY) {
17183             if (y < this.minY) { y = this.minY; }
17184             if (y > this.maxY) { y = this.maxY; }
17185         }
17186
17187         x = this.getTick(x, this.xTicks);
17188         y = this.getTick(y, this.yTicks);
17189
17190
17191         return {x:x, y:y};
17192     },
17193
17194     /*
17195      * Sets up config options specific to this class. Overrides
17196      * Roo.dd.DragDrop, but all versions of this method through the
17197      * inheritance chain are called
17198      */
17199     applyConfig: function() {
17200         Roo.dd.DD.superclass.applyConfig.call(this);
17201         this.scroll = (this.config.scroll !== false);
17202     },
17203
17204     /*
17205      * Event that fires prior to the onMouseDown event.  Overrides
17206      * Roo.dd.DragDrop.
17207      */
17208     b4MouseDown: function(e) {
17209         // this.resetConstraints();
17210         this.autoOffset(e.getPageX(),
17211                             e.getPageY());
17212     },
17213
17214     /*
17215      * Event that fires prior to the onDrag event.  Overrides
17216      * Roo.dd.DragDrop.
17217      */
17218     b4Drag: function(e) {
17219         this.setDragElPos(e.getPageX(),
17220                             e.getPageY());
17221     },
17222
17223     toString: function() {
17224         return ("DD " + this.id);
17225     }
17226
17227     //////////////////////////////////////////////////////////////////////////
17228     // Debugging ygDragDrop events that can be overridden
17229     //////////////////////////////////////////////////////////////////////////
17230     /*
17231     startDrag: function(x, y) {
17232     },
17233
17234     onDrag: function(e) {
17235     },
17236
17237     onDragEnter: function(e, id) {
17238     },
17239
17240     onDragOver: function(e, id) {
17241     },
17242
17243     onDragOut: function(e, id) {
17244     },
17245
17246     onDragDrop: function(e, id) {
17247     },
17248
17249     endDrag: function(e) {
17250     }
17251
17252     */
17253
17254 });/*
17255  * Based on:
17256  * Ext JS Library 1.1.1
17257  * Copyright(c) 2006-2007, Ext JS, LLC.
17258  *
17259  * Originally Released Under LGPL - original licence link has changed is not relivant.
17260  *
17261  * Fork - LGPL
17262  * <script type="text/javascript">
17263  */
17264
17265 /**
17266  * @class Roo.dd.DDProxy
17267  * A DragDrop implementation that inserts an empty, bordered div into
17268  * the document that follows the cursor during drag operations.  At the time of
17269  * the click, the frame div is resized to the dimensions of the linked html
17270  * element, and moved to the exact location of the linked element.
17271  *
17272  * References to the "frame" element refer to the single proxy element that
17273  * was created to be dragged in place of all DDProxy elements on the
17274  * page.
17275  *
17276  * @extends Roo.dd.DD
17277  * @constructor
17278  * @param {String} id the id of the linked html element
17279  * @param {String} sGroup the group of related DragDrop objects
17280  * @param {object} config an object containing configurable attributes
17281  *                Valid properties for DDProxy in addition to those in DragDrop:
17282  *                   resizeFrame, centerFrame, dragElId
17283  */
17284 Roo.dd.DDProxy = function(id, sGroup, config) {
17285     if (id) {
17286         this.init(id, sGroup, config);
17287         this.initFrame();
17288     }
17289 };
17290
17291 /**
17292  * The default drag frame div id
17293  * @property Roo.dd.DDProxy.dragElId
17294  * @type String
17295  * @static
17296  */
17297 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17298
17299 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17300
17301     /**
17302      * By default we resize the drag frame to be the same size as the element
17303      * we want to drag (this is to get the frame effect).  We can turn it off
17304      * if we want a different behavior.
17305      * @property resizeFrame
17306      * @type boolean
17307      */
17308     resizeFrame: true,
17309
17310     /**
17311      * By default the frame is positioned exactly where the drag element is, so
17312      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17313      * you do not have constraints on the obj is to have the drag frame centered
17314      * around the cursor.  Set centerFrame to true for this effect.
17315      * @property centerFrame
17316      * @type boolean
17317      */
17318     centerFrame: false,
17319
17320     /**
17321      * Creates the proxy element if it does not yet exist
17322      * @method createFrame
17323      */
17324     createFrame: function() {
17325         var self = this;
17326         var body = document.body;
17327
17328         if (!body || !body.firstChild) {
17329             setTimeout( function() { self.createFrame(); }, 50 );
17330             return;
17331         }
17332
17333         var div = this.getDragEl();
17334
17335         if (!div) {
17336             div    = document.createElement("div");
17337             div.id = this.dragElId;
17338             var s  = div.style;
17339
17340             s.position   = "absolute";
17341             s.visibility = "hidden";
17342             s.cursor     = "move";
17343             s.border     = "2px solid #aaa";
17344             s.zIndex     = 999;
17345
17346             // appendChild can blow up IE if invoked prior to the window load event
17347             // while rendering a table.  It is possible there are other scenarios
17348             // that would cause this to happen as well.
17349             body.insertBefore(div, body.firstChild);
17350         }
17351     },
17352
17353     /**
17354      * Initialization for the drag frame element.  Must be called in the
17355      * constructor of all subclasses
17356      * @method initFrame
17357      */
17358     initFrame: function() {
17359         this.createFrame();
17360     },
17361
17362     applyConfig: function() {
17363         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17364
17365         this.resizeFrame = (this.config.resizeFrame !== false);
17366         this.centerFrame = (this.config.centerFrame);
17367         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17368     },
17369
17370     /**
17371      * Resizes the drag frame to the dimensions of the clicked object, positions
17372      * it over the object, and finally displays it
17373      * @method showFrame
17374      * @param {int} iPageX X click position
17375      * @param {int} iPageY Y click position
17376      * @private
17377      */
17378     showFrame: function(iPageX, iPageY) {
17379         var el = this.getEl();
17380         var dragEl = this.getDragEl();
17381         var s = dragEl.style;
17382
17383         this._resizeProxy();
17384
17385         if (this.centerFrame) {
17386             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17387                            Math.round(parseInt(s.height, 10)/2) );
17388         }
17389
17390         this.setDragElPos(iPageX, iPageY);
17391
17392         Roo.fly(dragEl).show();
17393     },
17394
17395     /**
17396      * The proxy is automatically resized to the dimensions of the linked
17397      * element when a drag is initiated, unless resizeFrame is set to false
17398      * @method _resizeProxy
17399      * @private
17400      */
17401     _resizeProxy: function() {
17402         if (this.resizeFrame) {
17403             var el = this.getEl();
17404             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17405         }
17406     },
17407
17408     // overrides Roo.dd.DragDrop
17409     b4MouseDown: function(e) {
17410         var x = e.getPageX();
17411         var y = e.getPageY();
17412         this.autoOffset(x, y);
17413         this.setDragElPos(x, y);
17414     },
17415
17416     // overrides Roo.dd.DragDrop
17417     b4StartDrag: function(x, y) {
17418         // show the drag frame
17419         this.showFrame(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4EndDrag: function(e) {
17424         Roo.fly(this.getDragEl()).hide();
17425     },
17426
17427     // overrides Roo.dd.DragDrop
17428     // By default we try to move the element to the last location of the frame.
17429     // This is so that the default behavior mirrors that of Roo.dd.DD.
17430     endDrag: function(e) {
17431
17432         var lel = this.getEl();
17433         var del = this.getDragEl();
17434
17435         // Show the drag frame briefly so we can get its position
17436         del.style.visibility = "";
17437
17438         this.beforeMove();
17439         // Hide the linked element before the move to get around a Safari
17440         // rendering bug.
17441         lel.style.visibility = "hidden";
17442         Roo.dd.DDM.moveToEl(lel, del);
17443         del.style.visibility = "hidden";
17444         lel.style.visibility = "";
17445
17446         this.afterDrag();
17447     },
17448
17449     beforeMove : function(){
17450
17451     },
17452
17453     afterDrag : function(){
17454
17455     },
17456
17457     toString: function() {
17458         return ("DDProxy " + this.id);
17459     }
17460
17461 });
17462 /*
17463  * Based on:
17464  * Ext JS Library 1.1.1
17465  * Copyright(c) 2006-2007, Ext JS, LLC.
17466  *
17467  * Originally Released Under LGPL - original licence link has changed is not relivant.
17468  *
17469  * Fork - LGPL
17470  * <script type="text/javascript">
17471  */
17472
17473  /**
17474  * @class Roo.dd.DDTarget
17475  * A DragDrop implementation that does not move, but can be a drop
17476  * target.  You would get the same result by simply omitting implementation
17477  * for the event callbacks, but this way we reduce the processing cost of the
17478  * event listener and the callbacks.
17479  * @extends Roo.dd.DragDrop
17480  * @constructor
17481  * @param {String} id the id of the element that is a drop target
17482  * @param {String} sGroup the group of related DragDrop objects
17483  * @param {object} config an object containing configurable attributes
17484  *                 Valid properties for DDTarget in addition to those in
17485  *                 DragDrop:
17486  *                    none
17487  */
17488 Roo.dd.DDTarget = function(id, sGroup, config) {
17489     if (id) {
17490         this.initTarget(id, sGroup, config);
17491     }
17492     if (config.listeners || config.events) { 
17493        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17494             listeners : config.listeners || {}, 
17495             events : config.events || {} 
17496         });    
17497     }
17498 };
17499
17500 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17501 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17502     toString: function() {
17503         return ("DDTarget " + this.id);
17504     }
17505 });
17506 /*
17507  * Based on:
17508  * Ext JS Library 1.1.1
17509  * Copyright(c) 2006-2007, Ext JS, LLC.
17510  *
17511  * Originally Released Under LGPL - original licence link has changed is not relivant.
17512  *
17513  * Fork - LGPL
17514  * <script type="text/javascript">
17515  */
17516  
17517
17518 /**
17519  * @class Roo.dd.ScrollManager
17520  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17521  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17522  * @singleton
17523  */
17524 Roo.dd.ScrollManager = function(){
17525     var ddm = Roo.dd.DragDropMgr;
17526     var els = {};
17527     var dragEl = null;
17528     var proc = {};
17529     
17530     var onStop = function(e){
17531         dragEl = null;
17532         clearProc();
17533     };
17534     
17535     var triggerRefresh = function(){
17536         if(ddm.dragCurrent){
17537              ddm.refreshCache(ddm.dragCurrent.groups);
17538         }
17539     };
17540     
17541     var doScroll = function(){
17542         if(ddm.dragCurrent){
17543             var dds = Roo.dd.ScrollManager;
17544             if(!dds.animate){
17545                 if(proc.el.scroll(proc.dir, dds.increment)){
17546                     triggerRefresh();
17547                 }
17548             }else{
17549                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17550             }
17551         }
17552     };
17553     
17554     var clearProc = function(){
17555         if(proc.id){
17556             clearInterval(proc.id);
17557         }
17558         proc.id = 0;
17559         proc.el = null;
17560         proc.dir = "";
17561     };
17562     
17563     var startProc = function(el, dir){
17564         clearProc();
17565         proc.el = el;
17566         proc.dir = dir;
17567         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17568     };
17569     
17570     var onFire = function(e, isDrop){
17571         if(isDrop || !ddm.dragCurrent){ return; }
17572         var dds = Roo.dd.ScrollManager;
17573         if(!dragEl || dragEl != ddm.dragCurrent){
17574             dragEl = ddm.dragCurrent;
17575             // refresh regions on drag start
17576             dds.refreshCache();
17577         }
17578         
17579         var xy = Roo.lib.Event.getXY(e);
17580         var pt = new Roo.lib.Point(xy[0], xy[1]);
17581         for(var id in els){
17582             var el = els[id], r = el._region;
17583             if(r && r.contains(pt) && el.isScrollable()){
17584                 if(r.bottom - pt.y <= dds.thresh){
17585                     if(proc.el != el){
17586                         startProc(el, "down");
17587                     }
17588                     return;
17589                 }else if(r.right - pt.x <= dds.thresh){
17590                     if(proc.el != el){
17591                         startProc(el, "left");
17592                     }
17593                     return;
17594                 }else if(pt.y - r.top <= dds.thresh){
17595                     if(proc.el != el){
17596                         startProc(el, "up");
17597                     }
17598                     return;
17599                 }else if(pt.x - r.left <= dds.thresh){
17600                     if(proc.el != el){
17601                         startProc(el, "right");
17602                     }
17603                     return;
17604                 }
17605             }
17606         }
17607         clearProc();
17608     };
17609     
17610     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17611     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17612     
17613     return {
17614         /**
17615          * Registers new overflow element(s) to auto scroll
17616          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17617          */
17618         register : function(el){
17619             if(el instanceof Array){
17620                 for(var i = 0, len = el.length; i < len; i++) {
17621                         this.register(el[i]);
17622                 }
17623             }else{
17624                 el = Roo.get(el);
17625                 els[el.id] = el;
17626             }
17627         },
17628         
17629         /**
17630          * Unregisters overflow element(s) so they are no longer scrolled
17631          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17632          */
17633         unregister : function(el){
17634             if(el instanceof Array){
17635                 for(var i = 0, len = el.length; i < len; i++) {
17636                         this.unregister(el[i]);
17637                 }
17638             }else{
17639                 el = Roo.get(el);
17640                 delete els[el.id];
17641             }
17642         },
17643         
17644         /**
17645          * The number of pixels from the edge of a container the pointer needs to be to 
17646          * trigger scrolling (defaults to 25)
17647          * @type Number
17648          */
17649         thresh : 25,
17650         
17651         /**
17652          * The number of pixels to scroll in each scroll increment (defaults to 50)
17653          * @type Number
17654          */
17655         increment : 100,
17656         
17657         /**
17658          * The frequency of scrolls in milliseconds (defaults to 500)
17659          * @type Number
17660          */
17661         frequency : 500,
17662         
17663         /**
17664          * True to animate the scroll (defaults to true)
17665          * @type Boolean
17666          */
17667         animate: true,
17668         
17669         /**
17670          * The animation duration in seconds - 
17671          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17672          * @type Number
17673          */
17674         animDuration: .4,
17675         
17676         /**
17677          * Manually trigger a cache refresh.
17678          */
17679         refreshCache : function(){
17680             for(var id in els){
17681                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17682                     els[id]._region = els[id].getRegion();
17683                 }
17684             }
17685         }
17686     };
17687 }();/*
17688  * Based on:
17689  * Ext JS Library 1.1.1
17690  * Copyright(c) 2006-2007, Ext JS, LLC.
17691  *
17692  * Originally Released Under LGPL - original licence link has changed is not relivant.
17693  *
17694  * Fork - LGPL
17695  * <script type="text/javascript">
17696  */
17697  
17698
17699 /**
17700  * @class Roo.dd.Registry
17701  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17702  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17703  * @singleton
17704  */
17705 Roo.dd.Registry = function(){
17706     var elements = {}; 
17707     var handles = {}; 
17708     var autoIdSeed = 0;
17709
17710     var getId = function(el, autogen){
17711         if(typeof el == "string"){
17712             return el;
17713         }
17714         var id = el.id;
17715         if(!id && autogen !== false){
17716             id = "roodd-" + (++autoIdSeed);
17717             el.id = id;
17718         }
17719         return id;
17720     };
17721     
17722     return {
17723     /**
17724      * Register a drag drop element
17725      * @param {String|HTMLElement} element The id or DOM node to register
17726      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17727      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17728      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17729      * populated in the data object (if applicable):
17730      * <pre>
17731 Value      Description<br />
17732 ---------  ------------------------------------------<br />
17733 handles    Array of DOM nodes that trigger dragging<br />
17734            for the element being registered<br />
17735 isHandle   True if the element passed in triggers<br />
17736            dragging itself, else false
17737 </pre>
17738      */
17739         register : function(el, data){
17740             data = data || {};
17741             if(typeof el == "string"){
17742                 el = document.getElementById(el);
17743             }
17744             data.ddel = el;
17745             elements[getId(el)] = data;
17746             if(data.isHandle !== false){
17747                 handles[data.ddel.id] = data;
17748             }
17749             if(data.handles){
17750                 var hs = data.handles;
17751                 for(var i = 0, len = hs.length; i < len; i++){
17752                         handles[getId(hs[i])] = data;
17753                 }
17754             }
17755         },
17756
17757     /**
17758      * Unregister a drag drop element
17759      * @param {String|HTMLElement}  element The id or DOM node to unregister
17760      */
17761         unregister : function(el){
17762             var id = getId(el, false);
17763             var data = elements[id];
17764             if(data){
17765                 delete elements[id];
17766                 if(data.handles){
17767                     var hs = data.handles;
17768                     for(var i = 0, len = hs.length; i < len; i++){
17769                         delete handles[getId(hs[i], false)];
17770                     }
17771                 }
17772             }
17773         },
17774
17775     /**
17776      * Returns the handle registered for a DOM Node by id
17777      * @param {String|HTMLElement} id The DOM node or id to look up
17778      * @return {Object} handle The custom handle data
17779      */
17780         getHandle : function(id){
17781             if(typeof id != "string"){ // must be element?
17782                 id = id.id;
17783             }
17784             return handles[id];
17785         },
17786
17787     /**
17788      * Returns the handle that is registered for the DOM node that is the target of the event
17789      * @param {Event} e The event
17790      * @return {Object} handle The custom handle data
17791      */
17792         getHandleFromEvent : function(e){
17793             var t = Roo.lib.Event.getTarget(e);
17794             return t ? handles[t.id] : null;
17795         },
17796
17797     /**
17798      * Returns a custom data object that is registered for a DOM node by id
17799      * @param {String|HTMLElement} id The DOM node or id to look up
17800      * @return {Object} data The custom data
17801      */
17802         getTarget : function(id){
17803             if(typeof id != "string"){ // must be element?
17804                 id = id.id;
17805             }
17806             return elements[id];
17807         },
17808
17809     /**
17810      * Returns a custom data object that is registered for the DOM node that is the target of the event
17811      * @param {Event} e The event
17812      * @return {Object} data The custom data
17813      */
17814         getTargetFromEvent : function(e){
17815             var t = Roo.lib.Event.getTarget(e);
17816             return t ? elements[t.id] || handles[t.id] : null;
17817         }
17818     };
17819 }();/*
17820  * Based on:
17821  * Ext JS Library 1.1.1
17822  * Copyright(c) 2006-2007, Ext JS, LLC.
17823  *
17824  * Originally Released Under LGPL - original licence link has changed is not relivant.
17825  *
17826  * Fork - LGPL
17827  * <script type="text/javascript">
17828  */
17829  
17830
17831 /**
17832  * @class Roo.dd.StatusProxy
17833  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17834  * default drag proxy used by all Roo.dd components.
17835  * @constructor
17836  * @param {Object} config
17837  */
17838 Roo.dd.StatusProxy = function(config){
17839     Roo.apply(this, config);
17840     this.id = this.id || Roo.id();
17841     this.el = new Roo.Layer({
17842         dh: {
17843             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17844                 {tag: "div", cls: "x-dd-drop-icon"},
17845                 {tag: "div", cls: "x-dd-drag-ghost"}
17846             ]
17847         }, 
17848         shadow: !config || config.shadow !== false
17849     });
17850     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17851     this.dropStatus = this.dropNotAllowed;
17852 };
17853
17854 Roo.dd.StatusProxy.prototype = {
17855     /**
17856      * @cfg {String} dropAllowed
17857      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17858      */
17859     dropAllowed : "x-dd-drop-ok",
17860     /**
17861      * @cfg {String} dropNotAllowed
17862      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17863      */
17864     dropNotAllowed : "x-dd-drop-nodrop",
17865
17866     /**
17867      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17868      * over the current target element.
17869      * @param {String} cssClass The css class for the new drop status indicator image
17870      */
17871     setStatus : function(cssClass){
17872         cssClass = cssClass || this.dropNotAllowed;
17873         if(this.dropStatus != cssClass){
17874             this.el.replaceClass(this.dropStatus, cssClass);
17875             this.dropStatus = cssClass;
17876         }
17877     },
17878
17879     /**
17880      * Resets the status indicator to the default dropNotAllowed value
17881      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17882      */
17883     reset : function(clearGhost){
17884         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17885         this.dropStatus = this.dropNotAllowed;
17886         if(clearGhost){
17887             this.ghost.update("");
17888         }
17889     },
17890
17891     /**
17892      * Updates the contents of the ghost element
17893      * @param {String} html The html that will replace the current innerHTML of the ghost element
17894      */
17895     update : function(html){
17896         if(typeof html == "string"){
17897             this.ghost.update(html);
17898         }else{
17899             this.ghost.update("");
17900             html.style.margin = "0";
17901             this.ghost.dom.appendChild(html);
17902         }
17903         // ensure float = none set?? cant remember why though.
17904         var el = this.ghost.dom.firstChild;
17905                 if(el){
17906                         Roo.fly(el).setStyle('float', 'none');
17907                 }
17908     },
17909     
17910     /**
17911      * Returns the underlying proxy {@link Roo.Layer}
17912      * @return {Roo.Layer} el
17913     */
17914     getEl : function(){
17915         return this.el;
17916     },
17917
17918     /**
17919      * Returns the ghost element
17920      * @return {Roo.Element} el
17921      */
17922     getGhost : function(){
17923         return this.ghost;
17924     },
17925
17926     /**
17927      * Hides the proxy
17928      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17929      */
17930     hide : function(clear){
17931         this.el.hide();
17932         if(clear){
17933             this.reset(true);
17934         }
17935     },
17936
17937     /**
17938      * Stops the repair animation if it's currently running
17939      */
17940     stop : function(){
17941         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17942             this.anim.stop();
17943         }
17944     },
17945
17946     /**
17947      * Displays this proxy
17948      */
17949     show : function(){
17950         this.el.show();
17951     },
17952
17953     /**
17954      * Force the Layer to sync its shadow and shim positions to the element
17955      */
17956     sync : function(){
17957         this.el.sync();
17958     },
17959
17960     /**
17961      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17962      * invalid drop operation by the item being dragged.
17963      * @param {Array} xy The XY position of the element ([x, y])
17964      * @param {Function} callback The function to call after the repair is complete
17965      * @param {Object} scope The scope in which to execute the callback
17966      */
17967     repair : function(xy, callback, scope){
17968         this.callback = callback;
17969         this.scope = scope;
17970         if(xy && this.animRepair !== false){
17971             this.el.addClass("x-dd-drag-repair");
17972             this.el.hideUnders(true);
17973             this.anim = this.el.shift({
17974                 duration: this.repairDuration || .5,
17975                 easing: 'easeOut',
17976                 xy: xy,
17977                 stopFx: true,
17978                 callback: this.afterRepair,
17979                 scope: this
17980             });
17981         }else{
17982             this.afterRepair();
17983         }
17984     },
17985
17986     // private
17987     afterRepair : function(){
17988         this.hide(true);
17989         if(typeof this.callback == "function"){
17990             this.callback.call(this.scope || this);
17991         }
17992         this.callback = null;
17993         this.scope = null;
17994     }
17995 };/*
17996  * Based on:
17997  * Ext JS Library 1.1.1
17998  * Copyright(c) 2006-2007, Ext JS, LLC.
17999  *
18000  * Originally Released Under LGPL - original licence link has changed is not relivant.
18001  *
18002  * Fork - LGPL
18003  * <script type="text/javascript">
18004  */
18005
18006 /**
18007  * @class Roo.dd.DragSource
18008  * @extends Roo.dd.DDProxy
18009  * A simple class that provides the basic implementation needed to make any element draggable.
18010  * @constructor
18011  * @param {String/HTMLElement/Element} el The container element
18012  * @param {Object} config
18013  */
18014 Roo.dd.DragSource = function(el, config){
18015     this.el = Roo.get(el);
18016     this.dragData = {};
18017     
18018     Roo.apply(this, config);
18019     
18020     if(!this.proxy){
18021         this.proxy = new Roo.dd.StatusProxy();
18022     }
18023
18024     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18025           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18026     
18027     this.dragging = false;
18028 };
18029
18030 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18031     /**
18032      * @cfg {String} dropAllowed
18033      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18034      */
18035     dropAllowed : "x-dd-drop-ok",
18036     /**
18037      * @cfg {String} dropNotAllowed
18038      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18039      */
18040     dropNotAllowed : "x-dd-drop-nodrop",
18041
18042     /**
18043      * Returns the data object associated with this drag source
18044      * @return {Object} data An object containing arbitrary data
18045      */
18046     getDragData : function(e){
18047         return this.dragData;
18048     },
18049
18050     // private
18051     onDragEnter : function(e, id){
18052         var target = Roo.dd.DragDropMgr.getDDById(id);
18053         this.cachedTarget = target;
18054         if(this.beforeDragEnter(target, e, id) !== false){
18055             if(target.isNotifyTarget){
18056                 var status = target.notifyEnter(this, e, this.dragData);
18057                 this.proxy.setStatus(status);
18058             }else{
18059                 this.proxy.setStatus(this.dropAllowed);
18060             }
18061             
18062             if(this.afterDragEnter){
18063                 /**
18064                  * An empty function by default, but provided so that you can perform a custom action
18065                  * when the dragged item enters the drop target by providing an implementation.
18066                  * @param {Roo.dd.DragDrop} target The drop target
18067                  * @param {Event} e The event object
18068                  * @param {String} id The id of the dragged element
18069                  * @method afterDragEnter
18070                  */
18071                 this.afterDragEnter(target, e, id);
18072             }
18073         }
18074     },
18075
18076     /**
18077      * An empty function by default, but provided so that you can perform a custom action
18078      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18079      * @param {Roo.dd.DragDrop} target The drop target
18080      * @param {Event} e The event object
18081      * @param {String} id The id of the dragged element
18082      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18083      */
18084     beforeDragEnter : function(target, e, id){
18085         return true;
18086     },
18087
18088     // private
18089     alignElWithMouse: function() {
18090         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18091         this.proxy.sync();
18092     },
18093
18094     // private
18095     onDragOver : function(e, id){
18096         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18097         if(this.beforeDragOver(target, e, id) !== false){
18098             if(target.isNotifyTarget){
18099                 var status = target.notifyOver(this, e, this.dragData);
18100                 this.proxy.setStatus(status);
18101             }
18102
18103             if(this.afterDragOver){
18104                 /**
18105                  * An empty function by default, but provided so that you can perform a custom action
18106                  * while the dragged item is over the drop target by providing an implementation.
18107                  * @param {Roo.dd.DragDrop} target The drop target
18108                  * @param {Event} e The event object
18109                  * @param {String} id The id of the dragged element
18110                  * @method afterDragOver
18111                  */
18112                 this.afterDragOver(target, e, id);
18113             }
18114         }
18115     },
18116
18117     /**
18118      * An empty function by default, but provided so that you can perform a custom action
18119      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18120      * @param {Roo.dd.DragDrop} target The drop target
18121      * @param {Event} e The event object
18122      * @param {String} id The id of the dragged element
18123      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18124      */
18125     beforeDragOver : function(target, e, id){
18126         return true;
18127     },
18128
18129     // private
18130     onDragOut : function(e, id){
18131         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18132         if(this.beforeDragOut(target, e, id) !== false){
18133             if(target.isNotifyTarget){
18134                 target.notifyOut(this, e, this.dragData);
18135             }
18136             this.proxy.reset();
18137             if(this.afterDragOut){
18138                 /**
18139                  * An empty function by default, but provided so that you can perform a custom action
18140                  * after the dragged item is dragged out of the target without dropping.
18141                  * @param {Roo.dd.DragDrop} target The drop target
18142                  * @param {Event} e The event object
18143                  * @param {String} id The id of the dragged element
18144                  * @method afterDragOut
18145                  */
18146                 this.afterDragOut(target, e, id);
18147             }
18148         }
18149         this.cachedTarget = null;
18150     },
18151
18152     /**
18153      * An empty function by default, but provided so that you can perform a custom action before the dragged
18154      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18155      * @param {Roo.dd.DragDrop} target The drop target
18156      * @param {Event} e The event object
18157      * @param {String} id The id of the dragged element
18158      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18159      */
18160     beforeDragOut : function(target, e, id){
18161         return true;
18162     },
18163     
18164     // private
18165     onDragDrop : function(e, id){
18166         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18167         if(this.beforeDragDrop(target, e, id) !== false){
18168             if(target.isNotifyTarget){
18169                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18170                     this.onValidDrop(target, e, id);
18171                 }else{
18172                     this.onInvalidDrop(target, e, id);
18173                 }
18174             }else{
18175                 this.onValidDrop(target, e, id);
18176             }
18177             
18178             if(this.afterDragDrop){
18179                 /**
18180                  * An empty function by default, but provided so that you can perform a custom action
18181                  * after a valid drag drop has occurred by providing an implementation.
18182                  * @param {Roo.dd.DragDrop} target The drop target
18183                  * @param {Event} e The event object
18184                  * @param {String} id The id of the dropped element
18185                  * @method afterDragDrop
18186                  */
18187                 this.afterDragDrop(target, e, id);
18188             }
18189         }
18190         delete this.cachedTarget;
18191     },
18192
18193     /**
18194      * An empty function by default, but provided so that you can perform a custom action before the dragged
18195      * item is dropped onto the target and optionally cancel the onDragDrop.
18196      * @param {Roo.dd.DragDrop} target The drop target
18197      * @param {Event} e The event object
18198      * @param {String} id The id of the dragged element
18199      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18200      */
18201     beforeDragDrop : function(target, e, id){
18202         return true;
18203     },
18204
18205     // private
18206     onValidDrop : function(target, e, id){
18207         this.hideProxy();
18208         if(this.afterValidDrop){
18209             /**
18210              * An empty function by default, but provided so that you can perform a custom action
18211              * after a valid drop has occurred by providing an implementation.
18212              * @param {Object} target The target DD 
18213              * @param {Event} e The event object
18214              * @param {String} id The id of the dropped element
18215              * @method afterInvalidDrop
18216              */
18217             this.afterValidDrop(target, e, id);
18218         }
18219     },
18220
18221     // private
18222     getRepairXY : function(e, data){
18223         return this.el.getXY();  
18224     },
18225
18226     // private
18227     onInvalidDrop : function(target, e, id){
18228         this.beforeInvalidDrop(target, e, id);
18229         if(this.cachedTarget){
18230             if(this.cachedTarget.isNotifyTarget){
18231                 this.cachedTarget.notifyOut(this, e, this.dragData);
18232             }
18233             this.cacheTarget = null;
18234         }
18235         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18236
18237         if(this.afterInvalidDrop){
18238             /**
18239              * An empty function by default, but provided so that you can perform a custom action
18240              * after an invalid drop has occurred by providing an implementation.
18241              * @param {Event} e The event object
18242              * @param {String} id The id of the dropped element
18243              * @method afterInvalidDrop
18244              */
18245             this.afterInvalidDrop(e, id);
18246         }
18247     },
18248
18249     // private
18250     afterRepair : function(){
18251         if(Roo.enableFx){
18252             this.el.highlight(this.hlColor || "c3daf9");
18253         }
18254         this.dragging = false;
18255     },
18256
18257     /**
18258      * An empty function by default, but provided so that you can perform a custom action after an invalid
18259      * drop has occurred.
18260      * @param {Roo.dd.DragDrop} target The drop target
18261      * @param {Event} e The event object
18262      * @param {String} id The id of the dragged element
18263      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18264      */
18265     beforeInvalidDrop : function(target, e, id){
18266         return true;
18267     },
18268
18269     // private
18270     handleMouseDown : function(e){
18271         if(this.dragging) {
18272             return;
18273         }
18274         var data = this.getDragData(e);
18275         if(data && this.onBeforeDrag(data, e) !== false){
18276             this.dragData = data;
18277             this.proxy.stop();
18278             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18279         } 
18280     },
18281
18282     /**
18283      * An empty function by default, but provided so that you can perform a custom action before the initial
18284      * drag event begins and optionally cancel it.
18285      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18286      * @param {Event} e The event object
18287      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18288      */
18289     onBeforeDrag : function(data, e){
18290         return true;
18291     },
18292
18293     /**
18294      * An empty function by default, but provided so that you can perform a custom action once the initial
18295      * drag event has begun.  The drag cannot be canceled from this function.
18296      * @param {Number} x The x position of the click on the dragged object
18297      * @param {Number} y The y position of the click on the dragged object
18298      */
18299     onStartDrag : Roo.emptyFn,
18300
18301     // private - YUI override
18302     startDrag : function(x, y){
18303         this.proxy.reset();
18304         this.dragging = true;
18305         this.proxy.update("");
18306         this.onInitDrag(x, y);
18307         this.proxy.show();
18308     },
18309
18310     // private
18311     onInitDrag : function(x, y){
18312         var clone = this.el.dom.cloneNode(true);
18313         clone.id = Roo.id(); // prevent duplicate ids
18314         this.proxy.update(clone);
18315         this.onStartDrag(x, y);
18316         return true;
18317     },
18318
18319     /**
18320      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18321      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18322      */
18323     getProxy : function(){
18324         return this.proxy;  
18325     },
18326
18327     /**
18328      * Hides the drag source's {@link Roo.dd.StatusProxy}
18329      */
18330     hideProxy : function(){
18331         this.proxy.hide();  
18332         this.proxy.reset(true);
18333         this.dragging = false;
18334     },
18335
18336     // private
18337     triggerCacheRefresh : function(){
18338         Roo.dd.DDM.refreshCache(this.groups);
18339     },
18340
18341     // private - override to prevent hiding
18342     b4EndDrag: function(e) {
18343     },
18344
18345     // private - override to prevent moving
18346     endDrag : function(e){
18347         this.onEndDrag(this.dragData, e);
18348     },
18349
18350     // private
18351     onEndDrag : function(data, e){
18352     },
18353     
18354     // private - pin to cursor
18355     autoOffset : function(x, y) {
18356         this.setDelta(-12, -20);
18357     }    
18358 });/*
18359  * Based on:
18360  * Ext JS Library 1.1.1
18361  * Copyright(c) 2006-2007, Ext JS, LLC.
18362  *
18363  * Originally Released Under LGPL - original licence link has changed is not relivant.
18364  *
18365  * Fork - LGPL
18366  * <script type="text/javascript">
18367  */
18368
18369
18370 /**
18371  * @class Roo.dd.DropTarget
18372  * @extends Roo.dd.DDTarget
18373  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18374  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18375  * @constructor
18376  * @param {String/HTMLElement/Element} el The container element
18377  * @param {Object} config
18378  */
18379 Roo.dd.DropTarget = function(el, config){
18380     this.el = Roo.get(el);
18381     
18382     var listeners = false; ;
18383     if (config && config.listeners) {
18384         listeners= config.listeners;
18385         delete config.listeners;
18386     }
18387     Roo.apply(this, config);
18388     
18389     if(this.containerScroll){
18390         Roo.dd.ScrollManager.register(this.el);
18391     }
18392     this.addEvents( {
18393          /**
18394          * @scope Roo.dd.DropTarget
18395          */
18396          
18397          /**
18398          * @event enter
18399          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18400          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18401          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18402          * 
18403          * IMPORTANT : it should set this.overClass and this.dropAllowed
18404          * 
18405          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18406          * @param {Event} e The event
18407          * @param {Object} data An object containing arbitrary data supplied by the drag source
18408          */
18409         "enter" : true,
18410         
18411          /**
18412          * @event over
18413          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18414          * This method will be called on every mouse movement while the drag source is over the drop target.
18415          * This default implementation simply returns the dropAllowed config value.
18416          * 
18417          * IMPORTANT : it should set this.dropAllowed
18418          * 
18419          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18420          * @param {Event} e The event
18421          * @param {Object} data An object containing arbitrary data supplied by the drag source
18422          
18423          */
18424         "over" : true,
18425         /**
18426          * @event out
18427          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18428          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18429          * overClass (if any) from the drop element.
18430          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18431          * @param {Event} e The event
18432          * @param {Object} data An object containing arbitrary data supplied by the drag source
18433          */
18434          "out" : true,
18435          
18436         /**
18437          * @event drop
18438          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18439          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18440          * implementation that does something to process the drop event and returns true so that the drag source's
18441          * repair action does not run.
18442          * 
18443          * IMPORTANT : it should set this.success
18444          * 
18445          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18446          * @param {Event} e The event
18447          * @param {Object} data An object containing arbitrary data supplied by the drag source
18448         */
18449          "drop" : true
18450     });
18451             
18452      
18453     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18454         this.el.dom, 
18455         this.ddGroup || this.group,
18456         {
18457             isTarget: true,
18458             listeners : listeners || {} 
18459            
18460         
18461         }
18462     );
18463
18464 };
18465
18466 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18467     /**
18468      * @cfg {String} overClass
18469      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18470      */
18471      /**
18472      * @cfg {String} ddGroup
18473      * The drag drop group to handle drop events for
18474      */
18475      
18476     /**
18477      * @cfg {String} dropAllowed
18478      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18479      */
18480     dropAllowed : "x-dd-drop-ok",
18481     /**
18482      * @cfg {String} dropNotAllowed
18483      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18484      */
18485     dropNotAllowed : "x-dd-drop-nodrop",
18486     /**
18487      * @cfg {boolean} success
18488      * set this after drop listener.. 
18489      */
18490     success : false,
18491     /**
18492      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18493      * if the drop point is valid for over/enter..
18494      */
18495     valid : false,
18496     // private
18497     isTarget : true,
18498
18499     // private
18500     isNotifyTarget : true,
18501     
18502     /**
18503      * @hide
18504      */
18505     notifyEnter : function(dd, e, data)
18506     {
18507         this.valid = true;
18508         this.fireEvent('enter', dd, e, data);
18509         if(this.overClass){
18510             this.el.addClass(this.overClass);
18511         }
18512         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18513             this.valid ? this.dropAllowed : this.dropNotAllowed
18514         );
18515     },
18516
18517     /**
18518      * @hide
18519      */
18520     notifyOver : function(dd, e, data)
18521     {
18522         this.valid = true;
18523         this.fireEvent('over', dd, e, data);
18524         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18525             this.valid ? this.dropAllowed : this.dropNotAllowed
18526         );
18527     },
18528
18529     /**
18530      * @hide
18531      */
18532     notifyOut : function(dd, e, data)
18533     {
18534         this.fireEvent('out', dd, e, data);
18535         if(this.overClass){
18536             this.el.removeClass(this.overClass);
18537         }
18538     },
18539
18540     /**
18541      * @hide
18542      */
18543     notifyDrop : function(dd, e, data)
18544     {
18545         this.success = false;
18546         this.fireEvent('drop', dd, e, data);
18547         return this.success;
18548     }
18549 });/*
18550  * Based on:
18551  * Ext JS Library 1.1.1
18552  * Copyright(c) 2006-2007, Ext JS, LLC.
18553  *
18554  * Originally Released Under LGPL - original licence link has changed is not relivant.
18555  *
18556  * Fork - LGPL
18557  * <script type="text/javascript">
18558  */
18559
18560
18561 /**
18562  * @class Roo.dd.DragZone
18563  * @extends Roo.dd.DragSource
18564  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18565  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18566  * @constructor
18567  * @param {String/HTMLElement/Element} el The container element
18568  * @param {Object} config
18569  */
18570 Roo.dd.DragZone = function(el, config){
18571     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18572     if(this.containerScroll){
18573         Roo.dd.ScrollManager.register(this.el);
18574     }
18575 };
18576
18577 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18578     /**
18579      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18580      * for auto scrolling during drag operations.
18581      */
18582     /**
18583      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18584      * method after a failed drop (defaults to "c3daf9" - light blue)
18585      */
18586
18587     /**
18588      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18589      * for a valid target to drag based on the mouse down. Override this method
18590      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18591      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18592      * @param {EventObject} e The mouse down event
18593      * @return {Object} The dragData
18594      */
18595     getDragData : function(e){
18596         return Roo.dd.Registry.getHandleFromEvent(e);
18597     },
18598     
18599     /**
18600      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18601      * this.dragData.ddel
18602      * @param {Number} x The x position of the click on the dragged object
18603      * @param {Number} y The y position of the click on the dragged object
18604      * @return {Boolean} true to continue the drag, false to cancel
18605      */
18606     onInitDrag : function(x, y){
18607         this.proxy.update(this.dragData.ddel.cloneNode(true));
18608         this.onStartDrag(x, y);
18609         return true;
18610     },
18611     
18612     /**
18613      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18614      */
18615     afterRepair : function(){
18616         if(Roo.enableFx){
18617             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18618         }
18619         this.dragging = false;
18620     },
18621
18622     /**
18623      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18624      * the XY of this.dragData.ddel
18625      * @param {EventObject} e The mouse up event
18626      * @return {Array} The xy location (e.g. [100, 200])
18627      */
18628     getRepairXY : function(e){
18629         return Roo.Element.fly(this.dragData.ddel).getXY();  
18630     }
18631 });/*
18632  * Based on:
18633  * Ext JS Library 1.1.1
18634  * Copyright(c) 2006-2007, Ext JS, LLC.
18635  *
18636  * Originally Released Under LGPL - original licence link has changed is not relivant.
18637  *
18638  * Fork - LGPL
18639  * <script type="text/javascript">
18640  */
18641 /**
18642  * @class Roo.dd.DropZone
18643  * @extends Roo.dd.DropTarget
18644  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18645  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18646  * @constructor
18647  * @param {String/HTMLElement/Element} el The container element
18648  * @param {Object} config
18649  */
18650 Roo.dd.DropZone = function(el, config){
18651     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18652 };
18653
18654 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18655     /**
18656      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18657      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18658      * provide your own custom lookup.
18659      * @param {Event} e The event
18660      * @return {Object} data The custom data
18661      */
18662     getTargetFromEvent : function(e){
18663         return Roo.dd.Registry.getTargetFromEvent(e);
18664     },
18665
18666     /**
18667      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18668      * that it has registered.  This method has no default implementation and should be overridden to provide
18669      * node-specific processing if necessary.
18670      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18671      * {@link #getTargetFromEvent} for this node)
18672      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18673      * @param {Event} e The event
18674      * @param {Object} data An object containing arbitrary data supplied by the drag source
18675      */
18676     onNodeEnter : function(n, dd, e, data){
18677         
18678     },
18679
18680     /**
18681      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18682      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18683      * overridden to provide the proper feedback.
18684      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18685      * {@link #getTargetFromEvent} for this node)
18686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18687      * @param {Event} e The event
18688      * @param {Object} data An object containing arbitrary data supplied by the drag source
18689      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18690      * underlying {@link Roo.dd.StatusProxy} can be updated
18691      */
18692     onNodeOver : function(n, dd, e, data){
18693         return this.dropAllowed;
18694     },
18695
18696     /**
18697      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18698      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18699      * node-specific processing if necessary.
18700      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18701      * {@link #getTargetFromEvent} for this node)
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      */
18706     onNodeOut : function(n, dd, e, data){
18707         
18708     },
18709
18710     /**
18711      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18712      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18713      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18714      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18715      * {@link #getTargetFromEvent} for this node)
18716      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18717      * @param {Event} e The event
18718      * @param {Object} data An object containing arbitrary data supplied by the drag source
18719      * @return {Boolean} True if the drop was valid, else false
18720      */
18721     onNodeDrop : function(n, dd, e, data){
18722         return false;
18723     },
18724
18725     /**
18726      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18727      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18728      * it should be overridden to provide the proper feedback if necessary.
18729      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18730      * @param {Event} e The event
18731      * @param {Object} data An object containing arbitrary data supplied by the drag source
18732      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18733      * underlying {@link Roo.dd.StatusProxy} can be updated
18734      */
18735     onContainerOver : function(dd, e, data){
18736         return this.dropNotAllowed;
18737     },
18738
18739     /**
18740      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18741      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18742      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18743      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18744      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18745      * @param {Event} e The event
18746      * @param {Object} data An object containing arbitrary data supplied by the drag source
18747      * @return {Boolean} True if the drop was valid, else false
18748      */
18749     onContainerDrop : function(dd, e, data){
18750         return false;
18751     },
18752
18753     /**
18754      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18755      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18756      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18757      * you should override this method and provide a custom implementation.
18758      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18759      * @param {Event} e The event
18760      * @param {Object} data An object containing arbitrary data supplied by the drag source
18761      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18762      * underlying {@link Roo.dd.StatusProxy} can be updated
18763      */
18764     notifyEnter : function(dd, e, data){
18765         return this.dropNotAllowed;
18766     },
18767
18768     /**
18769      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18770      * This method will be called on every mouse movement while the drag source is over the drop zone.
18771      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18772      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18773      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18774      * registered node, it will call {@link #onContainerOver}.
18775      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18776      * @param {Event} e The event
18777      * @param {Object} data An object containing arbitrary data supplied by the drag source
18778      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18779      * underlying {@link Roo.dd.StatusProxy} can be updated
18780      */
18781     notifyOver : function(dd, e, data){
18782         var n = this.getTargetFromEvent(e);
18783         if(!n){ // not over valid drop target
18784             if(this.lastOverNode){
18785                 this.onNodeOut(this.lastOverNode, dd, e, data);
18786                 this.lastOverNode = null;
18787             }
18788             return this.onContainerOver(dd, e, data);
18789         }
18790         if(this.lastOverNode != n){
18791             if(this.lastOverNode){
18792                 this.onNodeOut(this.lastOverNode, dd, e, data);
18793             }
18794             this.onNodeEnter(n, dd, e, data);
18795             this.lastOverNode = n;
18796         }
18797         return this.onNodeOver(n, dd, e, data);
18798     },
18799
18800     /**
18801      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18802      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18803      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18804      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18805      * @param {Event} e The event
18806      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18807      */
18808     notifyOut : function(dd, e, data){
18809         if(this.lastOverNode){
18810             this.onNodeOut(this.lastOverNode, dd, e, data);
18811             this.lastOverNode = null;
18812         }
18813     },
18814
18815     /**
18816      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18817      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18818      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18819      * otherwise it will call {@link #onContainerDrop}.
18820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18821      * @param {Event} e The event
18822      * @param {Object} data An object containing arbitrary data supplied by the drag source
18823      * @return {Boolean} True if the drop was valid, else false
18824      */
18825     notifyDrop : function(dd, e, data){
18826         if(this.lastOverNode){
18827             this.onNodeOut(this.lastOverNode, dd, e, data);
18828             this.lastOverNode = null;
18829         }
18830         var n = this.getTargetFromEvent(e);
18831         return n ?
18832             this.onNodeDrop(n, dd, e, data) :
18833             this.onContainerDrop(dd, e, data);
18834     },
18835
18836     // private
18837     triggerCacheRefresh : function(){
18838         Roo.dd.DDM.refreshCache(this.groups);
18839     }  
18840 });/*
18841  * Based on:
18842  * Ext JS Library 1.1.1
18843  * Copyright(c) 2006-2007, Ext JS, LLC.
18844  *
18845  * Originally Released Under LGPL - original licence link has changed is not relivant.
18846  *
18847  * Fork - LGPL
18848  * <script type="text/javascript">
18849  */
18850
18851
18852 /**
18853  * @class Roo.data.SortTypes
18854  * @singleton
18855  * Defines the default sorting (casting?) comparison functions used when sorting data.
18856  */
18857 Roo.data.SortTypes = {
18858     /**
18859      * Default sort that does nothing
18860      * @param {Mixed} s The value being converted
18861      * @return {Mixed} The comparison value
18862      */
18863     none : function(s){
18864         return s;
18865     },
18866     
18867     /**
18868      * The regular expression used to strip tags
18869      * @type {RegExp}
18870      * @property
18871      */
18872     stripTagsRE : /<\/?[^>]+>/gi,
18873     
18874     /**
18875      * Strips all HTML tags to sort on text only
18876      * @param {Mixed} s The value being converted
18877      * @return {String} The comparison value
18878      */
18879     asText : function(s){
18880         return String(s).replace(this.stripTagsRE, "");
18881     },
18882     
18883     /**
18884      * Strips all HTML tags to sort on text only - Case insensitive
18885      * @param {Mixed} s The value being converted
18886      * @return {String} The comparison value
18887      */
18888     asUCText : function(s){
18889         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18890     },
18891     
18892     /**
18893      * Case insensitive string
18894      * @param {Mixed} s The value being converted
18895      * @return {String} The comparison value
18896      */
18897     asUCString : function(s) {
18898         return String(s).toUpperCase();
18899     },
18900     
18901     /**
18902      * Date sorting
18903      * @param {Mixed} s The value being converted
18904      * @return {Number} The comparison value
18905      */
18906     asDate : function(s) {
18907         if(!s){
18908             return 0;
18909         }
18910         if(s instanceof Date){
18911             return s.getTime();
18912         }
18913         return Date.parse(String(s));
18914     },
18915     
18916     /**
18917      * Float sorting
18918      * @param {Mixed} s The value being converted
18919      * @return {Float} The comparison value
18920      */
18921     asFloat : function(s) {
18922         var val = parseFloat(String(s).replace(/,/g, ""));
18923         if(isNaN(val)) val = 0;
18924         return val;
18925     },
18926     
18927     /**
18928      * Integer sorting
18929      * @param {Mixed} s The value being converted
18930      * @return {Number} The comparison value
18931      */
18932     asInt : function(s) {
18933         var val = parseInt(String(s).replace(/,/g, ""));
18934         if(isNaN(val)) val = 0;
18935         return val;
18936     }
18937 };/*
18938  * Based on:
18939  * Ext JS Library 1.1.1
18940  * Copyright(c) 2006-2007, Ext JS, LLC.
18941  *
18942  * Originally Released Under LGPL - original licence link has changed is not relivant.
18943  *
18944  * Fork - LGPL
18945  * <script type="text/javascript">
18946  */
18947
18948 /**
18949 * @class Roo.data.Record
18950  * Instances of this class encapsulate both record <em>definition</em> information, and record
18951  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18952  * to access Records cached in an {@link Roo.data.Store} object.<br>
18953  * <p>
18954  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18955  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18956  * objects.<br>
18957  * <p>
18958  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18959  * @constructor
18960  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18961  * {@link #create}. The parameters are the same.
18962  * @param {Array} data An associative Array of data values keyed by the field name.
18963  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18964  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18965  * not specified an integer id is generated.
18966  */
18967 Roo.data.Record = function(data, id){
18968     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18969     this.data = data;
18970 };
18971
18972 /**
18973  * Generate a constructor for a specific record layout.
18974  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18975  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18976  * Each field definition object may contain the following properties: <ul>
18977  * <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,
18978  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18979  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18980  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18981  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18982  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18983  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18984  * this may be omitted.</p></li>
18985  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18986  * <ul><li>auto (Default, implies no conversion)</li>
18987  * <li>string</li>
18988  * <li>int</li>
18989  * <li>float</li>
18990  * <li>boolean</li>
18991  * <li>date</li></ul></p></li>
18992  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18993  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18994  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18995  * by the Reader into an object that will be stored in the Record. It is passed the
18996  * following parameters:<ul>
18997  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18998  * </ul></p></li>
18999  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19000  * </ul>
19001  * <br>usage:<br><pre><code>
19002 var TopicRecord = Roo.data.Record.create(
19003     {name: 'title', mapping: 'topic_title'},
19004     {name: 'author', mapping: 'username'},
19005     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19006     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19007     {name: 'lastPoster', mapping: 'user2'},
19008     {name: 'excerpt', mapping: 'post_text'}
19009 );
19010
19011 var myNewRecord = new TopicRecord({
19012     title: 'Do my job please',
19013     author: 'noobie',
19014     totalPosts: 1,
19015     lastPost: new Date(),
19016     lastPoster: 'Animal',
19017     excerpt: 'No way dude!'
19018 });
19019 myStore.add(myNewRecord);
19020 </code></pre>
19021  * @method create
19022  * @static
19023  */
19024 Roo.data.Record.create = function(o){
19025     var f = function(){
19026         f.superclass.constructor.apply(this, arguments);
19027     };
19028     Roo.extend(f, Roo.data.Record);
19029     var p = f.prototype;
19030     p.fields = new Roo.util.MixedCollection(false, function(field){
19031         return field.name;
19032     });
19033     for(var i = 0, len = o.length; i < len; i++){
19034         p.fields.add(new Roo.data.Field(o[i]));
19035     }
19036     f.getField = function(name){
19037         return p.fields.get(name);  
19038     };
19039     return f;
19040 };
19041
19042 Roo.data.Record.AUTO_ID = 1000;
19043 Roo.data.Record.EDIT = 'edit';
19044 Roo.data.Record.REJECT = 'reject';
19045 Roo.data.Record.COMMIT = 'commit';
19046
19047 Roo.data.Record.prototype = {
19048     /**
19049      * Readonly flag - true if this record has been modified.
19050      * @type Boolean
19051      */
19052     dirty : false,
19053     editing : false,
19054     error: null,
19055     modified: null,
19056
19057     // private
19058     join : function(store){
19059         this.store = store;
19060     },
19061
19062     /**
19063      * Set the named field to the specified value.
19064      * @param {String} name The name of the field to set.
19065      * @param {Object} value The value to set the field to.
19066      */
19067     set : function(name, value){
19068         if(this.data[name] == value){
19069             return;
19070         }
19071         this.dirty = true;
19072         if(!this.modified){
19073             this.modified = {};
19074         }
19075         if(typeof this.modified[name] == 'undefined'){
19076             this.modified[name] = this.data[name];
19077         }
19078         this.data[name] = value;
19079         if(!this.editing && this.store){
19080             this.store.afterEdit(this);
19081         }       
19082     },
19083
19084     /**
19085      * Get the value of the named field.
19086      * @param {String} name The name of the field to get the value of.
19087      * @return {Object} The value of the field.
19088      */
19089     get : function(name){
19090         return this.data[name]; 
19091     },
19092
19093     // private
19094     beginEdit : function(){
19095         this.editing = true;
19096         this.modified = {}; 
19097     },
19098
19099     // private
19100     cancelEdit : function(){
19101         this.editing = false;
19102         delete this.modified;
19103     },
19104
19105     // private
19106     endEdit : function(){
19107         this.editing = false;
19108         if(this.dirty && this.store){
19109             this.store.afterEdit(this);
19110         }
19111     },
19112
19113     /**
19114      * Usually called by the {@link Roo.data.Store} which owns the Record.
19115      * Rejects all changes made to the Record since either creation, or the last commit operation.
19116      * Modified fields are reverted to their original values.
19117      * <p>
19118      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19119      * of reject operations.
19120      */
19121     reject : function(){
19122         var m = this.modified;
19123         for(var n in m){
19124             if(typeof m[n] != "function"){
19125                 this.data[n] = m[n];
19126             }
19127         }
19128         this.dirty = false;
19129         delete this.modified;
19130         this.editing = false;
19131         if(this.store){
19132             this.store.afterReject(this);
19133         }
19134     },
19135
19136     /**
19137      * Usually called by the {@link Roo.data.Store} which owns the Record.
19138      * Commits all changes made to the Record since either creation, or the last commit operation.
19139      * <p>
19140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19141      * of commit operations.
19142      */
19143     commit : function(){
19144         this.dirty = false;
19145         delete this.modified;
19146         this.editing = false;
19147         if(this.store){
19148             this.store.afterCommit(this);
19149         }
19150     },
19151
19152     // private
19153     hasError : function(){
19154         return this.error != null;
19155     },
19156
19157     // private
19158     clearError : function(){
19159         this.error = null;
19160     },
19161
19162     /**
19163      * Creates a copy of this record.
19164      * @param {String} id (optional) A new record id if you don't want to use this record's id
19165      * @return {Record}
19166      */
19167     copy : function(newId) {
19168         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19169     }
19170 };/*
19171  * Based on:
19172  * Ext JS Library 1.1.1
19173  * Copyright(c) 2006-2007, Ext JS, LLC.
19174  *
19175  * Originally Released Under LGPL - original licence link has changed is not relivant.
19176  *
19177  * Fork - LGPL
19178  * <script type="text/javascript">
19179  */
19180
19181
19182
19183 /**
19184  * @class Roo.data.Store
19185  * @extends Roo.util.Observable
19186  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19187  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19188  * <p>
19189  * 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
19190  * has no knowledge of the format of the data returned by the Proxy.<br>
19191  * <p>
19192  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19193  * instances from the data object. These records are cached and made available through accessor functions.
19194  * @constructor
19195  * Creates a new Store.
19196  * @param {Object} config A config object containing the objects needed for the Store to access data,
19197  * and read the data into Records.
19198  */
19199 Roo.data.Store = function(config){
19200     this.data = new Roo.util.MixedCollection(false);
19201     this.data.getKey = function(o){
19202         return o.id;
19203     };
19204     this.baseParams = {};
19205     // private
19206     this.paramNames = {
19207         "start" : "start",
19208         "limit" : "limit",
19209         "sort" : "sort",
19210         "dir" : "dir",
19211         "multisort" : "_multisort"
19212     };
19213
19214     if(config && config.data){
19215         this.inlineData = config.data;
19216         delete config.data;
19217     }
19218
19219     Roo.apply(this, config);
19220     
19221     if(this.reader){ // reader passed
19222         this.reader = Roo.factory(this.reader, Roo.data);
19223         this.reader.xmodule = this.xmodule || false;
19224         if(!this.recordType){
19225             this.recordType = this.reader.recordType;
19226         }
19227         if(this.reader.onMetaChange){
19228             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19229         }
19230     }
19231
19232     if(this.recordType){
19233         this.fields = this.recordType.prototype.fields;
19234     }
19235     this.modified = [];
19236
19237     this.addEvents({
19238         /**
19239          * @event datachanged
19240          * Fires when the data cache has changed, and a widget which is using this Store
19241          * as a Record cache should refresh its view.
19242          * @param {Store} this
19243          */
19244         datachanged : true,
19245         /**
19246          * @event metachange
19247          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19248          * @param {Store} this
19249          * @param {Object} meta The JSON metadata
19250          */
19251         metachange : true,
19252         /**
19253          * @event add
19254          * Fires when Records have been added to the Store
19255          * @param {Store} this
19256          * @param {Roo.data.Record[]} records The array of Records added
19257          * @param {Number} index The index at which the record(s) were added
19258          */
19259         add : true,
19260         /**
19261          * @event remove
19262          * Fires when a Record has been removed from the Store
19263          * @param {Store} this
19264          * @param {Roo.data.Record} record The Record that was removed
19265          * @param {Number} index The index at which the record was removed
19266          */
19267         remove : true,
19268         /**
19269          * @event update
19270          * Fires when a Record has been updated
19271          * @param {Store} this
19272          * @param {Roo.data.Record} record The Record that was updated
19273          * @param {String} operation The update operation being performed.  Value may be one of:
19274          * <pre><code>
19275  Roo.data.Record.EDIT
19276  Roo.data.Record.REJECT
19277  Roo.data.Record.COMMIT
19278          * </code></pre>
19279          */
19280         update : true,
19281         /**
19282          * @event clear
19283          * Fires when the data cache has been cleared.
19284          * @param {Store} this
19285          */
19286         clear : true,
19287         /**
19288          * @event beforeload
19289          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19290          * the load action will be canceled.
19291          * @param {Store} this
19292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19293          */
19294         beforeload : true,
19295         /**
19296          * @event load
19297          * Fires after a new set of Records has been loaded.
19298          * @param {Store} this
19299          * @param {Roo.data.Record[]} records The Records that were loaded
19300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19301          */
19302         load : true,
19303         /**
19304          * @event loadexception
19305          * Fires if an exception occurs in the Proxy during loading.
19306          * Called with the signature of the Proxy's "loadexception" event.
19307          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19308          * 
19309          * @param {Proxy} 
19310          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19311          * @param {Object} load options 
19312          * @param {Object} jsonData from your request (normally this contains the Exception)
19313          */
19314         loadexception : true
19315     });
19316     
19317     if(this.proxy){
19318         this.proxy = Roo.factory(this.proxy, Roo.data);
19319         this.proxy.xmodule = this.xmodule || false;
19320         this.relayEvents(this.proxy,  ["loadexception"]);
19321     }
19322     this.sortToggle = {};
19323     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19324
19325     Roo.data.Store.superclass.constructor.call(this);
19326
19327     if(this.inlineData){
19328         this.loadData(this.inlineData);
19329         delete this.inlineData;
19330     }
19331 };
19332 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19333      /**
19334     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19335     * without a remote query - used by combo/forms at present.
19336     */
19337     
19338     /**
19339     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19340     */
19341     /**
19342     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19343     */
19344     /**
19345     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19346     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19347     */
19348     /**
19349     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19350     * on any HTTP request
19351     */
19352     /**
19353     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19354     */
19355     /**
19356     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19357     */
19358     multiSort: false,
19359     /**
19360     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19361     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19362     */
19363     remoteSort : false,
19364
19365     /**
19366     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19367      * loaded or when a record is removed. (defaults to false).
19368     */
19369     pruneModifiedRecords : false,
19370
19371     // private
19372     lastOptions : null,
19373
19374     /**
19375      * Add Records to the Store and fires the add event.
19376      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19377      */
19378     add : function(records){
19379         records = [].concat(records);
19380         for(var i = 0, len = records.length; i < len; i++){
19381             records[i].join(this);
19382         }
19383         var index = this.data.length;
19384         this.data.addAll(records);
19385         this.fireEvent("add", this, records, index);
19386     },
19387
19388     /**
19389      * Remove a Record from the Store and fires the remove event.
19390      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19391      */
19392     remove : function(record){
19393         var index = this.data.indexOf(record);
19394         this.data.removeAt(index);
19395         if(this.pruneModifiedRecords){
19396             this.modified.remove(record);
19397         }
19398         this.fireEvent("remove", this, record, index);
19399     },
19400
19401     /**
19402      * Remove all Records from the Store and fires the clear event.
19403      */
19404     removeAll : function(){
19405         this.data.clear();
19406         if(this.pruneModifiedRecords){
19407             this.modified = [];
19408         }
19409         this.fireEvent("clear", this);
19410     },
19411
19412     /**
19413      * Inserts Records to the Store at the given index and fires the add event.
19414      * @param {Number} index The start index at which to insert the passed Records.
19415      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19416      */
19417     insert : function(index, records){
19418         records = [].concat(records);
19419         for(var i = 0, len = records.length; i < len; i++){
19420             this.data.insert(index, records[i]);
19421             records[i].join(this);
19422         }
19423         this.fireEvent("add", this, records, index);
19424     },
19425
19426     /**
19427      * Get the index within the cache of the passed Record.
19428      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19429      * @return {Number} The index of the passed Record. Returns -1 if not found.
19430      */
19431     indexOf : function(record){
19432         return this.data.indexOf(record);
19433     },
19434
19435     /**
19436      * Get the index within the cache of the Record with the passed id.
19437      * @param {String} id The id of the Record to find.
19438      * @return {Number} The index of the Record. Returns -1 if not found.
19439      */
19440     indexOfId : function(id){
19441         return this.data.indexOfKey(id);
19442     },
19443
19444     /**
19445      * Get the Record with the specified id.
19446      * @param {String} id The id of the Record to find.
19447      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19448      */
19449     getById : function(id){
19450         return this.data.key(id);
19451     },
19452
19453     /**
19454      * Get the Record at the specified index.
19455      * @param {Number} index The index of the Record to find.
19456      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19457      */
19458     getAt : function(index){
19459         return this.data.itemAt(index);
19460     },
19461
19462     /**
19463      * Returns a range of Records between specified indices.
19464      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19465      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19466      * @return {Roo.data.Record[]} An array of Records
19467      */
19468     getRange : function(start, end){
19469         return this.data.getRange(start, end);
19470     },
19471
19472     // private
19473     storeOptions : function(o){
19474         o = Roo.apply({}, o);
19475         delete o.callback;
19476         delete o.scope;
19477         this.lastOptions = o;
19478     },
19479
19480     /**
19481      * Loads the Record cache from the configured Proxy using the configured Reader.
19482      * <p>
19483      * If using remote paging, then the first load call must specify the <em>start</em>
19484      * and <em>limit</em> properties in the options.params property to establish the initial
19485      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19486      * <p>
19487      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19488      * and this call will return before the new data has been loaded. Perform any post-processing
19489      * in a callback function, or in a "load" event handler.</strong>
19490      * <p>
19491      * @param {Object} options An object containing properties which control loading options:<ul>
19492      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19493      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19494      * passed the following arguments:<ul>
19495      * <li>r : Roo.data.Record[]</li>
19496      * <li>options: Options object from the load call</li>
19497      * <li>success: Boolean success indicator</li></ul></li>
19498      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19499      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19500      * </ul>
19501      */
19502     load : function(options){
19503         options = options || {};
19504         if(this.fireEvent("beforeload", this, options) !== false){
19505             this.storeOptions(options);
19506             var p = Roo.apply(options.params || {}, this.baseParams);
19507             // if meta was not loaded from remote source.. try requesting it.
19508             if (!this.reader.metaFromRemote) {
19509                 p._requestMeta = 1;
19510             }
19511             if(this.sortInfo && this.remoteSort){
19512                 var pn = this.paramNames;
19513                 p[pn["sort"]] = this.sortInfo.field;
19514                 p[pn["dir"]] = this.sortInfo.direction;
19515             }
19516             if (this.multiSort) {
19517                 var pn = this.paramNames;
19518                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19519             }
19520             
19521             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19522         }
19523     },
19524
19525     /**
19526      * Reloads the Record cache from the configured Proxy using the configured Reader and
19527      * the options from the last load operation performed.
19528      * @param {Object} options (optional) An object containing properties which may override the options
19529      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19530      * the most recently used options are reused).
19531      */
19532     reload : function(options){
19533         this.load(Roo.applyIf(options||{}, this.lastOptions));
19534     },
19535
19536     // private
19537     // Called as a callback by the Reader during a load operation.
19538     loadRecords : function(o, options, success){
19539         if(!o || success === false){
19540             if(success !== false){
19541                 this.fireEvent("load", this, [], options);
19542             }
19543             if(options.callback){
19544                 options.callback.call(options.scope || this, [], options, false);
19545             }
19546             return;
19547         }
19548         // if data returned failure - throw an exception.
19549         if (o.success === false) {
19550             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19551             return;
19552         }
19553         var r = o.records, t = o.totalRecords || r.length;
19554         if(!options || options.add !== true){
19555             if(this.pruneModifiedRecords){
19556                 this.modified = [];
19557             }
19558             for(var i = 0, len = r.length; i < len; i++){
19559                 r[i].join(this);
19560             }
19561             if(this.snapshot){
19562                 this.data = this.snapshot;
19563                 delete this.snapshot;
19564             }
19565             this.data.clear();
19566             this.data.addAll(r);
19567             this.totalLength = t;
19568             this.applySort();
19569             this.fireEvent("datachanged", this);
19570         }else{
19571             this.totalLength = Math.max(t, this.data.length+r.length);
19572             this.add(r);
19573         }
19574         this.fireEvent("load", this, r, options);
19575         if(options.callback){
19576             options.callback.call(options.scope || this, r, options, true);
19577         }
19578     },
19579
19580     /**
19581      * Loads data from a passed data block. A Reader which understands the format of the data
19582      * must have been configured in the constructor.
19583      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19584      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19585      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19586      */
19587     loadData : function(o, append){
19588         var r = this.reader.readRecords(o);
19589         this.loadRecords(r, {add: append}, true);
19590     },
19591
19592     /**
19593      * Gets the number of cached records.
19594      * <p>
19595      * <em>If using paging, this may not be the total size of the dataset. If the data object
19596      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19597      * the data set size</em>
19598      */
19599     getCount : function(){
19600         return this.data.length || 0;
19601     },
19602
19603     /**
19604      * Gets the total number of records in the dataset as returned by the server.
19605      * <p>
19606      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19607      * the dataset size</em>
19608      */
19609     getTotalCount : function(){
19610         return this.totalLength || 0;
19611     },
19612
19613     /**
19614      * Returns the sort state of the Store as an object with two properties:
19615      * <pre><code>
19616  field {String} The name of the field by which the Records are sorted
19617  direction {String} The sort order, "ASC" or "DESC"
19618      * </code></pre>
19619      */
19620     getSortState : function(){
19621         return this.sortInfo;
19622     },
19623
19624     // private
19625     applySort : function(){
19626         if(this.sortInfo && !this.remoteSort){
19627             var s = this.sortInfo, f = s.field;
19628             var st = this.fields.get(f).sortType;
19629             var fn = function(r1, r2){
19630                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19631                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19632             };
19633             this.data.sort(s.direction, fn);
19634             if(this.snapshot && this.snapshot != this.data){
19635                 this.snapshot.sort(s.direction, fn);
19636             }
19637         }
19638     },
19639
19640     /**
19641      * Sets the default sort column and order to be used by the next load operation.
19642      * @param {String} fieldName The name of the field to sort by.
19643      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19644      */
19645     setDefaultSort : function(field, dir){
19646         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19647     },
19648
19649     /**
19650      * Sort the Records.
19651      * If remote sorting is used, the sort is performed on the server, and the cache is
19652      * reloaded. If local sorting is used, the cache is sorted internally.
19653      * @param {String} fieldName The name of the field to sort by.
19654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19655      */
19656     sort : function(fieldName, dir){
19657         var f = this.fields.get(fieldName);
19658         if(!dir){
19659             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19660             
19661             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19662                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19663             }else{
19664                 dir = f.sortDir;
19665             }
19666         }
19667         this.sortToggle[f.name] = dir;
19668         this.sortInfo = {field: f.name, direction: dir};
19669         if(!this.remoteSort){
19670             this.applySort();
19671             this.fireEvent("datachanged", this);
19672         }else{
19673             this.load(this.lastOptions);
19674         }
19675     },
19676
19677     /**
19678      * Calls the specified function for each of the Records in the cache.
19679      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19680      * Returning <em>false</em> aborts and exits the iteration.
19681      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19682      */
19683     each : function(fn, scope){
19684         this.data.each(fn, scope);
19685     },
19686
19687     /**
19688      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19689      * (e.g., during paging).
19690      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19691      */
19692     getModifiedRecords : function(){
19693         return this.modified;
19694     },
19695
19696     // private
19697     createFilterFn : function(property, value, anyMatch){
19698         if(!value.exec){ // not a regex
19699             value = String(value);
19700             if(value.length == 0){
19701                 return false;
19702             }
19703             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19704         }
19705         return function(r){
19706             return value.test(r.data[property]);
19707         };
19708     },
19709
19710     /**
19711      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19712      * @param {String} property A field on your records
19713      * @param {Number} start The record index to start at (defaults to 0)
19714      * @param {Number} end The last record index to include (defaults to length - 1)
19715      * @return {Number} The sum
19716      */
19717     sum : function(property, start, end){
19718         var rs = this.data.items, v = 0;
19719         start = start || 0;
19720         end = (end || end === 0) ? end : rs.length-1;
19721
19722         for(var i = start; i <= end; i++){
19723             v += (rs[i].data[property] || 0);
19724         }
19725         return v;
19726     },
19727
19728     /**
19729      * Filter the records by a specified property.
19730      * @param {String} field A field on your records
19731      * @param {String/RegExp} value Either a string that the field
19732      * should start with or a RegExp to test against the field
19733      * @param {Boolean} anyMatch True to match any part not just the beginning
19734      */
19735     filter : function(property, value, anyMatch){
19736         var fn = this.createFilterFn(property, value, anyMatch);
19737         return fn ? this.filterBy(fn) : this.clearFilter();
19738     },
19739
19740     /**
19741      * Filter by a function. The specified function will be called with each
19742      * record in this data source. If the function returns true the record is included,
19743      * otherwise it is filtered.
19744      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19745      * @param {Object} scope (optional) The scope of the function (defaults to this)
19746      */
19747     filterBy : function(fn, scope){
19748         this.snapshot = this.snapshot || this.data;
19749         this.data = this.queryBy(fn, scope||this);
19750         this.fireEvent("datachanged", this);
19751     },
19752
19753     /**
19754      * Query the records by a specified property.
19755      * @param {String} field A field on your records
19756      * @param {String/RegExp} value Either a string that the field
19757      * should start with or a RegExp to test against the field
19758      * @param {Boolean} anyMatch True to match any part not just the beginning
19759      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19760      */
19761     query : function(property, value, anyMatch){
19762         var fn = this.createFilterFn(property, value, anyMatch);
19763         return fn ? this.queryBy(fn) : this.data.clone();
19764     },
19765
19766     /**
19767      * Query by a function. The specified function will be called with each
19768      * record in this data source. If the function returns true the record is included
19769      * in the results.
19770      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19771      * @param {Object} scope (optional) The scope of the function (defaults to this)
19772       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19773      **/
19774     queryBy : function(fn, scope){
19775         var data = this.snapshot || this.data;
19776         return data.filterBy(fn, scope||this);
19777     },
19778
19779     /**
19780      * Collects unique values for a particular dataIndex from this store.
19781      * @param {String} dataIndex The property to collect
19782      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19783      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19784      * @return {Array} An array of the unique values
19785      **/
19786     collect : function(dataIndex, allowNull, bypassFilter){
19787         var d = (bypassFilter === true && this.snapshot) ?
19788                 this.snapshot.items : this.data.items;
19789         var v, sv, r = [], l = {};
19790         for(var i = 0, len = d.length; i < len; i++){
19791             v = d[i].data[dataIndex];
19792             sv = String(v);
19793             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19794                 l[sv] = true;
19795                 r[r.length] = v;
19796             }
19797         }
19798         return r;
19799     },
19800
19801     /**
19802      * Revert to a view of the Record cache with no filtering applied.
19803      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19804      */
19805     clearFilter : function(suppressEvent){
19806         if(this.snapshot && this.snapshot != this.data){
19807             this.data = this.snapshot;
19808             delete this.snapshot;
19809             if(suppressEvent !== true){
19810                 this.fireEvent("datachanged", this);
19811             }
19812         }
19813     },
19814
19815     // private
19816     afterEdit : function(record){
19817         if(this.modified.indexOf(record) == -1){
19818             this.modified.push(record);
19819         }
19820         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19821     },
19822
19823     // private
19824     afterReject : function(record){
19825         this.modified.remove(record);
19826         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19827     },
19828
19829     // private
19830     afterCommit : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19833     },
19834
19835     /**
19836      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19837      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19838      */
19839     commitChanges : function(){
19840         var m = this.modified.slice(0);
19841         this.modified = [];
19842         for(var i = 0, len = m.length; i < len; i++){
19843             m[i].commit();
19844         }
19845     },
19846
19847     /**
19848      * Cancel outstanding changes on all changed records.
19849      */
19850     rejectChanges : function(){
19851         var m = this.modified.slice(0);
19852         this.modified = [];
19853         for(var i = 0, len = m.length; i < len; i++){
19854             m[i].reject();
19855         }
19856     },
19857
19858     onMetaChange : function(meta, rtype, o){
19859         this.recordType = rtype;
19860         this.fields = rtype.prototype.fields;
19861         delete this.snapshot;
19862         this.sortInfo = meta.sortInfo || this.sortInfo;
19863         this.modified = [];
19864         this.fireEvent('metachange', this, this.reader.meta);
19865     }
19866 });/*
19867  * Based on:
19868  * Ext JS Library 1.1.1
19869  * Copyright(c) 2006-2007, Ext JS, LLC.
19870  *
19871  * Originally Released Under LGPL - original licence link has changed is not relivant.
19872  *
19873  * Fork - LGPL
19874  * <script type="text/javascript">
19875  */
19876
19877 /**
19878  * @class Roo.data.SimpleStore
19879  * @extends Roo.data.Store
19880  * Small helper class to make creating Stores from Array data easier.
19881  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19882  * @cfg {Array} fields An array of field definition objects, or field name strings.
19883  * @cfg {Array} data The multi-dimensional array of data
19884  * @constructor
19885  * @param {Object} config
19886  */
19887 Roo.data.SimpleStore = function(config){
19888     Roo.data.SimpleStore.superclass.constructor.call(this, {
19889         isLocal : true,
19890         reader: new Roo.data.ArrayReader({
19891                 id: config.id
19892             },
19893             Roo.data.Record.create(config.fields)
19894         ),
19895         proxy : new Roo.data.MemoryProxy(config.data)
19896     });
19897     this.load();
19898 };
19899 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19900  * Based on:
19901  * Ext JS Library 1.1.1
19902  * Copyright(c) 2006-2007, Ext JS, LLC.
19903  *
19904  * Originally Released Under LGPL - original licence link has changed is not relivant.
19905  *
19906  * Fork - LGPL
19907  * <script type="text/javascript">
19908  */
19909
19910 /**
19911 /**
19912  * @extends Roo.data.Store
19913  * @class Roo.data.JsonStore
19914  * Small helper class to make creating Stores for JSON data easier. <br/>
19915 <pre><code>
19916 var store = new Roo.data.JsonStore({
19917     url: 'get-images.php',
19918     root: 'images',
19919     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19920 });
19921 </code></pre>
19922  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19923  * JsonReader and HttpProxy (unless inline data is provided).</b>
19924  * @cfg {Array} fields An array of field definition objects, or field name strings.
19925  * @constructor
19926  * @param {Object} config
19927  */
19928 Roo.data.JsonStore = function(c){
19929     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19930         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19931         reader: new Roo.data.JsonReader(c, c.fields)
19932     }));
19933 };
19934 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19935  * Based on:
19936  * Ext JS Library 1.1.1
19937  * Copyright(c) 2006-2007, Ext JS, LLC.
19938  *
19939  * Originally Released Under LGPL - original licence link has changed is not relivant.
19940  *
19941  * Fork - LGPL
19942  * <script type="text/javascript">
19943  */
19944
19945  
19946 Roo.data.Field = function(config){
19947     if(typeof config == "string"){
19948         config = {name: config};
19949     }
19950     Roo.apply(this, config);
19951     
19952     if(!this.type){
19953         this.type = "auto";
19954     }
19955     
19956     var st = Roo.data.SortTypes;
19957     // named sortTypes are supported, here we look them up
19958     if(typeof this.sortType == "string"){
19959         this.sortType = st[this.sortType];
19960     }
19961     
19962     // set default sortType for strings and dates
19963     if(!this.sortType){
19964         switch(this.type){
19965             case "string":
19966                 this.sortType = st.asUCString;
19967                 break;
19968             case "date":
19969                 this.sortType = st.asDate;
19970                 break;
19971             default:
19972                 this.sortType = st.none;
19973         }
19974     }
19975
19976     // define once
19977     var stripRe = /[\$,%]/g;
19978
19979     // prebuilt conversion function for this field, instead of
19980     // switching every time we're reading a value
19981     if(!this.convert){
19982         var cv, dateFormat = this.dateFormat;
19983         switch(this.type){
19984             case "":
19985             case "auto":
19986             case undefined:
19987                 cv = function(v){ return v; };
19988                 break;
19989             case "string":
19990                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19991                 break;
19992             case "int":
19993                 cv = function(v){
19994                     return v !== undefined && v !== null && v !== '' ?
19995                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19996                     };
19997                 break;
19998             case "float":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20002                     };
20003                 break;
20004             case "bool":
20005             case "boolean":
20006                 cv = function(v){ return v === true || v === "true" || v == 1; };
20007                 break;
20008             case "date":
20009                 cv = function(v){
20010                     if(!v){
20011                         return '';
20012                     }
20013                     if(v instanceof Date){
20014                         return v;
20015                     }
20016                     if(dateFormat){
20017                         if(dateFormat == "timestamp"){
20018                             return new Date(v*1000);
20019                         }
20020                         return Date.parseDate(v, dateFormat);
20021                     }
20022                     var parsed = Date.parse(v);
20023                     return parsed ? new Date(parsed) : null;
20024                 };
20025              break;
20026             
20027         }
20028         this.convert = cv;
20029     }
20030 };
20031
20032 Roo.data.Field.prototype = {
20033     dateFormat: null,
20034     defaultValue: "",
20035     mapping: null,
20036     sortType : null,
20037     sortDir : "ASC"
20038 };/*
20039  * Based on:
20040  * Ext JS Library 1.1.1
20041  * Copyright(c) 2006-2007, Ext JS, LLC.
20042  *
20043  * Originally Released Under LGPL - original licence link has changed is not relivant.
20044  *
20045  * Fork - LGPL
20046  * <script type="text/javascript">
20047  */
20048  
20049 // Base class for reading structured data from a data source.  This class is intended to be
20050 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20051
20052 /**
20053  * @class Roo.data.DataReader
20054  * Base class for reading structured data from a data source.  This class is intended to be
20055  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20056  */
20057
20058 Roo.data.DataReader = function(meta, recordType){
20059     
20060     this.meta = meta;
20061     
20062     this.recordType = recordType instanceof Array ? 
20063         Roo.data.Record.create(recordType) : recordType;
20064 };
20065
20066 Roo.data.DataReader.prototype = {
20067      /**
20068      * Create an empty record
20069      * @param {Object} data (optional) - overlay some values
20070      * @return {Roo.data.Record} record created.
20071      */
20072     newRow :  function(d) {
20073         var da =  {};
20074         this.recordType.prototype.fields.each(function(c) {
20075             switch( c.type) {
20076                 case 'int' : da[c.name] = 0; break;
20077                 case 'date' : da[c.name] = new Date(); break;
20078                 case 'float' : da[c.name] = 0.0; break;
20079                 case 'boolean' : da[c.name] = false; break;
20080                 default : da[c.name] = ""; break;
20081             }
20082             
20083         });
20084         return new this.recordType(Roo.apply(da, d));
20085     }
20086     
20087 };/*
20088  * Based on:
20089  * Ext JS Library 1.1.1
20090  * Copyright(c) 2006-2007, Ext JS, LLC.
20091  *
20092  * Originally Released Under LGPL - original licence link has changed is not relivant.
20093  *
20094  * Fork - LGPL
20095  * <script type="text/javascript">
20096  */
20097
20098 /**
20099  * @class Roo.data.DataProxy
20100  * @extends Roo.data.Observable
20101  * This class is an abstract base class for implementations which provide retrieval of
20102  * unformatted data objects.<br>
20103  * <p>
20104  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20105  * (of the appropriate type which knows how to parse the data object) to provide a block of
20106  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20107  * <p>
20108  * Custom implementations must implement the load method as described in
20109  * {@link Roo.data.HttpProxy#load}.
20110  */
20111 Roo.data.DataProxy = function(){
20112     this.addEvents({
20113         /**
20114          * @event beforeload
20115          * Fires before a network request is made to retrieve a data object.
20116          * @param {Object} This DataProxy object.
20117          * @param {Object} params The params parameter to the load function.
20118          */
20119         beforeload : true,
20120         /**
20121          * @event load
20122          * Fires before the load method's callback is called.
20123          * @param {Object} This DataProxy object.
20124          * @param {Object} o The data object.
20125          * @param {Object} arg The callback argument object passed to the load function.
20126          */
20127         load : true,
20128         /**
20129          * @event loadexception
20130          * Fires if an Exception occurs during data retrieval.
20131          * @param {Object} This DataProxy object.
20132          * @param {Object} o The data object.
20133          * @param {Object} arg The callback argument object passed to the load function.
20134          * @param {Object} e The Exception.
20135          */
20136         loadexception : true
20137     });
20138     Roo.data.DataProxy.superclass.constructor.call(this);
20139 };
20140
20141 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20142
20143     /**
20144      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20145      */
20146 /*
20147  * Based on:
20148  * Ext JS Library 1.1.1
20149  * Copyright(c) 2006-2007, Ext JS, LLC.
20150  *
20151  * Originally Released Under LGPL - original licence link has changed is not relivant.
20152  *
20153  * Fork - LGPL
20154  * <script type="text/javascript">
20155  */
20156 /**
20157  * @class Roo.data.MemoryProxy
20158  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20159  * to the Reader when its load method is called.
20160  * @constructor
20161  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20162  */
20163 Roo.data.MemoryProxy = function(data){
20164     if (data.data) {
20165         data = data.data;
20166     }
20167     Roo.data.MemoryProxy.superclass.constructor.call(this);
20168     this.data = data;
20169 };
20170
20171 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20172     /**
20173      * Load data from the requested source (in this case an in-memory
20174      * data object passed to the constructor), read the data object into
20175      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20176      * process that block using the passed callback.
20177      * @param {Object} params This parameter is not used by the MemoryProxy class.
20178      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20179      * object into a block of Roo.data.Records.
20180      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20181      * The function must be passed <ul>
20182      * <li>The Record block object</li>
20183      * <li>The "arg" argument from the load function</li>
20184      * <li>A boolean success indicator</li>
20185      * </ul>
20186      * @param {Object} scope The scope in which to call the callback
20187      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20188      */
20189     load : function(params, reader, callback, scope, arg){
20190         params = params || {};
20191         var result;
20192         try {
20193             result = reader.readRecords(this.data);
20194         }catch(e){
20195             this.fireEvent("loadexception", this, arg, null, e);
20196             callback.call(scope, null, arg, false);
20197             return;
20198         }
20199         callback.call(scope, result, arg, true);
20200     },
20201     
20202     // private
20203     update : function(params, records){
20204         
20205     }
20206 });/*
20207  * Based on:
20208  * Ext JS Library 1.1.1
20209  * Copyright(c) 2006-2007, Ext JS, LLC.
20210  *
20211  * Originally Released Under LGPL - original licence link has changed is not relivant.
20212  *
20213  * Fork - LGPL
20214  * <script type="text/javascript">
20215  */
20216 /**
20217  * @class Roo.data.HttpProxy
20218  * @extends Roo.data.DataProxy
20219  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20220  * configured to reference a certain URL.<br><br>
20221  * <p>
20222  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20223  * from which the running page was served.<br><br>
20224  * <p>
20225  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20226  * <p>
20227  * Be aware that to enable the browser to parse an XML document, the server must set
20228  * the Content-Type header in the HTTP response to "text/xml".
20229  * @constructor
20230  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20231  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20232  * will be used to make the request.
20233  */
20234 Roo.data.HttpProxy = function(conn){
20235     Roo.data.HttpProxy.superclass.constructor.call(this);
20236     // is conn a conn config or a real conn?
20237     this.conn = conn;
20238     this.useAjax = !conn || !conn.events;
20239   
20240 };
20241
20242 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20243     // thse are take from connection...
20244     
20245     /**
20246      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20247      */
20248     /**
20249      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20250      * extra parameters to each request made by this object. (defaults to undefined)
20251      */
20252     /**
20253      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20254      *  to each request made by this object. (defaults to undefined)
20255      */
20256     /**
20257      * @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)
20258      */
20259     /**
20260      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20261      */
20262      /**
20263      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20264      * @type Boolean
20265      */
20266   
20267
20268     /**
20269      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20270      * @type Boolean
20271      */
20272     /**
20273      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20274      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20275      * a finer-grained basis than the DataProxy events.
20276      */
20277     getConnection : function(){
20278         return this.useAjax ? Roo.Ajax : this.conn;
20279     },
20280
20281     /**
20282      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20283      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20284      * process that block using the passed callback.
20285      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20286      * for the request to the remote server.
20287      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20288      * object into a block of Roo.data.Records.
20289      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20290      * The function must be passed <ul>
20291      * <li>The Record block object</li>
20292      * <li>The "arg" argument from the load function</li>
20293      * <li>A boolean success indicator</li>
20294      * </ul>
20295      * @param {Object} scope The scope in which to call the callback
20296      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20297      */
20298     load : function(params, reader, callback, scope, arg){
20299         if(this.fireEvent("beforeload", this, params) !== false){
20300             var  o = {
20301                 params : params || {},
20302                 request: {
20303                     callback : callback,
20304                     scope : scope,
20305                     arg : arg
20306                 },
20307                 reader: reader,
20308                 callback : this.loadResponse,
20309                 scope: this
20310             };
20311             if(this.useAjax){
20312                 Roo.applyIf(o, this.conn);
20313                 if(this.activeRequest){
20314                     Roo.Ajax.abort(this.activeRequest);
20315                 }
20316                 this.activeRequest = Roo.Ajax.request(o);
20317             }else{
20318                 this.conn.request(o);
20319             }
20320         }else{
20321             callback.call(scope||this, null, arg, false);
20322         }
20323     },
20324
20325     // private
20326     loadResponse : function(o, success, response){
20327         delete this.activeRequest;
20328         if(!success){
20329             this.fireEvent("loadexception", this, o, response);
20330             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20331             return;
20332         }
20333         var result;
20334         try {
20335             result = o.reader.read(response);
20336         }catch(e){
20337             this.fireEvent("loadexception", this, o, response, e);
20338             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20339             return;
20340         }
20341         
20342         this.fireEvent("load", this, o, o.request.arg);
20343         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20344     },
20345
20346     // private
20347     update : function(dataSet){
20348
20349     },
20350
20351     // private
20352     updateResponse : function(dataSet){
20353
20354     }
20355 });/*
20356  * Based on:
20357  * Ext JS Library 1.1.1
20358  * Copyright(c) 2006-2007, Ext JS, LLC.
20359  *
20360  * Originally Released Under LGPL - original licence link has changed is not relivant.
20361  *
20362  * Fork - LGPL
20363  * <script type="text/javascript">
20364  */
20365
20366 /**
20367  * @class Roo.data.ScriptTagProxy
20368  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20369  * other than the originating domain of the running page.<br><br>
20370  * <p>
20371  * <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
20372  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20373  * <p>
20374  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20375  * source code that is used as the source inside a &lt;script> tag.<br><br>
20376  * <p>
20377  * In order for the browser to process the returned data, the server must wrap the data object
20378  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20379  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20380  * depending on whether the callback name was passed:
20381  * <p>
20382  * <pre><code>
20383 boolean scriptTag = false;
20384 String cb = request.getParameter("callback");
20385 if (cb != null) {
20386     scriptTag = true;
20387     response.setContentType("text/javascript");
20388 } else {
20389     response.setContentType("application/x-json");
20390 }
20391 Writer out = response.getWriter();
20392 if (scriptTag) {
20393     out.write(cb + "(");
20394 }
20395 out.print(dataBlock.toJsonString());
20396 if (scriptTag) {
20397     out.write(");");
20398 }
20399 </pre></code>
20400  *
20401  * @constructor
20402  * @param {Object} config A configuration object.
20403  */
20404 Roo.data.ScriptTagProxy = function(config){
20405     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20406     Roo.apply(this, config);
20407     this.head = document.getElementsByTagName("head")[0];
20408 };
20409
20410 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20411
20412 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20413     /**
20414      * @cfg {String} url The URL from which to request the data object.
20415      */
20416     /**
20417      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20418      */
20419     timeout : 30000,
20420     /**
20421      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20422      * the server the name of the callback function set up by the load call to process the returned data object.
20423      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20424      * javascript output which calls this named function passing the data object as its only parameter.
20425      */
20426     callbackParam : "callback",
20427     /**
20428      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20429      * name to the request.
20430      */
20431     nocache : true,
20432
20433     /**
20434      * Load data from the configured URL, read the data object into
20435      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20436      * process that block using the passed callback.
20437      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20438      * for the request to the remote server.
20439      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20440      * object into a block of Roo.data.Records.
20441      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20442      * The function must be passed <ul>
20443      * <li>The Record block object</li>
20444      * <li>The "arg" argument from the load function</li>
20445      * <li>A boolean success indicator</li>
20446      * </ul>
20447      * @param {Object} scope The scope in which to call the callback
20448      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20449      */
20450     load : function(params, reader, callback, scope, arg){
20451         if(this.fireEvent("beforeload", this, params) !== false){
20452
20453             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20454
20455             var url = this.url;
20456             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20457             if(this.nocache){
20458                 url += "&_dc=" + (new Date().getTime());
20459             }
20460             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20461             var trans = {
20462                 id : transId,
20463                 cb : "stcCallback"+transId,
20464                 scriptId : "stcScript"+transId,
20465                 params : params,
20466                 arg : arg,
20467                 url : url,
20468                 callback : callback,
20469                 scope : scope,
20470                 reader : reader
20471             };
20472             var conn = this;
20473
20474             window[trans.cb] = function(o){
20475                 conn.handleResponse(o, trans);
20476             };
20477
20478             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20479
20480             if(this.autoAbort !== false){
20481                 this.abort();
20482             }
20483
20484             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20485
20486             var script = document.createElement("script");
20487             script.setAttribute("src", url);
20488             script.setAttribute("type", "text/javascript");
20489             script.setAttribute("id", trans.scriptId);
20490             this.head.appendChild(script);
20491
20492             this.trans = trans;
20493         }else{
20494             callback.call(scope||this, null, arg, false);
20495         }
20496     },
20497
20498     // private
20499     isLoading : function(){
20500         return this.trans ? true : false;
20501     },
20502
20503     /**
20504      * Abort the current server request.
20505      */
20506     abort : function(){
20507         if(this.isLoading()){
20508             this.destroyTrans(this.trans);
20509         }
20510     },
20511
20512     // private
20513     destroyTrans : function(trans, isLoaded){
20514         this.head.removeChild(document.getElementById(trans.scriptId));
20515         clearTimeout(trans.timeoutId);
20516         if(isLoaded){
20517             window[trans.cb] = undefined;
20518             try{
20519                 delete window[trans.cb];
20520             }catch(e){}
20521         }else{
20522             // if hasn't been loaded, wait for load to remove it to prevent script error
20523             window[trans.cb] = function(){
20524                 window[trans.cb] = undefined;
20525                 try{
20526                     delete window[trans.cb];
20527                 }catch(e){}
20528             };
20529         }
20530     },
20531
20532     // private
20533     handleResponse : function(o, trans){
20534         this.trans = false;
20535         this.destroyTrans(trans, true);
20536         var result;
20537         try {
20538             result = trans.reader.readRecords(o);
20539         }catch(e){
20540             this.fireEvent("loadexception", this, o, trans.arg, e);
20541             trans.callback.call(trans.scope||window, null, trans.arg, false);
20542             return;
20543         }
20544         this.fireEvent("load", this, o, trans.arg);
20545         trans.callback.call(trans.scope||window, result, trans.arg, true);
20546     },
20547
20548     // private
20549     handleFailure : function(trans){
20550         this.trans = false;
20551         this.destroyTrans(trans, false);
20552         this.fireEvent("loadexception", this, null, trans.arg);
20553         trans.callback.call(trans.scope||window, null, trans.arg, false);
20554     }
20555 });/*
20556  * Based on:
20557  * Ext JS Library 1.1.1
20558  * Copyright(c) 2006-2007, Ext JS, LLC.
20559  *
20560  * Originally Released Under LGPL - original licence link has changed is not relivant.
20561  *
20562  * Fork - LGPL
20563  * <script type="text/javascript">
20564  */
20565
20566 /**
20567  * @class Roo.data.JsonReader
20568  * @extends Roo.data.DataReader
20569  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20570  * based on mappings in a provided Roo.data.Record constructor.
20571  * 
20572  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20573  * in the reply previously. 
20574  * 
20575  * <p>
20576  * Example code:
20577  * <pre><code>
20578 var RecordDef = Roo.data.Record.create([
20579     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20580     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20581 ]);
20582 var myReader = new Roo.data.JsonReader({
20583     totalProperty: "results",    // The property which contains the total dataset size (optional)
20584     root: "rows",                // The property which contains an Array of row objects
20585     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20586 }, RecordDef);
20587 </code></pre>
20588  * <p>
20589  * This would consume a JSON file like this:
20590  * <pre><code>
20591 { 'results': 2, 'rows': [
20592     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20593     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20594 }
20595 </code></pre>
20596  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20597  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20598  * paged from the remote server.
20599  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20600  * @cfg {String} root name of the property which contains the Array of row objects.
20601  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20602  * @constructor
20603  * Create a new JsonReader
20604  * @param {Object} meta Metadata configuration options
20605  * @param {Object} recordType Either an Array of field definition objects,
20606  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20607  */
20608 Roo.data.JsonReader = function(meta, recordType){
20609     
20610     meta = meta || {};
20611     // set some defaults:
20612     Roo.applyIf(meta, {
20613         totalProperty: 'total',
20614         successProperty : 'success',
20615         root : 'data',
20616         id : 'id'
20617     });
20618     
20619     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20620 };
20621 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20622     
20623     /**
20624      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20625      * Used by Store query builder to append _requestMeta to params.
20626      * 
20627      */
20628     metaFromRemote : false,
20629     /**
20630      * This method is only used by a DataProxy which has retrieved data from a remote server.
20631      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20632      * @return {Object} data A data block which is used by an Roo.data.Store object as
20633      * a cache of Roo.data.Records.
20634      */
20635     read : function(response){
20636         var json = response.responseText;
20637        
20638         var o = /* eval:var:o */ eval("("+json+")");
20639         if(!o) {
20640             throw {message: "JsonReader.read: Json object not found"};
20641         }
20642         
20643         if(o.metaData){
20644             
20645             delete this.ef;
20646             this.metaFromRemote = true;
20647             this.meta = o.metaData;
20648             this.recordType = Roo.data.Record.create(o.metaData.fields);
20649             this.onMetaChange(this.meta, this.recordType, o);
20650         }
20651         return this.readRecords(o);
20652     },
20653
20654     // private function a store will implement
20655     onMetaChange : function(meta, recordType, o){
20656
20657     },
20658
20659     /**
20660          * @ignore
20661          */
20662     simpleAccess: function(obj, subsc) {
20663         return obj[subsc];
20664     },
20665
20666         /**
20667          * @ignore
20668          */
20669     getJsonAccessor: function(){
20670         var re = /[\[\.]/;
20671         return function(expr) {
20672             try {
20673                 return(re.test(expr))
20674                     ? new Function("obj", "return obj." + expr)
20675                     : function(obj){
20676                         return obj[expr];
20677                     };
20678             } catch(e){}
20679             return Roo.emptyFn;
20680         };
20681     }(),
20682
20683     /**
20684      * Create a data block containing Roo.data.Records from an XML document.
20685      * @param {Object} o An object which contains an Array of row objects in the property specified
20686      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20687      * which contains the total size of the dataset.
20688      * @return {Object} data A data block which is used by an Roo.data.Store object as
20689      * a cache of Roo.data.Records.
20690      */
20691     readRecords : function(o){
20692         /**
20693          * After any data loads, the raw JSON data is available for further custom processing.
20694          * @type Object
20695          */
20696         this.jsonData = o;
20697         var s = this.meta, Record = this.recordType,
20698             f = Record.prototype.fields, fi = f.items, fl = f.length;
20699
20700 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20701         if (!this.ef) {
20702             if(s.totalProperty) {
20703                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20704                 }
20705                 if(s.successProperty) {
20706                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20707                 }
20708                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20709                 if (s.id) {
20710                         var g = this.getJsonAccessor(s.id);
20711                         this.getId = function(rec) {
20712                                 var r = g(rec);
20713                                 return (r === undefined || r === "") ? null : r;
20714                         };
20715                 } else {
20716                         this.getId = function(){return null;};
20717                 }
20718             this.ef = [];
20719             for(var jj = 0; jj < fl; jj++){
20720                 f = fi[jj];
20721                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20722                 this.ef[jj] = this.getJsonAccessor(map);
20723             }
20724         }
20725
20726         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20727         if(s.totalProperty){
20728             var vt = parseInt(this.getTotal(o), 10);
20729             if(!isNaN(vt)){
20730                 totalRecords = vt;
20731             }
20732         }
20733         if(s.successProperty){
20734             var vs = this.getSuccess(o);
20735             if(vs === false || vs === 'false'){
20736                 success = false;
20737             }
20738         }
20739         var records = [];
20740             for(var i = 0; i < c; i++){
20741                     var n = root[i];
20742                 var values = {};
20743                 var id = this.getId(n);
20744                 for(var j = 0; j < fl; j++){
20745                     f = fi[j];
20746                 var v = this.ef[j](n);
20747                 if (!f.convert) {
20748                     Roo.log('missing convert for ' + f.name);
20749                     Roo.log(f);
20750                     continue;
20751                 }
20752                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20753                 }
20754                 var record = new Record(values, id);
20755                 record.json = n;
20756                 records[i] = record;
20757             }
20758             return {
20759                 success : success,
20760                 records : records,
20761                 totalRecords : totalRecords
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775 /**
20776  * @class Roo.data.XmlReader
20777  * @extends Roo.data.DataReader
20778  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20779  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20780  * <p>
20781  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20782  * header in the HTTP response must be set to "text/xml".</em>
20783  * <p>
20784  * Example code:
20785  * <pre><code>
20786 var RecordDef = Roo.data.Record.create([
20787    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20788    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20789 ]);
20790 var myReader = new Roo.data.XmlReader({
20791    totalRecords: "results", // The element which contains the total dataset size (optional)
20792    record: "row",           // The repeated element which contains row information
20793    id: "id"                 // The element within the row that provides an ID for the record (optional)
20794 }, RecordDef);
20795 </code></pre>
20796  * <p>
20797  * This would consume an XML file like this:
20798  * <pre><code>
20799 &lt;?xml?>
20800 &lt;dataset>
20801  &lt;results>2&lt;/results>
20802  &lt;row>
20803    &lt;id>1&lt;/id>
20804    &lt;name>Bill&lt;/name>
20805    &lt;occupation>Gardener&lt;/occupation>
20806  &lt;/row>
20807  &lt;row>
20808    &lt;id>2&lt;/id>
20809    &lt;name>Ben&lt;/name>
20810    &lt;occupation>Horticulturalist&lt;/occupation>
20811  &lt;/row>
20812 &lt;/dataset>
20813 </code></pre>
20814  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20815  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20816  * paged from the remote server.
20817  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20818  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20819  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20820  * a record identifier value.
20821  * @constructor
20822  * Create a new XmlReader
20823  * @param {Object} meta Metadata configuration options
20824  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20825  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20826  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20827  */
20828 Roo.data.XmlReader = function(meta, recordType){
20829     meta = meta || {};
20830     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20831 };
20832 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20833     /**
20834      * This method is only used by a DataProxy which has retrieved data from a remote server.
20835          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20836          * to contain a method called 'responseXML' that returns an XML document object.
20837      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20838      * a cache of Roo.data.Records.
20839      */
20840     read : function(response){
20841         var doc = response.responseXML;
20842         if(!doc) {
20843             throw {message: "XmlReader.read: XML Document not available"};
20844         }
20845         return this.readRecords(doc);
20846     },
20847
20848     /**
20849      * Create a data block containing Roo.data.Records from an XML document.
20850          * @param {Object} doc A parsed XML document.
20851      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20852      * a cache of Roo.data.Records.
20853      */
20854     readRecords : function(doc){
20855         /**
20856          * After any data loads/reads, the raw XML Document is available for further custom processing.
20857          * @type XMLDocument
20858          */
20859         this.xmlData = doc;
20860         var root = doc.documentElement || doc;
20861         var q = Roo.DomQuery;
20862         var recordType = this.recordType, fields = recordType.prototype.fields;
20863         var sid = this.meta.id;
20864         var totalRecords = 0, success = true;
20865         if(this.meta.totalRecords){
20866             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20867         }
20868         
20869         if(this.meta.success){
20870             var sv = q.selectValue(this.meta.success, root, true);
20871             success = sv !== false && sv !== 'false';
20872         }
20873         var records = [];
20874         var ns = q.select(this.meta.record, root);
20875         for(var i = 0, len = ns.length; i < len; i++) {
20876                 var n = ns[i];
20877                 var values = {};
20878                 var id = sid ? q.selectValue(sid, n) : undefined;
20879                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20880                     var f = fields.items[j];
20881                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20882                     v = f.convert(v);
20883                     values[f.name] = v;
20884                 }
20885                 var record = new recordType(values, id);
20886                 record.node = n;
20887                 records[records.length] = record;
20888             }
20889
20890             return {
20891                 success : success,
20892                 records : records,
20893                 totalRecords : totalRecords || records.length
20894             };
20895     }
20896 });/*
20897  * Based on:
20898  * Ext JS Library 1.1.1
20899  * Copyright(c) 2006-2007, Ext JS, LLC.
20900  *
20901  * Originally Released Under LGPL - original licence link has changed is not relivant.
20902  *
20903  * Fork - LGPL
20904  * <script type="text/javascript">
20905  */
20906
20907 /**
20908  * @class Roo.data.ArrayReader
20909  * @extends Roo.data.DataReader
20910  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20911  * Each element of that Array represents a row of data fields. The
20912  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20913  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20914  * <p>
20915  * Example code:.
20916  * <pre><code>
20917 var RecordDef = Roo.data.Record.create([
20918     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20919     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20920 ]);
20921 var myReader = new Roo.data.ArrayReader({
20922     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20923 }, RecordDef);
20924 </code></pre>
20925  * <p>
20926  * This would consume an Array like this:
20927  * <pre><code>
20928 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20929   </code></pre>
20930  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20931  * @constructor
20932  * Create a new JsonReader
20933  * @param {Object} meta Metadata configuration options.
20934  * @param {Object} recordType Either an Array of field definition objects
20935  * as specified to {@link Roo.data.Record#create},
20936  * or an {@link Roo.data.Record} object
20937  * created using {@link Roo.data.Record#create}.
20938  */
20939 Roo.data.ArrayReader = function(meta, recordType){
20940     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20941 };
20942
20943 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20944     /**
20945      * Create a data block containing Roo.data.Records from an XML document.
20946      * @param {Object} o An Array of row objects which represents the dataset.
20947      * @return {Object} data A data block which is used by an Roo.data.Store object as
20948      * a cache of Roo.data.Records.
20949      */
20950     readRecords : function(o){
20951         var sid = this.meta ? this.meta.id : null;
20952         var recordType = this.recordType, fields = recordType.prototype.fields;
20953         var records = [];
20954         var root = o;
20955             for(var i = 0; i < root.length; i++){
20956                     var n = root[i];
20957                 var values = {};
20958                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20959                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20960                 var f = fields.items[j];
20961                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20962                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20963                 v = f.convert(v);
20964                 values[f.name] = v;
20965             }
20966                 var record = new recordType(values, id);
20967                 record.json = n;
20968                 records[records.length] = record;
20969             }
20970             return {
20971                 records : records,
20972                 totalRecords : records.length
20973             };
20974     }
20975 });/*
20976  * Based on:
20977  * Ext JS Library 1.1.1
20978  * Copyright(c) 2006-2007, Ext JS, LLC.
20979  *
20980  * Originally Released Under LGPL - original licence link has changed is not relivant.
20981  *
20982  * Fork - LGPL
20983  * <script type="text/javascript">
20984  */
20985
20986
20987 /**
20988  * @class Roo.data.Tree
20989  * @extends Roo.util.Observable
20990  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20991  * in the tree have most standard DOM functionality.
20992  * @constructor
20993  * @param {Node} root (optional) The root node
20994  */
20995 Roo.data.Tree = function(root){
20996    this.nodeHash = {};
20997    /**
20998     * The root node for this tree
20999     * @type Node
21000     */
21001    this.root = null;
21002    if(root){
21003        this.setRootNode(root);
21004    }
21005    this.addEvents({
21006        /**
21007         * @event append
21008         * Fires when a new child node is appended to a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The newly appended node
21012         * @param {Number} index The index of the newly appended node
21013         */
21014        "append" : true,
21015        /**
21016         * @event remove
21017         * Fires when a child node is removed from a node in this tree.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node removed
21021         */
21022        "remove" : true,
21023        /**
21024         * @event move
21025         * Fires when a node is moved to a new location in the tree
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} node The node moved
21028         * @param {Node} oldParent The old parent of this node
21029         * @param {Node} newParent The new parent of this node
21030         * @param {Number} index The index it was moved to
21031         */
21032        "move" : true,
21033        /**
21034         * @event insert
21035         * Fires when a new child node is inserted in a node in this tree.
21036         * @param {Tree} tree The owner tree
21037         * @param {Node} parent The parent node
21038         * @param {Node} node The child node inserted
21039         * @param {Node} refNode The child node the node was inserted before
21040         */
21041        "insert" : true,
21042        /**
21043         * @event beforeappend
21044         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21045         * @param {Tree} tree The owner tree
21046         * @param {Node} parent The parent node
21047         * @param {Node} node The child node to be appended
21048         */
21049        "beforeappend" : true,
21050        /**
21051         * @event beforeremove
21052         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21053         * @param {Tree} tree The owner tree
21054         * @param {Node} parent The parent node
21055         * @param {Node} node The child node to be removed
21056         */
21057        "beforeremove" : true,
21058        /**
21059         * @event beforemove
21060         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21061         * @param {Tree} tree The owner tree
21062         * @param {Node} node The node being moved
21063         * @param {Node} oldParent The parent of the node
21064         * @param {Node} newParent The new parent the node is moving to
21065         * @param {Number} index The index it is being moved to
21066         */
21067        "beforemove" : true,
21068        /**
21069         * @event beforeinsert
21070         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21071         * @param {Tree} tree The owner tree
21072         * @param {Node} parent The parent node
21073         * @param {Node} node The child node to be inserted
21074         * @param {Node} refNode The child node the node is being inserted before
21075         */
21076        "beforeinsert" : true
21077    });
21078
21079     Roo.data.Tree.superclass.constructor.call(this);
21080 };
21081
21082 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21083     pathSeparator: "/",
21084
21085     proxyNodeEvent : function(){
21086         return this.fireEvent.apply(this, arguments);
21087     },
21088
21089     /**
21090      * Returns the root node for this tree.
21091      * @return {Node}
21092      */
21093     getRootNode : function(){
21094         return this.root;
21095     },
21096
21097     /**
21098      * Sets the root node for this tree.
21099      * @param {Node} node
21100      * @return {Node}
21101      */
21102     setRootNode : function(node){
21103         this.root = node;
21104         node.ownerTree = this;
21105         node.isRoot = true;
21106         this.registerNode(node);
21107         return node;
21108     },
21109
21110     /**
21111      * Gets a node in this tree by its id.
21112      * @param {String} id
21113      * @return {Node}
21114      */
21115     getNodeById : function(id){
21116         return this.nodeHash[id];
21117     },
21118
21119     registerNode : function(node){
21120         this.nodeHash[node.id] = node;
21121     },
21122
21123     unregisterNode : function(node){
21124         delete this.nodeHash[node.id];
21125     },
21126
21127     toString : function(){
21128         return "[Tree"+(this.id?" "+this.id:"")+"]";
21129     }
21130 });
21131
21132 /**
21133  * @class Roo.data.Node
21134  * @extends Roo.util.Observable
21135  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21136  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21137  * @constructor
21138  * @param {Object} attributes The attributes/config for the node
21139  */
21140 Roo.data.Node = function(attributes){
21141     /**
21142      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21143      * @type {Object}
21144      */
21145     this.attributes = attributes || {};
21146     this.leaf = this.attributes.leaf;
21147     /**
21148      * The node id. @type String
21149      */
21150     this.id = this.attributes.id;
21151     if(!this.id){
21152         this.id = Roo.id(null, "ynode-");
21153         this.attributes.id = this.id;
21154     }
21155     /**
21156      * All child nodes of this node. @type Array
21157      */
21158     this.childNodes = [];
21159     if(!this.childNodes.indexOf){ // indexOf is a must
21160         this.childNodes.indexOf = function(o){
21161             for(var i = 0, len = this.length; i < len; i++){
21162                 if(this[i] == o) {
21163                     return i;
21164                 }
21165             }
21166             return -1;
21167         };
21168     }
21169     /**
21170      * The parent node for this node. @type Node
21171      */
21172     this.parentNode = null;
21173     /**
21174      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21175      */
21176     this.firstChild = null;
21177     /**
21178      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21179      */
21180     this.lastChild = null;
21181     /**
21182      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21183      */
21184     this.previousSibling = null;
21185     /**
21186      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21187      */
21188     this.nextSibling = null;
21189
21190     this.addEvents({
21191        /**
21192         * @event append
21193         * Fires when a new child node is appended
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The newly appended node
21197         * @param {Number} index The index of the newly appended node
21198         */
21199        "append" : true,
21200        /**
21201         * @event remove
21202         * Fires when a child node is removed
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The removed node
21206         */
21207        "remove" : true,
21208        /**
21209         * @event move
21210         * Fires when this node is moved to a new location in the tree
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} oldParent The old parent of this node
21214         * @param {Node} newParent The new parent of this node
21215         * @param {Number} index The index it was moved to
21216         */
21217        "move" : true,
21218        /**
21219         * @event insert
21220         * Fires when a new child node is inserted.
21221         * @param {Tree} tree The owner tree
21222         * @param {Node} this This node
21223         * @param {Node} node The child node inserted
21224         * @param {Node} refNode The child node the node was inserted before
21225         */
21226        "insert" : true,
21227        /**
21228         * @event beforeappend
21229         * Fires before a new child is appended, return false to cancel the append.
21230         * @param {Tree} tree The owner tree
21231         * @param {Node} this This node
21232         * @param {Node} node The child node to be appended
21233         */
21234        "beforeappend" : true,
21235        /**
21236         * @event beforeremove
21237         * Fires before a child is removed, return false to cancel the remove.
21238         * @param {Tree} tree The owner tree
21239         * @param {Node} this This node
21240         * @param {Node} node The child node to be removed
21241         */
21242        "beforeremove" : true,
21243        /**
21244         * @event beforemove
21245         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21246         * @param {Tree} tree The owner tree
21247         * @param {Node} this This node
21248         * @param {Node} oldParent The parent of this node
21249         * @param {Node} newParent The new parent this node is moving to
21250         * @param {Number} index The index it is being moved to
21251         */
21252        "beforemove" : true,
21253        /**
21254         * @event beforeinsert
21255         * Fires before a new child is inserted, return false to cancel the insert.
21256         * @param {Tree} tree The owner tree
21257         * @param {Node} this This node
21258         * @param {Node} node The child node to be inserted
21259         * @param {Node} refNode The child node the node is being inserted before
21260         */
21261        "beforeinsert" : true
21262    });
21263     this.listeners = this.attributes.listeners;
21264     Roo.data.Node.superclass.constructor.call(this);
21265 };
21266
21267 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21268     fireEvent : function(evtName){
21269         // first do standard event for this node
21270         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21271             return false;
21272         }
21273         // then bubble it up to the tree if the event wasn't cancelled
21274         var ot = this.getOwnerTree();
21275         if(ot){
21276             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21277                 return false;
21278             }
21279         }
21280         return true;
21281     },
21282
21283     /**
21284      * Returns true if this node is a leaf
21285      * @return {Boolean}
21286      */
21287     isLeaf : function(){
21288         return this.leaf === true;
21289     },
21290
21291     // private
21292     setFirstChild : function(node){
21293         this.firstChild = node;
21294     },
21295
21296     //private
21297     setLastChild : function(node){
21298         this.lastChild = node;
21299     },
21300
21301
21302     /**
21303      * Returns true if this node is the last child of its parent
21304      * @return {Boolean}
21305      */
21306     isLast : function(){
21307        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21308     },
21309
21310     /**
21311      * Returns true if this node is the first child of its parent
21312      * @return {Boolean}
21313      */
21314     isFirst : function(){
21315        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21316     },
21317
21318     hasChildNodes : function(){
21319         return !this.isLeaf() && this.childNodes.length > 0;
21320     },
21321
21322     /**
21323      * Insert node(s) as the last child node of this node.
21324      * @param {Node/Array} node The node or Array of nodes to append
21325      * @return {Node} The appended node if single append, or null if an array was passed
21326      */
21327     appendChild : function(node){
21328         var multi = false;
21329         if(node instanceof Array){
21330             multi = node;
21331         }else if(arguments.length > 1){
21332             multi = arguments;
21333         }
21334         // if passed an array or multiple args do them one by one
21335         if(multi){
21336             for(var i = 0, len = multi.length; i < len; i++) {
21337                 this.appendChild(multi[i]);
21338             }
21339         }else{
21340             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21341                 return false;
21342             }
21343             var index = this.childNodes.length;
21344             var oldParent = node.parentNode;
21345             // it's a move, make sure we move it cleanly
21346             if(oldParent){
21347                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21348                     return false;
21349                 }
21350                 oldParent.removeChild(node);
21351             }
21352             index = this.childNodes.length;
21353             if(index == 0){
21354                 this.setFirstChild(node);
21355             }
21356             this.childNodes.push(node);
21357             node.parentNode = this;
21358             var ps = this.childNodes[index-1];
21359             if(ps){
21360                 node.previousSibling = ps;
21361                 ps.nextSibling = node;
21362             }else{
21363                 node.previousSibling = null;
21364             }
21365             node.nextSibling = null;
21366             this.setLastChild(node);
21367             node.setOwnerTree(this.getOwnerTree());
21368             this.fireEvent("append", this.ownerTree, this, node, index);
21369             if(oldParent){
21370                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21371             }
21372             return node;
21373         }
21374     },
21375
21376     /**
21377      * Removes a child node from this node.
21378      * @param {Node} node The node to remove
21379      * @return {Node} The removed node
21380      */
21381     removeChild : function(node){
21382         var index = this.childNodes.indexOf(node);
21383         if(index == -1){
21384             return false;
21385         }
21386         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21387             return false;
21388         }
21389
21390         // remove it from childNodes collection
21391         this.childNodes.splice(index, 1);
21392
21393         // update siblings
21394         if(node.previousSibling){
21395             node.previousSibling.nextSibling = node.nextSibling;
21396         }
21397         if(node.nextSibling){
21398             node.nextSibling.previousSibling = node.previousSibling;
21399         }
21400
21401         // update child refs
21402         if(this.firstChild == node){
21403             this.setFirstChild(node.nextSibling);
21404         }
21405         if(this.lastChild == node){
21406             this.setLastChild(node.previousSibling);
21407         }
21408
21409         node.setOwnerTree(null);
21410         // clear any references from the node
21411         node.parentNode = null;
21412         node.previousSibling = null;
21413         node.nextSibling = null;
21414         this.fireEvent("remove", this.ownerTree, this, node);
21415         return node;
21416     },
21417
21418     /**
21419      * Inserts the first node before the second node in this nodes childNodes collection.
21420      * @param {Node} node The node to insert
21421      * @param {Node} refNode The node to insert before (if null the node is appended)
21422      * @return {Node} The inserted node
21423      */
21424     insertBefore : function(node, refNode){
21425         if(!refNode){ // like standard Dom, refNode can be null for append
21426             return this.appendChild(node);
21427         }
21428         // nothing to do
21429         if(node == refNode){
21430             return false;
21431         }
21432
21433         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21434             return false;
21435         }
21436         var index = this.childNodes.indexOf(refNode);
21437         var oldParent = node.parentNode;
21438         var refIndex = index;
21439
21440         // when moving internally, indexes will change after remove
21441         if(oldParent == this && this.childNodes.indexOf(node) < index){
21442             refIndex--;
21443         }
21444
21445         // it's a move, make sure we move it cleanly
21446         if(oldParent){
21447             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21448                 return false;
21449             }
21450             oldParent.removeChild(node);
21451         }
21452         if(refIndex == 0){
21453             this.setFirstChild(node);
21454         }
21455         this.childNodes.splice(refIndex, 0, node);
21456         node.parentNode = this;
21457         var ps = this.childNodes[refIndex-1];
21458         if(ps){
21459             node.previousSibling = ps;
21460             ps.nextSibling = node;
21461         }else{
21462             node.previousSibling = null;
21463         }
21464         node.nextSibling = refNode;
21465         refNode.previousSibling = node;
21466         node.setOwnerTree(this.getOwnerTree());
21467         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21468         if(oldParent){
21469             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21470         }
21471         return node;
21472     },
21473
21474     /**
21475      * Returns the child node at the specified index.
21476      * @param {Number} index
21477      * @return {Node}
21478      */
21479     item : function(index){
21480         return this.childNodes[index];
21481     },
21482
21483     /**
21484      * Replaces one child node in this node with another.
21485      * @param {Node} newChild The replacement node
21486      * @param {Node} oldChild The node to replace
21487      * @return {Node} The replaced node
21488      */
21489     replaceChild : function(newChild, oldChild){
21490         this.insertBefore(newChild, oldChild);
21491         this.removeChild(oldChild);
21492         return oldChild;
21493     },
21494
21495     /**
21496      * Returns the index of a child node
21497      * @param {Node} node
21498      * @return {Number} The index of the node or -1 if it was not found
21499      */
21500     indexOf : function(child){
21501         return this.childNodes.indexOf(child);
21502     },
21503
21504     /**
21505      * Returns the tree this node is in.
21506      * @return {Tree}
21507      */
21508     getOwnerTree : function(){
21509         // if it doesn't have one, look for one
21510         if(!this.ownerTree){
21511             var p = this;
21512             while(p){
21513                 if(p.ownerTree){
21514                     this.ownerTree = p.ownerTree;
21515                     break;
21516                 }
21517                 p = p.parentNode;
21518             }
21519         }
21520         return this.ownerTree;
21521     },
21522
21523     /**
21524      * Returns depth of this node (the root node has a depth of 0)
21525      * @return {Number}
21526      */
21527     getDepth : function(){
21528         var depth = 0;
21529         var p = this;
21530         while(p.parentNode){
21531             ++depth;
21532             p = p.parentNode;
21533         }
21534         return depth;
21535     },
21536
21537     // private
21538     setOwnerTree : function(tree){
21539         // if it's move, we need to update everyone
21540         if(tree != this.ownerTree){
21541             if(this.ownerTree){
21542                 this.ownerTree.unregisterNode(this);
21543             }
21544             this.ownerTree = tree;
21545             var cs = this.childNodes;
21546             for(var i = 0, len = cs.length; i < len; i++) {
21547                 cs[i].setOwnerTree(tree);
21548             }
21549             if(tree){
21550                 tree.registerNode(this);
21551             }
21552         }
21553     },
21554
21555     /**
21556      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21557      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21558      * @return {String} The path
21559      */
21560     getPath : function(attr){
21561         attr = attr || "id";
21562         var p = this.parentNode;
21563         var b = [this.attributes[attr]];
21564         while(p){
21565             b.unshift(p.attributes[attr]);
21566             p = p.parentNode;
21567         }
21568         var sep = this.getOwnerTree().pathSeparator;
21569         return sep + b.join(sep);
21570     },
21571
21572     /**
21573      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21574      * function call will be the scope provided or the current node. The arguments to the function
21575      * will be the args provided or the current node. If the function returns false at any point,
21576      * the bubble is stopped.
21577      * @param {Function} fn The function to call
21578      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21579      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21580      */
21581     bubble : function(fn, scope, args){
21582         var p = this;
21583         while(p){
21584             if(fn.call(scope || p, args || p) === false){
21585                 break;
21586             }
21587             p = p.parentNode;
21588         }
21589     },
21590
21591     /**
21592      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21593      * function call will be the scope provided or the current node. The arguments to the function
21594      * will be the args provided or the current node. If the function returns false at any point,
21595      * the cascade is stopped on that branch.
21596      * @param {Function} fn The function to call
21597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21599      */
21600     cascade : function(fn, scope, args){
21601         if(fn.call(scope || this, args || this) !== false){
21602             var cs = this.childNodes;
21603             for(var i = 0, len = cs.length; i < len; i++) {
21604                 cs[i].cascade(fn, scope, args);
21605             }
21606         }
21607     },
21608
21609     /**
21610      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21611      * function call will be the scope provided or the current node. The arguments to the function
21612      * will be the args provided or the current node. If the function returns false at any point,
21613      * the iteration stops.
21614      * @param {Function} fn The function to call
21615      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21616      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21617      */
21618     eachChild : function(fn, scope, args){
21619         var cs = this.childNodes;
21620         for(var i = 0, len = cs.length; i < len; i++) {
21621                 if(fn.call(scope || this, args || cs[i]) === false){
21622                     break;
21623                 }
21624         }
21625     },
21626
21627     /**
21628      * Finds the first child that has the attribute with the specified value.
21629      * @param {String} attribute The attribute name
21630      * @param {Mixed} value The value to search for
21631      * @return {Node} The found child or null if none was found
21632      */
21633     findChild : function(attribute, value){
21634         var cs = this.childNodes;
21635         for(var i = 0, len = cs.length; i < len; i++) {
21636                 if(cs[i].attributes[attribute] == value){
21637                     return cs[i];
21638                 }
21639         }
21640         return null;
21641     },
21642
21643     /**
21644      * Finds the first child by a custom function. The child matches if the function passed
21645      * returns true.
21646      * @param {Function} fn
21647      * @param {Object} scope (optional)
21648      * @return {Node} The found child or null if none was found
21649      */
21650     findChildBy : function(fn, scope){
21651         var cs = this.childNodes;
21652         for(var i = 0, len = cs.length; i < len; i++) {
21653                 if(fn.call(scope||cs[i], cs[i]) === true){
21654                     return cs[i];
21655                 }
21656         }
21657         return null;
21658     },
21659
21660     /**
21661      * Sorts this nodes children using the supplied sort function
21662      * @param {Function} fn
21663      * @param {Object} scope (optional)
21664      */
21665     sort : function(fn, scope){
21666         var cs = this.childNodes;
21667         var len = cs.length;
21668         if(len > 0){
21669             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21670             cs.sort(sortFn);
21671             for(var i = 0; i < len; i++){
21672                 var n = cs[i];
21673                 n.previousSibling = cs[i-1];
21674                 n.nextSibling = cs[i+1];
21675                 if(i == 0){
21676                     this.setFirstChild(n);
21677                 }
21678                 if(i == len-1){
21679                     this.setLastChild(n);
21680                 }
21681             }
21682         }
21683     },
21684
21685     /**
21686      * Returns true if this node is an ancestor (at any point) of the passed node.
21687      * @param {Node} node
21688      * @return {Boolean}
21689      */
21690     contains : function(node){
21691         return node.isAncestor(this);
21692     },
21693
21694     /**
21695      * Returns true if the passed node is an ancestor (at any point) of this node.
21696      * @param {Node} node
21697      * @return {Boolean}
21698      */
21699     isAncestor : function(node){
21700         var p = this.parentNode;
21701         while(p){
21702             if(p == node){
21703                 return true;
21704             }
21705             p = p.parentNode;
21706         }
21707         return false;
21708     },
21709
21710     toString : function(){
21711         return "[Node"+(this.id?" "+this.id:"")+"]";
21712     }
21713 });/*
21714  * Based on:
21715  * Ext JS Library 1.1.1
21716  * Copyright(c) 2006-2007, Ext JS, LLC.
21717  *
21718  * Originally Released Under LGPL - original licence link has changed is not relivant.
21719  *
21720  * Fork - LGPL
21721  * <script type="text/javascript">
21722  */
21723  
21724
21725 /**
21726  * @class Roo.ComponentMgr
21727  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21728  * @singleton
21729  */
21730 Roo.ComponentMgr = function(){
21731     var all = new Roo.util.MixedCollection();
21732
21733     return {
21734         /**
21735          * Registers a component.
21736          * @param {Roo.Component} c The component
21737          */
21738         register : function(c){
21739             all.add(c);
21740         },
21741
21742         /**
21743          * Unregisters a component.
21744          * @param {Roo.Component} c The component
21745          */
21746         unregister : function(c){
21747             all.remove(c);
21748         },
21749
21750         /**
21751          * Returns a component by id
21752          * @param {String} id The component id
21753          */
21754         get : function(id){
21755             return all.get(id);
21756         },
21757
21758         /**
21759          * Registers a function that will be called when a specified component is added to ComponentMgr
21760          * @param {String} id The component id
21761          * @param {Funtction} fn The callback function
21762          * @param {Object} scope The scope of the callback
21763          */
21764         onAvailable : function(id, fn, scope){
21765             all.on("add", function(index, o){
21766                 if(o.id == id){
21767                     fn.call(scope || o, o);
21768                     all.un("add", fn, scope);
21769                 }
21770             });
21771         }
21772     };
21773 }();/*
21774  * Based on:
21775  * Ext JS Library 1.1.1
21776  * Copyright(c) 2006-2007, Ext JS, LLC.
21777  *
21778  * Originally Released Under LGPL - original licence link has changed is not relivant.
21779  *
21780  * Fork - LGPL
21781  * <script type="text/javascript">
21782  */
21783  
21784 /**
21785  * @class Roo.Component
21786  * @extends Roo.util.Observable
21787  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21788  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21789  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21790  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21791  * All visual components (widgets) that require rendering into a layout should subclass Component.
21792  * @constructor
21793  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21794  * 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
21795  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21796  */
21797 Roo.Component = function(config){
21798     config = config || {};
21799     if(config.tagName || config.dom || typeof config == "string"){ // element object
21800         config = {el: config, id: config.id || config};
21801     }
21802     this.initialConfig = config;
21803
21804     Roo.apply(this, config);
21805     this.addEvents({
21806         /**
21807          * @event disable
21808          * Fires after the component is disabled.
21809              * @param {Roo.Component} this
21810              */
21811         disable : true,
21812         /**
21813          * @event enable
21814          * Fires after the component is enabled.
21815              * @param {Roo.Component} this
21816              */
21817         enable : true,
21818         /**
21819          * @event beforeshow
21820          * Fires before the component is shown.  Return false to stop the show.
21821              * @param {Roo.Component} this
21822              */
21823         beforeshow : true,
21824         /**
21825          * @event show
21826          * Fires after the component is shown.
21827              * @param {Roo.Component} this
21828              */
21829         show : true,
21830         /**
21831          * @event beforehide
21832          * Fires before the component is hidden. Return false to stop the hide.
21833              * @param {Roo.Component} this
21834              */
21835         beforehide : true,
21836         /**
21837          * @event hide
21838          * Fires after the component is hidden.
21839              * @param {Roo.Component} this
21840              */
21841         hide : true,
21842         /**
21843          * @event beforerender
21844          * Fires before the component is rendered. Return false to stop the render.
21845              * @param {Roo.Component} this
21846              */
21847         beforerender : true,
21848         /**
21849          * @event render
21850          * Fires after the component is rendered.
21851              * @param {Roo.Component} this
21852              */
21853         render : true,
21854         /**
21855          * @event beforedestroy
21856          * Fires before the component is destroyed. Return false to stop the destroy.
21857              * @param {Roo.Component} this
21858              */
21859         beforedestroy : true,
21860         /**
21861          * @event destroy
21862          * Fires after the component is destroyed.
21863              * @param {Roo.Component} this
21864              */
21865         destroy : true
21866     });
21867     if(!this.id){
21868         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21869     }
21870     Roo.ComponentMgr.register(this);
21871     Roo.Component.superclass.constructor.call(this);
21872     this.initComponent();
21873     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21874         this.render(this.renderTo);
21875         delete this.renderTo;
21876     }
21877 };
21878
21879 // private
21880 Roo.Component.AUTO_ID = 1000;
21881
21882 Roo.extend(Roo.Component, Roo.util.Observable, {
21883     /**
21884      * @property {Boolean} hidden
21885      * true if this component is hidden. Read-only.
21886      */
21887     hidden : false,
21888     /**
21889      * true if this component is disabled. Read-only.
21890      */
21891     disabled : false,
21892     /**
21893      * true if this component has been rendered. Read-only.
21894      */
21895     rendered : false,
21896     
21897     /** @cfg {String} disableClass
21898      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21899      */
21900     disabledClass : "x-item-disabled",
21901         /** @cfg {Boolean} allowDomMove
21902          * Whether the component can move the Dom node when rendering (defaults to true).
21903          */
21904     allowDomMove : true,
21905     /** @cfg {String} hideMode
21906      * How this component should hidden. Supported values are
21907      * "visibility" (css visibility), "offsets" (negative offset position) and
21908      * "display" (css display) - defaults to "display".
21909      */
21910     hideMode: 'display',
21911
21912     // private
21913     ctype : "Roo.Component",
21914
21915     /** @cfg {String} actionMode 
21916      * which property holds the element that used for  hide() / show() / disable() / enable()
21917      * default is 'el' 
21918      */
21919     actionMode : "el",
21920
21921     // private
21922     getActionEl : function(){
21923         return this[this.actionMode];
21924     },
21925
21926     initComponent : Roo.emptyFn,
21927     /**
21928      * If this is a lazy rendering component, render it to its container element.
21929      * @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.
21930      */
21931     render : function(container, position){
21932         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21933             if(!container && this.el){
21934                 this.el = Roo.get(this.el);
21935                 container = this.el.dom.parentNode;
21936                 this.allowDomMove = false;
21937             }
21938             this.container = Roo.get(container);
21939             this.rendered = true;
21940             if(position !== undefined){
21941                 if(typeof position == 'number'){
21942                     position = this.container.dom.childNodes[position];
21943                 }else{
21944                     position = Roo.getDom(position);
21945                 }
21946             }
21947             this.onRender(this.container, position || null);
21948             if(this.cls){
21949                 this.el.addClass(this.cls);
21950                 delete this.cls;
21951             }
21952             if(this.style){
21953                 this.el.applyStyles(this.style);
21954                 delete this.style;
21955             }
21956             this.fireEvent("render", this);
21957             this.afterRender(this.container);
21958             if(this.hidden){
21959                 this.hide();
21960             }
21961             if(this.disabled){
21962                 this.disable();
21963             }
21964         }
21965         return this;
21966     },
21967
21968     // private
21969     // default function is not really useful
21970     onRender : function(ct, position){
21971         if(this.el){
21972             this.el = Roo.get(this.el);
21973             if(this.allowDomMove !== false){
21974                 ct.dom.insertBefore(this.el.dom, position);
21975             }
21976         }
21977     },
21978
21979     // private
21980     getAutoCreate : function(){
21981         var cfg = typeof this.autoCreate == "object" ?
21982                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21983         if(this.id && !cfg.id){
21984             cfg.id = this.id;
21985         }
21986         return cfg;
21987     },
21988
21989     // private
21990     afterRender : Roo.emptyFn,
21991
21992     /**
21993      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21994      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21995      */
21996     destroy : function(){
21997         if(this.fireEvent("beforedestroy", this) !== false){
21998             this.purgeListeners();
21999             this.beforeDestroy();
22000             if(this.rendered){
22001                 this.el.removeAllListeners();
22002                 this.el.remove();
22003                 if(this.actionMode == "container"){
22004                     this.container.remove();
22005                 }
22006             }
22007             this.onDestroy();
22008             Roo.ComponentMgr.unregister(this);
22009             this.fireEvent("destroy", this);
22010         }
22011     },
22012
22013         // private
22014     beforeDestroy : function(){
22015
22016     },
22017
22018         // private
22019         onDestroy : function(){
22020
22021     },
22022
22023     /**
22024      * Returns the underlying {@link Roo.Element}.
22025      * @return {Roo.Element} The element
22026      */
22027     getEl : function(){
22028         return this.el;
22029     },
22030
22031     /**
22032      * Returns the id of this component.
22033      * @return {String}
22034      */
22035     getId : function(){
22036         return this.id;
22037     },
22038
22039     /**
22040      * Try to focus this component.
22041      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22042      * @return {Roo.Component} this
22043      */
22044     focus : function(selectText){
22045         if(this.rendered){
22046             this.el.focus();
22047             if(selectText === true){
22048                 this.el.dom.select();
22049             }
22050         }
22051         return this;
22052     },
22053
22054     // private
22055     blur : function(){
22056         if(this.rendered){
22057             this.el.blur();
22058         }
22059         return this;
22060     },
22061
22062     /**
22063      * Disable this component.
22064      * @return {Roo.Component} this
22065      */
22066     disable : function(){
22067         if(this.rendered){
22068             this.onDisable();
22069         }
22070         this.disabled = true;
22071         this.fireEvent("disable", this);
22072         return this;
22073     },
22074
22075         // private
22076     onDisable : function(){
22077         this.getActionEl().addClass(this.disabledClass);
22078         this.el.dom.disabled = true;
22079     },
22080
22081     /**
22082      * Enable this component.
22083      * @return {Roo.Component} this
22084      */
22085     enable : function(){
22086         if(this.rendered){
22087             this.onEnable();
22088         }
22089         this.disabled = false;
22090         this.fireEvent("enable", this);
22091         return this;
22092     },
22093
22094         // private
22095     onEnable : function(){
22096         this.getActionEl().removeClass(this.disabledClass);
22097         this.el.dom.disabled = false;
22098     },
22099
22100     /**
22101      * Convenience function for setting disabled/enabled by boolean.
22102      * @param {Boolean} disabled
22103      */
22104     setDisabled : function(disabled){
22105         this[disabled ? "disable" : "enable"]();
22106     },
22107
22108     /**
22109      * Show this component.
22110      * @return {Roo.Component} this
22111      */
22112     show: function(){
22113         if(this.fireEvent("beforeshow", this) !== false){
22114             this.hidden = false;
22115             if(this.rendered){
22116                 this.onShow();
22117             }
22118             this.fireEvent("show", this);
22119         }
22120         return this;
22121     },
22122
22123     // private
22124     onShow : function(){
22125         var ae = this.getActionEl();
22126         if(this.hideMode == 'visibility'){
22127             ae.dom.style.visibility = "visible";
22128         }else if(this.hideMode == 'offsets'){
22129             ae.removeClass('x-hidden');
22130         }else{
22131             ae.dom.style.display = "";
22132         }
22133     },
22134
22135     /**
22136      * Hide this component.
22137      * @return {Roo.Component} this
22138      */
22139     hide: function(){
22140         if(this.fireEvent("beforehide", this) !== false){
22141             this.hidden = true;
22142             if(this.rendered){
22143                 this.onHide();
22144             }
22145             this.fireEvent("hide", this);
22146         }
22147         return this;
22148     },
22149
22150     // private
22151     onHide : function(){
22152         var ae = this.getActionEl();
22153         if(this.hideMode == 'visibility'){
22154             ae.dom.style.visibility = "hidden";
22155         }else if(this.hideMode == 'offsets'){
22156             ae.addClass('x-hidden');
22157         }else{
22158             ae.dom.style.display = "none";
22159         }
22160     },
22161
22162     /**
22163      * Convenience function to hide or show this component by boolean.
22164      * @param {Boolean} visible True to show, false to hide
22165      * @return {Roo.Component} this
22166      */
22167     setVisible: function(visible){
22168         if(visible) {
22169             this.show();
22170         }else{
22171             this.hide();
22172         }
22173         return this;
22174     },
22175
22176     /**
22177      * Returns true if this component is visible.
22178      */
22179     isVisible : function(){
22180         return this.getActionEl().isVisible();
22181     },
22182
22183     cloneConfig : function(overrides){
22184         overrides = overrides || {};
22185         var id = overrides.id || Roo.id();
22186         var cfg = Roo.applyIf(overrides, this.initialConfig);
22187         cfg.id = id; // prevent dup id
22188         return new this.constructor(cfg);
22189     }
22190 });/*
22191  * Based on:
22192  * Ext JS Library 1.1.1
22193  * Copyright(c) 2006-2007, Ext JS, LLC.
22194  *
22195  * Originally Released Under LGPL - original licence link has changed is not relivant.
22196  *
22197  * Fork - LGPL
22198  * <script type="text/javascript">
22199  */
22200  (function(){ 
22201 /**
22202  * @class Roo.Layer
22203  * @extends Roo.Element
22204  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22205  * automatic maintaining of shadow/shim positions.
22206  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22207  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22208  * you can pass a string with a CSS class name. False turns off the shadow.
22209  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22210  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22211  * @cfg {String} cls CSS class to add to the element
22212  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22213  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22214  * @constructor
22215  * @param {Object} config An object with config options.
22216  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22217  */
22218
22219 Roo.Layer = function(config, existingEl){
22220     config = config || {};
22221     var dh = Roo.DomHelper;
22222     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22223     if(existingEl){
22224         this.dom = Roo.getDom(existingEl);
22225     }
22226     if(!this.dom){
22227         var o = config.dh || {tag: "div", cls: "x-layer"};
22228         this.dom = dh.append(pel, o);
22229     }
22230     if(config.cls){
22231         this.addClass(config.cls);
22232     }
22233     this.constrain = config.constrain !== false;
22234     this.visibilityMode = Roo.Element.VISIBILITY;
22235     if(config.id){
22236         this.id = this.dom.id = config.id;
22237     }else{
22238         this.id = Roo.id(this.dom);
22239     }
22240     this.zindex = config.zindex || this.getZIndex();
22241     this.position("absolute", this.zindex);
22242     if(config.shadow){
22243         this.shadowOffset = config.shadowOffset || 4;
22244         this.shadow = new Roo.Shadow({
22245             offset : this.shadowOffset,
22246             mode : config.shadow
22247         });
22248     }else{
22249         this.shadowOffset = 0;
22250     }
22251     this.useShim = config.shim !== false && Roo.useShims;
22252     this.useDisplay = config.useDisplay;
22253     this.hide();
22254 };
22255
22256 var supr = Roo.Element.prototype;
22257
22258 // shims are shared among layer to keep from having 100 iframes
22259 var shims = [];
22260
22261 Roo.extend(Roo.Layer, Roo.Element, {
22262
22263     getZIndex : function(){
22264         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22265     },
22266
22267     getShim : function(){
22268         if(!this.useShim){
22269             return null;
22270         }
22271         if(this.shim){
22272             return this.shim;
22273         }
22274         var shim = shims.shift();
22275         if(!shim){
22276             shim = this.createShim();
22277             shim.enableDisplayMode('block');
22278             shim.dom.style.display = 'none';
22279             shim.dom.style.visibility = 'visible';
22280         }
22281         var pn = this.dom.parentNode;
22282         if(shim.dom.parentNode != pn){
22283             pn.insertBefore(shim.dom, this.dom);
22284         }
22285         shim.setStyle('z-index', this.getZIndex()-2);
22286         this.shim = shim;
22287         return shim;
22288     },
22289
22290     hideShim : function(){
22291         if(this.shim){
22292             this.shim.setDisplayed(false);
22293             shims.push(this.shim);
22294             delete this.shim;
22295         }
22296     },
22297
22298     disableShadow : function(){
22299         if(this.shadow){
22300             this.shadowDisabled = true;
22301             this.shadow.hide();
22302             this.lastShadowOffset = this.shadowOffset;
22303             this.shadowOffset = 0;
22304         }
22305     },
22306
22307     enableShadow : function(show){
22308         if(this.shadow){
22309             this.shadowDisabled = false;
22310             this.shadowOffset = this.lastShadowOffset;
22311             delete this.lastShadowOffset;
22312             if(show){
22313                 this.sync(true);
22314             }
22315         }
22316     },
22317
22318     // private
22319     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22320     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22321     sync : function(doShow){
22322         var sw = this.shadow;
22323         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22324             var sh = this.getShim();
22325
22326             var w = this.getWidth(),
22327                 h = this.getHeight();
22328
22329             var l = this.getLeft(true),
22330                 t = this.getTop(true);
22331
22332             if(sw && !this.shadowDisabled){
22333                 if(doShow && !sw.isVisible()){
22334                     sw.show(this);
22335                 }else{
22336                     sw.realign(l, t, w, h);
22337                 }
22338                 if(sh){
22339                     if(doShow){
22340                        sh.show();
22341                     }
22342                     // fit the shim behind the shadow, so it is shimmed too
22343                     var a = sw.adjusts, s = sh.dom.style;
22344                     s.left = (Math.min(l, l+a.l))+"px";
22345                     s.top = (Math.min(t, t+a.t))+"px";
22346                     s.width = (w+a.w)+"px";
22347                     s.height = (h+a.h)+"px";
22348                 }
22349             }else if(sh){
22350                 if(doShow){
22351                    sh.show();
22352                 }
22353                 sh.setSize(w, h);
22354                 sh.setLeftTop(l, t);
22355             }
22356             
22357         }
22358     },
22359
22360     // private
22361     destroy : function(){
22362         this.hideShim();
22363         if(this.shadow){
22364             this.shadow.hide();
22365         }
22366         this.removeAllListeners();
22367         var pn = this.dom.parentNode;
22368         if(pn){
22369             pn.removeChild(this.dom);
22370         }
22371         Roo.Element.uncache(this.id);
22372     },
22373
22374     remove : function(){
22375         this.destroy();
22376     },
22377
22378     // private
22379     beginUpdate : function(){
22380         this.updating = true;
22381     },
22382
22383     // private
22384     endUpdate : function(){
22385         this.updating = false;
22386         this.sync(true);
22387     },
22388
22389     // private
22390     hideUnders : function(negOffset){
22391         if(this.shadow){
22392             this.shadow.hide();
22393         }
22394         this.hideShim();
22395     },
22396
22397     // private
22398     constrainXY : function(){
22399         if(this.constrain){
22400             var vw = Roo.lib.Dom.getViewWidth(),
22401                 vh = Roo.lib.Dom.getViewHeight();
22402             var s = Roo.get(document).getScroll();
22403
22404             var xy = this.getXY();
22405             var x = xy[0], y = xy[1];   
22406             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22407             // only move it if it needs it
22408             var moved = false;
22409             // first validate right/bottom
22410             if((x + w) > vw+s.left){
22411                 x = vw - w - this.shadowOffset;
22412                 moved = true;
22413             }
22414             if((y + h) > vh+s.top){
22415                 y = vh - h - this.shadowOffset;
22416                 moved = true;
22417             }
22418             // then make sure top/left isn't negative
22419             if(x < s.left){
22420                 x = s.left;
22421                 moved = true;
22422             }
22423             if(y < s.top){
22424                 y = s.top;
22425                 moved = true;
22426             }
22427             if(moved){
22428                 if(this.avoidY){
22429                     var ay = this.avoidY;
22430                     if(y <= ay && (y+h) >= ay){
22431                         y = ay-h-5;   
22432                     }
22433                 }
22434                 xy = [x, y];
22435                 this.storeXY(xy);
22436                 supr.setXY.call(this, xy);
22437                 this.sync();
22438             }
22439         }
22440     },
22441
22442     isVisible : function(){
22443         return this.visible;    
22444     },
22445
22446     // private
22447     showAction : function(){
22448         this.visible = true; // track visibility to prevent getStyle calls
22449         if(this.useDisplay === true){
22450             this.setDisplayed("");
22451         }else if(this.lastXY){
22452             supr.setXY.call(this, this.lastXY);
22453         }else if(this.lastLT){
22454             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22455         }
22456     },
22457
22458     // private
22459     hideAction : function(){
22460         this.visible = false;
22461         if(this.useDisplay === true){
22462             this.setDisplayed(false);
22463         }else{
22464             this.setLeftTop(-10000,-10000);
22465         }
22466     },
22467
22468     // overridden Element method
22469     setVisible : function(v, a, d, c, e){
22470         if(v){
22471             this.showAction();
22472         }
22473         if(a && v){
22474             var cb = function(){
22475                 this.sync(true);
22476                 if(c){
22477                     c();
22478                 }
22479             }.createDelegate(this);
22480             supr.setVisible.call(this, true, true, d, cb, e);
22481         }else{
22482             if(!v){
22483                 this.hideUnders(true);
22484             }
22485             var cb = c;
22486             if(a){
22487                 cb = function(){
22488                     this.hideAction();
22489                     if(c){
22490                         c();
22491                     }
22492                 }.createDelegate(this);
22493             }
22494             supr.setVisible.call(this, v, a, d, cb, e);
22495             if(v){
22496                 this.sync(true);
22497             }else if(!a){
22498                 this.hideAction();
22499             }
22500         }
22501     },
22502
22503     storeXY : function(xy){
22504         delete this.lastLT;
22505         this.lastXY = xy;
22506     },
22507
22508     storeLeftTop : function(left, top){
22509         delete this.lastXY;
22510         this.lastLT = [left, top];
22511     },
22512
22513     // private
22514     beforeFx : function(){
22515         this.beforeAction();
22516         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22517     },
22518
22519     // private
22520     afterFx : function(){
22521         Roo.Layer.superclass.afterFx.apply(this, arguments);
22522         this.sync(this.isVisible());
22523     },
22524
22525     // private
22526     beforeAction : function(){
22527         if(!this.updating && this.shadow){
22528             this.shadow.hide();
22529         }
22530     },
22531
22532     // overridden Element method
22533     setLeft : function(left){
22534         this.storeLeftTop(left, this.getTop(true));
22535         supr.setLeft.apply(this, arguments);
22536         this.sync();
22537     },
22538
22539     setTop : function(top){
22540         this.storeLeftTop(this.getLeft(true), top);
22541         supr.setTop.apply(this, arguments);
22542         this.sync();
22543     },
22544
22545     setLeftTop : function(left, top){
22546         this.storeLeftTop(left, top);
22547         supr.setLeftTop.apply(this, arguments);
22548         this.sync();
22549     },
22550
22551     setXY : function(xy, a, d, c, e){
22552         this.fixDisplay();
22553         this.beforeAction();
22554         this.storeXY(xy);
22555         var cb = this.createCB(c);
22556         supr.setXY.call(this, xy, a, d, cb, e);
22557         if(!a){
22558             cb();
22559         }
22560     },
22561
22562     // private
22563     createCB : function(c){
22564         var el = this;
22565         return function(){
22566             el.constrainXY();
22567             el.sync(true);
22568             if(c){
22569                 c();
22570             }
22571         };
22572     },
22573
22574     // overridden Element method
22575     setX : function(x, a, d, c, e){
22576         this.setXY([x, this.getY()], a, d, c, e);
22577     },
22578
22579     // overridden Element method
22580     setY : function(y, a, d, c, e){
22581         this.setXY([this.getX(), y], a, d, c, e);
22582     },
22583
22584     // overridden Element method
22585     setSize : function(w, h, a, d, c, e){
22586         this.beforeAction();
22587         var cb = this.createCB(c);
22588         supr.setSize.call(this, w, h, a, d, cb, e);
22589         if(!a){
22590             cb();
22591         }
22592     },
22593
22594     // overridden Element method
22595     setWidth : function(w, a, d, c, e){
22596         this.beforeAction();
22597         var cb = this.createCB(c);
22598         supr.setWidth.call(this, w, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // overridden Element method
22605     setHeight : function(h, a, d, c, e){
22606         this.beforeAction();
22607         var cb = this.createCB(c);
22608         supr.setHeight.call(this, h, a, d, cb, e);
22609         if(!a){
22610             cb();
22611         }
22612     },
22613
22614     // overridden Element method
22615     setBounds : function(x, y, w, h, a, d, c, e){
22616         this.beforeAction();
22617         var cb = this.createCB(c);
22618         if(!a){
22619             this.storeXY([x, y]);
22620             supr.setXY.call(this, [x, y]);
22621             supr.setSize.call(this, w, h, a, d, cb, e);
22622             cb();
22623         }else{
22624             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22625         }
22626         return this;
22627     },
22628     
22629     /**
22630      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22631      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22632      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22633      * @param {Number} zindex The new z-index to set
22634      * @return {this} The Layer
22635      */
22636     setZIndex : function(zindex){
22637         this.zindex = zindex;
22638         this.setStyle("z-index", zindex + 2);
22639         if(this.shadow){
22640             this.shadow.setZIndex(zindex + 1);
22641         }
22642         if(this.shim){
22643             this.shim.setStyle("z-index", zindex);
22644         }
22645     }
22646 });
22647 })();/*
22648  * Based on:
22649  * Ext JS Library 1.1.1
22650  * Copyright(c) 2006-2007, Ext JS, LLC.
22651  *
22652  * Originally Released Under LGPL - original licence link has changed is not relivant.
22653  *
22654  * Fork - LGPL
22655  * <script type="text/javascript">
22656  */
22657
22658
22659 /**
22660  * @class Roo.Shadow
22661  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22662  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22663  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22664  * @constructor
22665  * Create a new Shadow
22666  * @param {Object} config The config object
22667  */
22668 Roo.Shadow = function(config){
22669     Roo.apply(this, config);
22670     if(typeof this.mode != "string"){
22671         this.mode = this.defaultMode;
22672     }
22673     var o = this.offset, a = {h: 0};
22674     var rad = Math.floor(this.offset/2);
22675     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22676         case "drop":
22677             a.w = 0;
22678             a.l = a.t = o;
22679             a.t -= 1;
22680             if(Roo.isIE){
22681                 a.l -= this.offset + rad;
22682                 a.t -= this.offset + rad;
22683                 a.w -= rad;
22684                 a.h -= rad;
22685                 a.t += 1;
22686             }
22687         break;
22688         case "sides":
22689             a.w = (o*2);
22690             a.l = -o;
22691             a.t = o-1;
22692             if(Roo.isIE){
22693                 a.l -= (this.offset - rad);
22694                 a.t -= this.offset + rad;
22695                 a.l += 1;
22696                 a.w -= (this.offset - rad)*2;
22697                 a.w -= rad + 1;
22698                 a.h -= 1;
22699             }
22700         break;
22701         case "frame":
22702             a.w = a.h = (o*2);
22703             a.l = a.t = -o;
22704             a.t += 1;
22705             a.h -= 2;
22706             if(Roo.isIE){
22707                 a.l -= (this.offset - rad);
22708                 a.t -= (this.offset - rad);
22709                 a.l += 1;
22710                 a.w -= (this.offset + rad + 1);
22711                 a.h -= (this.offset + rad);
22712                 a.h += 1;
22713             }
22714         break;
22715     };
22716
22717     this.adjusts = a;
22718 };
22719
22720 Roo.Shadow.prototype = {
22721     /**
22722      * @cfg {String} mode
22723      * The shadow display mode.  Supports the following options:<br />
22724      * sides: Shadow displays on both sides and bottom only<br />
22725      * frame: Shadow displays equally on all four sides<br />
22726      * drop: Traditional bottom-right drop shadow (default)
22727      */
22728     /**
22729      * @cfg {String} offset
22730      * The number of pixels to offset the shadow from the element (defaults to 4)
22731      */
22732     offset: 4,
22733
22734     // private
22735     defaultMode: "drop",
22736
22737     /**
22738      * Displays the shadow under the target element
22739      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22740      */
22741     show : function(target){
22742         target = Roo.get(target);
22743         if(!this.el){
22744             this.el = Roo.Shadow.Pool.pull();
22745             if(this.el.dom.nextSibling != target.dom){
22746                 this.el.insertBefore(target);
22747             }
22748         }
22749         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22750         if(Roo.isIE){
22751             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22752         }
22753         this.realign(
22754             target.getLeft(true),
22755             target.getTop(true),
22756             target.getWidth(),
22757             target.getHeight()
22758         );
22759         this.el.dom.style.display = "block";
22760     },
22761
22762     /**
22763      * Returns true if the shadow is visible, else false
22764      */
22765     isVisible : function(){
22766         return this.el ? true : false;  
22767     },
22768
22769     /**
22770      * Direct alignment when values are already available. Show must be called at least once before
22771      * calling this method to ensure it is initialized.
22772      * @param {Number} left The target element left position
22773      * @param {Number} top The target element top position
22774      * @param {Number} width The target element width
22775      * @param {Number} height The target element height
22776      */
22777     realign : function(l, t, w, h){
22778         if(!this.el){
22779             return;
22780         }
22781         var a = this.adjusts, d = this.el.dom, s = d.style;
22782         var iea = 0;
22783         s.left = (l+a.l)+"px";
22784         s.top = (t+a.t)+"px";
22785         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22786  
22787         if(s.width != sws || s.height != shs){
22788             s.width = sws;
22789             s.height = shs;
22790             if(!Roo.isIE){
22791                 var cn = d.childNodes;
22792                 var sww = Math.max(0, (sw-12))+"px";
22793                 cn[0].childNodes[1].style.width = sww;
22794                 cn[1].childNodes[1].style.width = sww;
22795                 cn[2].childNodes[1].style.width = sww;
22796                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22797             }
22798         }
22799     },
22800
22801     /**
22802      * Hides this shadow
22803      */
22804     hide : function(){
22805         if(this.el){
22806             this.el.dom.style.display = "none";
22807             Roo.Shadow.Pool.push(this.el);
22808             delete this.el;
22809         }
22810     },
22811
22812     /**
22813      * Adjust the z-index of this shadow
22814      * @param {Number} zindex The new z-index
22815      */
22816     setZIndex : function(z){
22817         this.zIndex = z;
22818         if(this.el){
22819             this.el.setStyle("z-index", z);
22820         }
22821     }
22822 };
22823
22824 // Private utility class that manages the internal Shadow cache
22825 Roo.Shadow.Pool = function(){
22826     var p = [];
22827     var markup = Roo.isIE ?
22828                  '<div class="x-ie-shadow"></div>' :
22829                  '<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>';
22830     return {
22831         pull : function(){
22832             var sh = p.shift();
22833             if(!sh){
22834                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22835                 sh.autoBoxAdjust = false;
22836             }
22837             return sh;
22838         },
22839
22840         push : function(sh){
22841             p.push(sh);
22842         }
22843     };
22844 }();/*
22845  * Based on:
22846  * Ext JS Library 1.1.1
22847  * Copyright(c) 2006-2007, Ext JS, LLC.
22848  *
22849  * Originally Released Under LGPL - original licence link has changed is not relivant.
22850  *
22851  * Fork - LGPL
22852  * <script type="text/javascript">
22853  */
22854
22855 /**
22856  * @class Roo.BoxComponent
22857  * @extends Roo.Component
22858  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22859  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22860  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22861  * layout containers.
22862  * @constructor
22863  * @param {Roo.Element/String/Object} config The configuration options.
22864  */
22865 Roo.BoxComponent = function(config){
22866     Roo.Component.call(this, config);
22867     this.addEvents({
22868         /**
22869          * @event resize
22870          * Fires after the component is resized.
22871              * @param {Roo.Component} this
22872              * @param {Number} adjWidth The box-adjusted width that was set
22873              * @param {Number} adjHeight The box-adjusted height that was set
22874              * @param {Number} rawWidth The width that was originally specified
22875              * @param {Number} rawHeight The height that was originally specified
22876              */
22877         resize : true,
22878         /**
22879          * @event move
22880          * Fires after the component is moved.
22881              * @param {Roo.Component} this
22882              * @param {Number} x The new x position
22883              * @param {Number} y The new y position
22884              */
22885         move : true
22886     });
22887 };
22888
22889 Roo.extend(Roo.BoxComponent, Roo.Component, {
22890     // private, set in afterRender to signify that the component has been rendered
22891     boxReady : false,
22892     // private, used to defer height settings to subclasses
22893     deferHeight: false,
22894     /** @cfg {Number} width
22895      * width (optional) size of component
22896      */
22897      /** @cfg {Number} height
22898      * height (optional) size of component
22899      */
22900      
22901     /**
22902      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22903      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22904      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22905      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22906      * @return {Roo.BoxComponent} this
22907      */
22908     setSize : function(w, h){
22909         // support for standard size objects
22910         if(typeof w == 'object'){
22911             h = w.height;
22912             w = w.width;
22913         }
22914         // not rendered
22915         if(!this.boxReady){
22916             this.width = w;
22917             this.height = h;
22918             return this;
22919         }
22920
22921         // prevent recalcs when not needed
22922         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22923             return this;
22924         }
22925         this.lastSize = {width: w, height: h};
22926
22927         var adj = this.adjustSize(w, h);
22928         var aw = adj.width, ah = adj.height;
22929         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22930             var rz = this.getResizeEl();
22931             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22932                 rz.setSize(aw, ah);
22933             }else if(!this.deferHeight && ah !== undefined){
22934                 rz.setHeight(ah);
22935             }else if(aw !== undefined){
22936                 rz.setWidth(aw);
22937             }
22938             this.onResize(aw, ah, w, h);
22939             this.fireEvent('resize', this, aw, ah, w, h);
22940         }
22941         return this;
22942     },
22943
22944     /**
22945      * Gets the current size of the component's underlying element.
22946      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22947      */
22948     getSize : function(){
22949         return this.el.getSize();
22950     },
22951
22952     /**
22953      * Gets the current XY position of the component's underlying element.
22954      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22955      * @return {Array} The XY position of the element (e.g., [100, 200])
22956      */
22957     getPosition : function(local){
22958         if(local === true){
22959             return [this.el.getLeft(true), this.el.getTop(true)];
22960         }
22961         return this.xy || this.el.getXY();
22962     },
22963
22964     /**
22965      * Gets the current box measurements of the component's underlying element.
22966      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22967      * @returns {Object} box An object in the format {x, y, width, height}
22968      */
22969     getBox : function(local){
22970         var s = this.el.getSize();
22971         if(local){
22972             s.x = this.el.getLeft(true);
22973             s.y = this.el.getTop(true);
22974         }else{
22975             var xy = this.xy || this.el.getXY();
22976             s.x = xy[0];
22977             s.y = xy[1];
22978         }
22979         return s;
22980     },
22981
22982     /**
22983      * Sets the current box measurements of the component's underlying element.
22984      * @param {Object} box An object in the format {x, y, width, height}
22985      * @returns {Roo.BoxComponent} this
22986      */
22987     updateBox : function(box){
22988         this.setSize(box.width, box.height);
22989         this.setPagePosition(box.x, box.y);
22990         return this;
22991     },
22992
22993     // protected
22994     getResizeEl : function(){
22995         return this.resizeEl || this.el;
22996     },
22997
22998     // protected
22999     getPositionEl : function(){
23000         return this.positionEl || this.el;
23001     },
23002
23003     /**
23004      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23005      * This method fires the move event.
23006      * @param {Number} left The new left
23007      * @param {Number} top The new top
23008      * @returns {Roo.BoxComponent} this
23009      */
23010     setPosition : function(x, y){
23011         this.x = x;
23012         this.y = y;
23013         if(!this.boxReady){
23014             return this;
23015         }
23016         var adj = this.adjustPosition(x, y);
23017         var ax = adj.x, ay = adj.y;
23018
23019         var el = this.getPositionEl();
23020         if(ax !== undefined || ay !== undefined){
23021             if(ax !== undefined && ay !== undefined){
23022                 el.setLeftTop(ax, ay);
23023             }else if(ax !== undefined){
23024                 el.setLeft(ax);
23025             }else if(ay !== undefined){
23026                 el.setTop(ay);
23027             }
23028             this.onPosition(ax, ay);
23029             this.fireEvent('move', this, ax, ay);
23030         }
23031         return this;
23032     },
23033
23034     /**
23035      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23036      * This method fires the move event.
23037      * @param {Number} x The new x position
23038      * @param {Number} y The new y position
23039      * @returns {Roo.BoxComponent} this
23040      */
23041     setPagePosition : function(x, y){
23042         this.pageX = x;
23043         this.pageY = y;
23044         if(!this.boxReady){
23045             return;
23046         }
23047         if(x === undefined || y === undefined){ // cannot translate undefined points
23048             return;
23049         }
23050         var p = this.el.translatePoints(x, y);
23051         this.setPosition(p.left, p.top);
23052         return this;
23053     },
23054
23055     // private
23056     onRender : function(ct, position){
23057         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23058         if(this.resizeEl){
23059             this.resizeEl = Roo.get(this.resizeEl);
23060         }
23061         if(this.positionEl){
23062             this.positionEl = Roo.get(this.positionEl);
23063         }
23064     },
23065
23066     // private
23067     afterRender : function(){
23068         Roo.BoxComponent.superclass.afterRender.call(this);
23069         this.boxReady = true;
23070         this.setSize(this.width, this.height);
23071         if(this.x || this.y){
23072             this.setPosition(this.x, this.y);
23073         }
23074         if(this.pageX || this.pageY){
23075             this.setPagePosition(this.pageX, this.pageY);
23076         }
23077     },
23078
23079     /**
23080      * Force the component's size to recalculate based on the underlying element's current height and width.
23081      * @returns {Roo.BoxComponent} this
23082      */
23083     syncSize : function(){
23084         delete this.lastSize;
23085         this.setSize(this.el.getWidth(), this.el.getHeight());
23086         return this;
23087     },
23088
23089     /**
23090      * Called after the component is resized, this method is empty by default but can be implemented by any
23091      * subclass that needs to perform custom logic after a resize occurs.
23092      * @param {Number} adjWidth The box-adjusted width that was set
23093      * @param {Number} adjHeight The box-adjusted height that was set
23094      * @param {Number} rawWidth The width that was originally specified
23095      * @param {Number} rawHeight The height that was originally specified
23096      */
23097     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23098
23099     },
23100
23101     /**
23102      * Called after the component is moved, this method is empty by default but can be implemented by any
23103      * subclass that needs to perform custom logic after a move occurs.
23104      * @param {Number} x The new x position
23105      * @param {Number} y The new y position
23106      */
23107     onPosition : function(x, y){
23108
23109     },
23110
23111     // private
23112     adjustSize : function(w, h){
23113         if(this.autoWidth){
23114             w = 'auto';
23115         }
23116         if(this.autoHeight){
23117             h = 'auto';
23118         }
23119         return {width : w, height: h};
23120     },
23121
23122     // private
23123     adjustPosition : function(x, y){
23124         return {x : x, y: y};
23125     }
23126 });/*
23127  * Based on:
23128  * Ext JS Library 1.1.1
23129  * Copyright(c) 2006-2007, Ext JS, LLC.
23130  *
23131  * Originally Released Under LGPL - original licence link has changed is not relivant.
23132  *
23133  * Fork - LGPL
23134  * <script type="text/javascript">
23135  */
23136
23137
23138 /**
23139  * @class Roo.SplitBar
23140  * @extends Roo.util.Observable
23141  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23142  * <br><br>
23143  * Usage:
23144  * <pre><code>
23145 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23146                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23147 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23148 split.minSize = 100;
23149 split.maxSize = 600;
23150 split.animate = true;
23151 split.on('moved', splitterMoved);
23152 </code></pre>
23153  * @constructor
23154  * Create a new SplitBar
23155  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23156  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23157  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23158  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23159                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23160                         position of the SplitBar).
23161  */
23162 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23163     
23164     /** @private */
23165     this.el = Roo.get(dragElement, true);
23166     this.el.dom.unselectable = "on";
23167     /** @private */
23168     this.resizingEl = Roo.get(resizingElement, true);
23169
23170     /**
23171      * @private
23172      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23173      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23174      * @type Number
23175      */
23176     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23177     
23178     /**
23179      * The minimum size of the resizing element. (Defaults to 0)
23180      * @type Number
23181      */
23182     this.minSize = 0;
23183     
23184     /**
23185      * The maximum size of the resizing element. (Defaults to 2000)
23186      * @type Number
23187      */
23188     this.maxSize = 2000;
23189     
23190     /**
23191      * Whether to animate the transition to the new size
23192      * @type Boolean
23193      */
23194     this.animate = false;
23195     
23196     /**
23197      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23198      * @type Boolean
23199      */
23200     this.useShim = false;
23201     
23202     /** @private */
23203     this.shim = null;
23204     
23205     if(!existingProxy){
23206         /** @private */
23207         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23208     }else{
23209         this.proxy = Roo.get(existingProxy).dom;
23210     }
23211     /** @private */
23212     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23213     
23214     /** @private */
23215     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23216     
23217     /** @private */
23218     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23219     
23220     /** @private */
23221     this.dragSpecs = {};
23222     
23223     /**
23224      * @private The adapter to use to positon and resize elements
23225      */
23226     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23227     this.adapter.init(this);
23228     
23229     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23230         /** @private */
23231         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23232         this.el.addClass("x-splitbar-h");
23233     }else{
23234         /** @private */
23235         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23236         this.el.addClass("x-splitbar-v");
23237     }
23238     
23239     this.addEvents({
23240         /**
23241          * @event resize
23242          * Fires when the splitter is moved (alias for {@link #event-moved})
23243          * @param {Roo.SplitBar} this
23244          * @param {Number} newSize the new width or height
23245          */
23246         "resize" : true,
23247         /**
23248          * @event moved
23249          * Fires when the splitter is moved
23250          * @param {Roo.SplitBar} this
23251          * @param {Number} newSize the new width or height
23252          */
23253         "moved" : true,
23254         /**
23255          * @event beforeresize
23256          * Fires before the splitter is dragged
23257          * @param {Roo.SplitBar} this
23258          */
23259         "beforeresize" : true,
23260
23261         "beforeapply" : true
23262     });
23263
23264     Roo.util.Observable.call(this);
23265 };
23266
23267 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23268     onStartProxyDrag : function(x, y){
23269         this.fireEvent("beforeresize", this);
23270         if(!this.overlay){
23271             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23272             o.unselectable();
23273             o.enableDisplayMode("block");
23274             // all splitbars share the same overlay
23275             Roo.SplitBar.prototype.overlay = o;
23276         }
23277         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23278         this.overlay.show();
23279         Roo.get(this.proxy).setDisplayed("block");
23280         var size = this.adapter.getElementSize(this);
23281         this.activeMinSize = this.getMinimumSize();;
23282         this.activeMaxSize = this.getMaximumSize();;
23283         var c1 = size - this.activeMinSize;
23284         var c2 = Math.max(this.activeMaxSize - size, 0);
23285         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23286             this.dd.resetConstraints();
23287             this.dd.setXConstraint(
23288                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23289                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23290             );
23291             this.dd.setYConstraint(0, 0);
23292         }else{
23293             this.dd.resetConstraints();
23294             this.dd.setXConstraint(0, 0);
23295             this.dd.setYConstraint(
23296                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23297                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23298             );
23299          }
23300         this.dragSpecs.startSize = size;
23301         this.dragSpecs.startPoint = [x, y];
23302         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23303     },
23304     
23305     /** 
23306      * @private Called after the drag operation by the DDProxy
23307      */
23308     onEndProxyDrag : function(e){
23309         Roo.get(this.proxy).setDisplayed(false);
23310         var endPoint = Roo.lib.Event.getXY(e);
23311         if(this.overlay){
23312             this.overlay.hide();
23313         }
23314         var newSize;
23315         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23316             newSize = this.dragSpecs.startSize + 
23317                 (this.placement == Roo.SplitBar.LEFT ?
23318                     endPoint[0] - this.dragSpecs.startPoint[0] :
23319                     this.dragSpecs.startPoint[0] - endPoint[0]
23320                 );
23321         }else{
23322             newSize = this.dragSpecs.startSize + 
23323                 (this.placement == Roo.SplitBar.TOP ?
23324                     endPoint[1] - this.dragSpecs.startPoint[1] :
23325                     this.dragSpecs.startPoint[1] - endPoint[1]
23326                 );
23327         }
23328         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23329         if(newSize != this.dragSpecs.startSize){
23330             if(this.fireEvent('beforeapply', this, newSize) !== false){
23331                 this.adapter.setElementSize(this, newSize);
23332                 this.fireEvent("moved", this, newSize);
23333                 this.fireEvent("resize", this, newSize);
23334             }
23335         }
23336     },
23337     
23338     /**
23339      * Get the adapter this SplitBar uses
23340      * @return The adapter object
23341      */
23342     getAdapter : function(){
23343         return this.adapter;
23344     },
23345     
23346     /**
23347      * Set the adapter this SplitBar uses
23348      * @param {Object} adapter A SplitBar adapter object
23349      */
23350     setAdapter : function(adapter){
23351         this.adapter = adapter;
23352         this.adapter.init(this);
23353     },
23354     
23355     /**
23356      * Gets the minimum size for the resizing element
23357      * @return {Number} The minimum size
23358      */
23359     getMinimumSize : function(){
23360         return this.minSize;
23361     },
23362     
23363     /**
23364      * Sets the minimum size for the resizing element
23365      * @param {Number} minSize The minimum size
23366      */
23367     setMinimumSize : function(minSize){
23368         this.minSize = minSize;
23369     },
23370     
23371     /**
23372      * Gets the maximum size for the resizing element
23373      * @return {Number} The maximum size
23374      */
23375     getMaximumSize : function(){
23376         return this.maxSize;
23377     },
23378     
23379     /**
23380      * Sets the maximum size for the resizing element
23381      * @param {Number} maxSize The maximum size
23382      */
23383     setMaximumSize : function(maxSize){
23384         this.maxSize = maxSize;
23385     },
23386     
23387     /**
23388      * Sets the initialize size for the resizing element
23389      * @param {Number} size The initial size
23390      */
23391     setCurrentSize : function(size){
23392         var oldAnimate = this.animate;
23393         this.animate = false;
23394         this.adapter.setElementSize(this, size);
23395         this.animate = oldAnimate;
23396     },
23397     
23398     /**
23399      * Destroy this splitbar. 
23400      * @param {Boolean} removeEl True to remove the element
23401      */
23402     destroy : function(removeEl){
23403         if(this.shim){
23404             this.shim.remove();
23405         }
23406         this.dd.unreg();
23407         this.proxy.parentNode.removeChild(this.proxy);
23408         if(removeEl){
23409             this.el.remove();
23410         }
23411     }
23412 });
23413
23414 /**
23415  * @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.
23416  */
23417 Roo.SplitBar.createProxy = function(dir){
23418     var proxy = new Roo.Element(document.createElement("div"));
23419     proxy.unselectable();
23420     var cls = 'x-splitbar-proxy';
23421     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23422     document.body.appendChild(proxy.dom);
23423     return proxy.dom;
23424 };
23425
23426 /** 
23427  * @class Roo.SplitBar.BasicLayoutAdapter
23428  * Default Adapter. It assumes the splitter and resizing element are not positioned
23429  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23430  */
23431 Roo.SplitBar.BasicLayoutAdapter = function(){
23432 };
23433
23434 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23435     // do nothing for now
23436     init : function(s){
23437     
23438     },
23439     /**
23440      * Called before drag operations to get the current size of the resizing element. 
23441      * @param {Roo.SplitBar} s The SplitBar using this adapter
23442      */
23443      getElementSize : function(s){
23444         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23445             return s.resizingEl.getWidth();
23446         }else{
23447             return s.resizingEl.getHeight();
23448         }
23449     },
23450     
23451     /**
23452      * Called after drag operations to set the size of the resizing element.
23453      * @param {Roo.SplitBar} s The SplitBar using this adapter
23454      * @param {Number} newSize The new size to set
23455      * @param {Function} onComplete A function to be invoked when resizing is complete
23456      */
23457     setElementSize : function(s, newSize, onComplete){
23458         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23459             if(!s.animate){
23460                 s.resizingEl.setWidth(newSize);
23461                 if(onComplete){
23462                     onComplete(s, newSize);
23463                 }
23464             }else{
23465                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23466             }
23467         }else{
23468             
23469             if(!s.animate){
23470                 s.resizingEl.setHeight(newSize);
23471                 if(onComplete){
23472                     onComplete(s, newSize);
23473                 }
23474             }else{
23475                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23476             }
23477         }
23478     }
23479 };
23480
23481 /** 
23482  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23483  * @extends Roo.SplitBar.BasicLayoutAdapter
23484  * Adapter that  moves the splitter element to align with the resized sizing element. 
23485  * Used with an absolute positioned SplitBar.
23486  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23487  * document.body, make sure you assign an id to the body element.
23488  */
23489 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23490     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23491     this.container = Roo.get(container);
23492 };
23493
23494 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23495     init : function(s){
23496         this.basic.init(s);
23497     },
23498     
23499     getElementSize : function(s){
23500         return this.basic.getElementSize(s);
23501     },
23502     
23503     setElementSize : function(s, newSize, onComplete){
23504         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23505     },
23506     
23507     moveSplitter : function(s){
23508         var yes = Roo.SplitBar;
23509         switch(s.placement){
23510             case yes.LEFT:
23511                 s.el.setX(s.resizingEl.getRight());
23512                 break;
23513             case yes.RIGHT:
23514                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23515                 break;
23516             case yes.TOP:
23517                 s.el.setY(s.resizingEl.getBottom());
23518                 break;
23519             case yes.BOTTOM:
23520                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23521                 break;
23522         }
23523     }
23524 };
23525
23526 /**
23527  * Orientation constant - Create a vertical SplitBar
23528  * @static
23529  * @type Number
23530  */
23531 Roo.SplitBar.VERTICAL = 1;
23532
23533 /**
23534  * Orientation constant - Create a horizontal SplitBar
23535  * @static
23536  * @type Number
23537  */
23538 Roo.SplitBar.HORIZONTAL = 2;
23539
23540 /**
23541  * Placement constant - The resizing element is to the left of the splitter element
23542  * @static
23543  * @type Number
23544  */
23545 Roo.SplitBar.LEFT = 1;
23546
23547 /**
23548  * Placement constant - The resizing element is to the right of the splitter element
23549  * @static
23550  * @type Number
23551  */
23552 Roo.SplitBar.RIGHT = 2;
23553
23554 /**
23555  * Placement constant - The resizing element is positioned above the splitter element
23556  * @static
23557  * @type Number
23558  */
23559 Roo.SplitBar.TOP = 3;
23560
23561 /**
23562  * Placement constant - The resizing element is positioned under splitter element
23563  * @static
23564  * @type Number
23565  */
23566 Roo.SplitBar.BOTTOM = 4;
23567 /*
23568  * Based on:
23569  * Ext JS Library 1.1.1
23570  * Copyright(c) 2006-2007, Ext JS, LLC.
23571  *
23572  * Originally Released Under LGPL - original licence link has changed is not relivant.
23573  *
23574  * Fork - LGPL
23575  * <script type="text/javascript">
23576  */
23577
23578 /**
23579  * @class Roo.View
23580  * @extends Roo.util.Observable
23581  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23582  * This class also supports single and multi selection modes. <br>
23583  * Create a data model bound view:
23584  <pre><code>
23585  var store = new Roo.data.Store(...);
23586
23587  var view = new Roo.View({
23588     el : "my-element",
23589     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23590  
23591     singleSelect: true,
23592     selectedClass: "ydataview-selected",
23593     store: store
23594  });
23595
23596  // listen for node click?
23597  view.on("click", function(vw, index, node, e){
23598  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23599  });
23600
23601  // load XML data
23602  dataModel.load("foobar.xml");
23603  </code></pre>
23604  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23605  * <br><br>
23606  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23607  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23608  * 
23609  * Note: old style constructor is still suported (container, template, config)
23610  * 
23611  * @constructor
23612  * Create a new View
23613  * @param {Object} config The config object
23614  * 
23615  */
23616 Roo.View = function(config, depreciated_tpl, depreciated_config){
23617     
23618     if (typeof(depreciated_tpl) == 'undefined') {
23619         // new way.. - universal constructor.
23620         Roo.apply(this, config);
23621         this.el  = Roo.get(this.el);
23622     } else {
23623         // old format..
23624         this.el  = Roo.get(config);
23625         this.tpl = depreciated_tpl;
23626         Roo.apply(this, depreciated_config);
23627     }
23628      
23629     
23630     if(typeof(this.tpl) == "string"){
23631         this.tpl = new Roo.Template(this.tpl);
23632     } else {
23633         // support xtype ctors..
23634         this.tpl = new Roo.factory(this.tpl, Roo);
23635     }
23636     
23637     
23638     this.tpl.compile();
23639    
23640
23641      
23642     /** @private */
23643     this.addEvents({
23644         /**
23645          * @event beforeclick
23646          * Fires before a click is processed. Returns false to cancel the default action.
23647          * @param {Roo.View} this
23648          * @param {Number} index The index of the target node
23649          * @param {HTMLElement} node The target node
23650          * @param {Roo.EventObject} e The raw event object
23651          */
23652             "beforeclick" : true,
23653         /**
23654          * @event click
23655          * Fires when a template node is clicked.
23656          * @param {Roo.View} this
23657          * @param {Number} index The index of the target node
23658          * @param {HTMLElement} node The target node
23659          * @param {Roo.EventObject} e The raw event object
23660          */
23661             "click" : true,
23662         /**
23663          * @event dblclick
23664          * Fires when a template node is double clicked.
23665          * @param {Roo.View} this
23666          * @param {Number} index The index of the target node
23667          * @param {HTMLElement} node The target node
23668          * @param {Roo.EventObject} e The raw event object
23669          */
23670             "dblclick" : true,
23671         /**
23672          * @event contextmenu
23673          * Fires when a template node is right clicked.
23674          * @param {Roo.View} this
23675          * @param {Number} index The index of the target node
23676          * @param {HTMLElement} node The target node
23677          * @param {Roo.EventObject} e The raw event object
23678          */
23679             "contextmenu" : true,
23680         /**
23681          * @event selectionchange
23682          * Fires when the selected nodes change.
23683          * @param {Roo.View} this
23684          * @param {Array} selections Array of the selected nodes
23685          */
23686             "selectionchange" : true,
23687     
23688         /**
23689          * @event beforeselect
23690          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23691          * @param {Roo.View} this
23692          * @param {HTMLElement} node The node to be selected
23693          * @param {Array} selections Array of currently selected nodes
23694          */
23695             "beforeselect" : true,
23696         /**
23697          * @event preparedata
23698          * Fires on every row to render, to allow you to change the data.
23699          * @param {Roo.View} this
23700          * @param {Object} data to be rendered (change this)
23701          */
23702           "preparedata" : true
23703         });
23704
23705     this.el.on({
23706         "click": this.onClick,
23707         "dblclick": this.onDblClick,
23708         "contextmenu": this.onContextMenu,
23709         scope:this
23710     });
23711
23712     this.selections = [];
23713     this.nodes = [];
23714     this.cmp = new Roo.CompositeElementLite([]);
23715     if(this.store){
23716         this.store = Roo.factory(this.store, Roo.data);
23717         this.setStore(this.store, true);
23718     }
23719     Roo.View.superclass.constructor.call(this);
23720 };
23721
23722 Roo.extend(Roo.View, Roo.util.Observable, {
23723     
23724      /**
23725      * @cfg {Roo.data.Store} store Data store to load data from.
23726      */
23727     store : false,
23728     
23729     /**
23730      * @cfg {String|Roo.Element} el The container element.
23731      */
23732     el : '',
23733     
23734     /**
23735      * @cfg {String|Roo.Template} tpl The template used by this View 
23736      */
23737     tpl : false,
23738     
23739     /**
23740      * @cfg {String} selectedClass The css class to add to selected nodes
23741      */
23742     selectedClass : "x-view-selected",
23743      /**
23744      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23745      */
23746     emptyText : "",
23747     /**
23748      * @cfg {Boolean} multiSelect Allow multiple selection
23749      */
23750     multiSelect : false,
23751     /**
23752      * @cfg {Boolean} singleSelect Allow single selection
23753      */
23754     singleSelect:  false,
23755     
23756     /**
23757      * @cfg {Boolean} toggleSelect - selecting 
23758      */
23759     toggleSelect : false,
23760     
23761     /**
23762      * Returns the element this view is bound to.
23763      * @return {Roo.Element}
23764      */
23765     getEl : function(){
23766         return this.el;
23767     },
23768
23769     /**
23770      * Refreshes the view.
23771      */
23772     refresh : function(){
23773         var t = this.tpl;
23774         this.clearSelections();
23775         this.el.update("");
23776         var html = [];
23777         var records = this.store.getRange();
23778         if(records.length < 1){
23779             this.el.update(this.emptyText);
23780             return;
23781         }
23782         for(var i = 0, len = records.length; i < len; i++){
23783             var data = this.prepareData(records[i].data, i, records[i]);
23784             this.fireEvent("preparedata", this, data, i, records[i]);
23785             html[html.length] = t.apply(data);
23786         }
23787         this.el.update(html.join(""));
23788         this.nodes = this.el.dom.childNodes;
23789         this.updateIndexes(0);
23790     },
23791
23792     /**
23793      * Function to override to reformat the data that is sent to
23794      * the template for each node.
23795      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23796      * a JSON object for an UpdateManager bound view).
23797      */
23798     prepareData : function(data){
23799         return data;
23800     },
23801
23802     onUpdate : function(ds, record){
23803         this.clearSelections();
23804         var index = this.store.indexOf(record);
23805         var n = this.nodes[index];
23806         this.tpl.insertBefore(n, this.prepareData(record.data));
23807         n.parentNode.removeChild(n);
23808         this.updateIndexes(index, index);
23809     },
23810
23811     onAdd : function(ds, records, index){
23812         this.clearSelections();
23813         if(this.nodes.length == 0){
23814             this.refresh();
23815             return;
23816         }
23817         var n = this.nodes[index];
23818         for(var i = 0, len = records.length; i < len; i++){
23819             var d = this.prepareData(records[i].data);
23820             if(n){
23821                 this.tpl.insertBefore(n, d);
23822             }else{
23823                 this.tpl.append(this.el, d);
23824             }
23825         }
23826         this.updateIndexes(index);
23827     },
23828
23829     onRemove : function(ds, record, index){
23830         this.clearSelections();
23831         this.el.dom.removeChild(this.nodes[index]);
23832         this.updateIndexes(index);
23833     },
23834
23835     /**
23836      * Refresh an individual node.
23837      * @param {Number} index
23838      */
23839     refreshNode : function(index){
23840         this.onUpdate(this.store, this.store.getAt(index));
23841     },
23842
23843     updateIndexes : function(startIndex, endIndex){
23844         var ns = this.nodes;
23845         startIndex = startIndex || 0;
23846         endIndex = endIndex || ns.length - 1;
23847         for(var i = startIndex; i <= endIndex; i++){
23848             ns[i].nodeIndex = i;
23849         }
23850     },
23851
23852     /**
23853      * Changes the data store this view uses and refresh the view.
23854      * @param {Store} store
23855      */
23856     setStore : function(store, initial){
23857         if(!initial && this.store){
23858             this.store.un("datachanged", this.refresh);
23859             this.store.un("add", this.onAdd);
23860             this.store.un("remove", this.onRemove);
23861             this.store.un("update", this.onUpdate);
23862             this.store.un("clear", this.refresh);
23863         }
23864         if(store){
23865           
23866             store.on("datachanged", this.refresh, this);
23867             store.on("add", this.onAdd, this);
23868             store.on("remove", this.onRemove, this);
23869             store.on("update", this.onUpdate, this);
23870             store.on("clear", this.refresh, this);
23871         }
23872         
23873         if(store){
23874             this.refresh();
23875         }
23876     },
23877
23878     /**
23879      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23880      * @param {HTMLElement} node
23881      * @return {HTMLElement} The template node
23882      */
23883     findItemFromChild : function(node){
23884         var el = this.el.dom;
23885         if(!node || node.parentNode == el){
23886                     return node;
23887             }
23888             var p = node.parentNode;
23889             while(p && p != el){
23890             if(p.parentNode == el){
23891                 return p;
23892             }
23893             p = p.parentNode;
23894         }
23895             return null;
23896     },
23897
23898     /** @ignore */
23899     onClick : function(e){
23900         var item = this.findItemFromChild(e.getTarget());
23901         if(item){
23902             var index = this.indexOf(item);
23903             if(this.onItemClick(item, index, e) !== false){
23904                 this.fireEvent("click", this, index, item, e);
23905             }
23906         }else{
23907             this.clearSelections();
23908         }
23909     },
23910
23911     /** @ignore */
23912     onContextMenu : function(e){
23913         var item = this.findItemFromChild(e.getTarget());
23914         if(item){
23915             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23916         }
23917     },
23918
23919     /** @ignore */
23920     onDblClick : function(e){
23921         var item = this.findItemFromChild(e.getTarget());
23922         if(item){
23923             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23924         }
23925     },
23926
23927     onItemClick : function(item, index, e)
23928     {
23929         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23930             return false;
23931         }
23932         if (this.toggleSelect) {
23933             var m = this.isSelected(item) ? 'unselect' : 'select';
23934             Roo.log(m);
23935             var _t = this;
23936             _t[m](item, true, false);
23937             return true;
23938         }
23939         if(this.multiSelect || this.singleSelect){
23940             if(this.multiSelect && e.shiftKey && this.lastSelection){
23941                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23942             }else{
23943                 this.select(item, this.multiSelect && e.ctrlKey);
23944                 this.lastSelection = item;
23945             }
23946             e.preventDefault();
23947         }
23948         return true;
23949     },
23950
23951     /**
23952      * Get the number of selected nodes.
23953      * @return {Number}
23954      */
23955     getSelectionCount : function(){
23956         return this.selections.length;
23957     },
23958
23959     /**
23960      * Get the currently selected nodes.
23961      * @return {Array} An array of HTMLElements
23962      */
23963     getSelectedNodes : function(){
23964         return this.selections;
23965     },
23966
23967     /**
23968      * Get the indexes of the selected nodes.
23969      * @return {Array}
23970      */
23971     getSelectedIndexes : function(){
23972         var indexes = [], s = this.selections;
23973         for(var i = 0, len = s.length; i < len; i++){
23974             indexes.push(s[i].nodeIndex);
23975         }
23976         return indexes;
23977     },
23978
23979     /**
23980      * Clear all selections
23981      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23982      */
23983     clearSelections : function(suppressEvent){
23984         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23985             this.cmp.elements = this.selections;
23986             this.cmp.removeClass(this.selectedClass);
23987             this.selections = [];
23988             if(!suppressEvent){
23989                 this.fireEvent("selectionchange", this, this.selections);
23990             }
23991         }
23992     },
23993
23994     /**
23995      * Returns true if the passed node is selected
23996      * @param {HTMLElement/Number} node The node or node index
23997      * @return {Boolean}
23998      */
23999     isSelected : function(node){
24000         var s = this.selections;
24001         if(s.length < 1){
24002             return false;
24003         }
24004         node = this.getNode(node);
24005         return s.indexOf(node) !== -1;
24006     },
24007
24008     /**
24009      * Selects nodes.
24010      * @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
24011      * @param {Boolean} keepExisting (optional) true to keep existing selections
24012      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24013      */
24014     select : function(nodeInfo, keepExisting, suppressEvent){
24015         if(nodeInfo instanceof Array){
24016             if(!keepExisting){
24017                 this.clearSelections(true);
24018             }
24019             for(var i = 0, len = nodeInfo.length; i < len; i++){
24020                 this.select(nodeInfo[i], true, true);
24021             }
24022             return;
24023         } 
24024         var node = this.getNode(nodeInfo);
24025         if(!node || this.isSelected(node)){
24026             return; // already selected.
24027         }
24028         if(!keepExisting){
24029             this.clearSelections(true);
24030         }
24031         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24032             Roo.fly(node).addClass(this.selectedClass);
24033             this.selections.push(node);
24034             if(!suppressEvent){
24035                 this.fireEvent("selectionchange", this, this.selections);
24036             }
24037         }
24038         
24039         
24040     },
24041       /**
24042      * Unselects nodes.
24043      * @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
24044      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24045      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24046      */
24047     unselect : function(nodeInfo, keepExisting, suppressEvent)
24048     {
24049         if(nodeInfo instanceof Array){
24050             Roo.each(this.selections, function(s) {
24051                 this.unselect(s, nodeInfo);
24052             }, this);
24053             return;
24054         }
24055         var node = this.getNode(nodeInfo);
24056         if(!node || !this.isSelected(node)){
24057             Roo.log("not selected");
24058             return; // not selected.
24059         }
24060         // fireevent???
24061         var ns = [];
24062         Roo.each(this.selections, function(s) {
24063             if (s == node ) {
24064                 Roo.fly(node).removeClass(this.selectedClass);
24065
24066                 return;
24067             }
24068             ns.push(s);
24069         },this);
24070         
24071         this.selections= ns;
24072         this.fireEvent("selectionchange", this, this.selections);
24073     },
24074
24075     /**
24076      * Gets a template node.
24077      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24078      * @return {HTMLElement} The node or null if it wasn't found
24079      */
24080     getNode : function(nodeInfo){
24081         if(typeof nodeInfo == "string"){
24082             return document.getElementById(nodeInfo);
24083         }else if(typeof nodeInfo == "number"){
24084             return this.nodes[nodeInfo];
24085         }
24086         return nodeInfo;
24087     },
24088
24089     /**
24090      * Gets a range template nodes.
24091      * @param {Number} startIndex
24092      * @param {Number} endIndex
24093      * @return {Array} An array of nodes
24094      */
24095     getNodes : function(start, end){
24096         var ns = this.nodes;
24097         start = start || 0;
24098         end = typeof end == "undefined" ? ns.length - 1 : end;
24099         var nodes = [];
24100         if(start <= end){
24101             for(var i = start; i <= end; i++){
24102                 nodes.push(ns[i]);
24103             }
24104         } else{
24105             for(var i = start; i >= end; i--){
24106                 nodes.push(ns[i]);
24107             }
24108         }
24109         return nodes;
24110     },
24111
24112     /**
24113      * Finds the index of the passed node
24114      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24115      * @return {Number} The index of the node or -1
24116      */
24117     indexOf : function(node){
24118         node = this.getNode(node);
24119         if(typeof node.nodeIndex == "number"){
24120             return node.nodeIndex;
24121         }
24122         var ns = this.nodes;
24123         for(var i = 0, len = ns.length; i < len; i++){
24124             if(ns[i] == node){
24125                 return i;
24126             }
24127         }
24128         return -1;
24129     }
24130 });
24131 /*
24132  * Based on:
24133  * Ext JS Library 1.1.1
24134  * Copyright(c) 2006-2007, Ext JS, LLC.
24135  *
24136  * Originally Released Under LGPL - original licence link has changed is not relivant.
24137  *
24138  * Fork - LGPL
24139  * <script type="text/javascript">
24140  */
24141
24142 /**
24143  * @class Roo.JsonView
24144  * @extends Roo.View
24145  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24146 <pre><code>
24147 var view = new Roo.JsonView({
24148     container: "my-element",
24149     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24150     multiSelect: true, 
24151     jsonRoot: "data" 
24152 });
24153
24154 // listen for node click?
24155 view.on("click", function(vw, index, node, e){
24156     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24157 });
24158
24159 // direct load of JSON data
24160 view.load("foobar.php");
24161
24162 // Example from my blog list
24163 var tpl = new Roo.Template(
24164     '&lt;div class="entry"&gt;' +
24165     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24166     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24167     "&lt;/div&gt;&lt;hr /&gt;"
24168 );
24169
24170 var moreView = new Roo.JsonView({
24171     container :  "entry-list", 
24172     template : tpl,
24173     jsonRoot: "posts"
24174 });
24175 moreView.on("beforerender", this.sortEntries, this);
24176 moreView.load({
24177     url: "/blog/get-posts.php",
24178     params: "allposts=true",
24179     text: "Loading Blog Entries..."
24180 });
24181 </code></pre>
24182
24183 * Note: old code is supported with arguments : (container, template, config)
24184
24185
24186  * @constructor
24187  * Create a new JsonView
24188  * 
24189  * @param {Object} config The config object
24190  * 
24191  */
24192 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24193     
24194     
24195     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24196
24197     var um = this.el.getUpdateManager();
24198     um.setRenderer(this);
24199     um.on("update", this.onLoad, this);
24200     um.on("failure", this.onLoadException, this);
24201
24202     /**
24203      * @event beforerender
24204      * Fires before rendering of the downloaded JSON data.
24205      * @param {Roo.JsonView} this
24206      * @param {Object} data The JSON data loaded
24207      */
24208     /**
24209      * @event load
24210      * Fires when data is loaded.
24211      * @param {Roo.JsonView} this
24212      * @param {Object} data The JSON data loaded
24213      * @param {Object} response The raw Connect response object
24214      */
24215     /**
24216      * @event loadexception
24217      * Fires when loading fails.
24218      * @param {Roo.JsonView} this
24219      * @param {Object} response The raw Connect response object
24220      */
24221     this.addEvents({
24222         'beforerender' : true,
24223         'load' : true,
24224         'loadexception' : true
24225     });
24226 };
24227 Roo.extend(Roo.JsonView, Roo.View, {
24228     /**
24229      * @type {String} The root property in the loaded JSON object that contains the data
24230      */
24231     jsonRoot : "",
24232
24233     /**
24234      * Refreshes the view.
24235      */
24236     refresh : function(){
24237         this.clearSelections();
24238         this.el.update("");
24239         var html = [];
24240         var o = this.jsonData;
24241         if(o && o.length > 0){
24242             for(var i = 0, len = o.length; i < len; i++){
24243                 var data = this.prepareData(o[i], i, o);
24244                 html[html.length] = this.tpl.apply(data);
24245             }
24246         }else{
24247             html.push(this.emptyText);
24248         }
24249         this.el.update(html.join(""));
24250         this.nodes = this.el.dom.childNodes;
24251         this.updateIndexes(0);
24252     },
24253
24254     /**
24255      * 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.
24256      * @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:
24257      <pre><code>
24258      view.load({
24259          url: "your-url.php",
24260          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24261          callback: yourFunction,
24262          scope: yourObject, //(optional scope)
24263          discardUrl: false,
24264          nocache: false,
24265          text: "Loading...",
24266          timeout: 30,
24267          scripts: false
24268      });
24269      </code></pre>
24270      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24271      * 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.
24272      * @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}
24273      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24274      * @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.
24275      */
24276     load : function(){
24277         var um = this.el.getUpdateManager();
24278         um.update.apply(um, arguments);
24279     },
24280
24281     render : function(el, response){
24282         this.clearSelections();
24283         this.el.update("");
24284         var o;
24285         try{
24286             o = Roo.util.JSON.decode(response.responseText);
24287             if(this.jsonRoot){
24288                 
24289                 o = o[this.jsonRoot];
24290             }
24291         } catch(e){
24292         }
24293         /**
24294          * The current JSON data or null
24295          */
24296         this.jsonData = o;
24297         this.beforeRender();
24298         this.refresh();
24299     },
24300
24301 /**
24302  * Get the number of records in the current JSON dataset
24303  * @return {Number}
24304  */
24305     getCount : function(){
24306         return this.jsonData ? this.jsonData.length : 0;
24307     },
24308
24309 /**
24310  * Returns the JSON object for the specified node(s)
24311  * @param {HTMLElement/Array} node The node or an array of nodes
24312  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24313  * you get the JSON object for the node
24314  */
24315     getNodeData : function(node){
24316         if(node instanceof Array){
24317             var data = [];
24318             for(var i = 0, len = node.length; i < len; i++){
24319                 data.push(this.getNodeData(node[i]));
24320             }
24321             return data;
24322         }
24323         return this.jsonData[this.indexOf(node)] || null;
24324     },
24325
24326     beforeRender : function(){
24327         this.snapshot = this.jsonData;
24328         if(this.sortInfo){
24329             this.sort.apply(this, this.sortInfo);
24330         }
24331         this.fireEvent("beforerender", this, this.jsonData);
24332     },
24333
24334     onLoad : function(el, o){
24335         this.fireEvent("load", this, this.jsonData, o);
24336     },
24337
24338     onLoadException : function(el, o){
24339         this.fireEvent("loadexception", this, o);
24340     },
24341
24342 /**
24343  * Filter the data by a specific property.
24344  * @param {String} property A property on your JSON objects
24345  * @param {String/RegExp} value Either string that the property values
24346  * should start with, or a RegExp to test against the property
24347  */
24348     filter : function(property, value){
24349         if(this.jsonData){
24350             var data = [];
24351             var ss = this.snapshot;
24352             if(typeof value == "string"){
24353                 var vlen = value.length;
24354                 if(vlen == 0){
24355                     this.clearFilter();
24356                     return;
24357                 }
24358                 value = value.toLowerCase();
24359                 for(var i = 0, len = ss.length; i < len; i++){
24360                     var o = ss[i];
24361                     if(o[property].substr(0, vlen).toLowerCase() == value){
24362                         data.push(o);
24363                     }
24364                 }
24365             } else if(value.exec){ // regex?
24366                 for(var i = 0, len = ss.length; i < len; i++){
24367                     var o = ss[i];
24368                     if(value.test(o[property])){
24369                         data.push(o);
24370                     }
24371                 }
24372             } else{
24373                 return;
24374             }
24375             this.jsonData = data;
24376             this.refresh();
24377         }
24378     },
24379
24380 /**
24381  * Filter by a function. The passed function will be called with each
24382  * object in the current dataset. If the function returns true the value is kept,
24383  * otherwise it is filtered.
24384  * @param {Function} fn
24385  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24386  */
24387     filterBy : function(fn, scope){
24388         if(this.jsonData){
24389             var data = [];
24390             var ss = this.snapshot;
24391             for(var i = 0, len = ss.length; i < len; i++){
24392                 var o = ss[i];
24393                 if(fn.call(scope || this, o)){
24394                     data.push(o);
24395                 }
24396             }
24397             this.jsonData = data;
24398             this.refresh();
24399         }
24400     },
24401
24402 /**
24403  * Clears the current filter.
24404  */
24405     clearFilter : function(){
24406         if(this.snapshot && this.jsonData != this.snapshot){
24407             this.jsonData = this.snapshot;
24408             this.refresh();
24409         }
24410     },
24411
24412
24413 /**
24414  * Sorts the data for this view and refreshes it.
24415  * @param {String} property A property on your JSON objects to sort on
24416  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24417  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24418  */
24419     sort : function(property, dir, sortType){
24420         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24421         if(this.jsonData){
24422             var p = property;
24423             var dsc = dir && dir.toLowerCase() == "desc";
24424             var f = function(o1, o2){
24425                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24426                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24427                 ;
24428                 if(v1 < v2){
24429                     return dsc ? +1 : -1;
24430                 } else if(v1 > v2){
24431                     return dsc ? -1 : +1;
24432                 } else{
24433                     return 0;
24434                 }
24435             };
24436             this.jsonData.sort(f);
24437             this.refresh();
24438             if(this.jsonData != this.snapshot){
24439                 this.snapshot.sort(f);
24440             }
24441         }
24442     }
24443 });/*
24444  * Based on:
24445  * Ext JS Library 1.1.1
24446  * Copyright(c) 2006-2007, Ext JS, LLC.
24447  *
24448  * Originally Released Under LGPL - original licence link has changed is not relivant.
24449  *
24450  * Fork - LGPL
24451  * <script type="text/javascript">
24452  */
24453  
24454
24455 /**
24456  * @class Roo.ColorPalette
24457  * @extends Roo.Component
24458  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24459  * Here's an example of typical usage:
24460  * <pre><code>
24461 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24462 cp.render('my-div');
24463
24464 cp.on('select', function(palette, selColor){
24465     // do something with selColor
24466 });
24467 </code></pre>
24468  * @constructor
24469  * Create a new ColorPalette
24470  * @param {Object} config The config object
24471  */
24472 Roo.ColorPalette = function(config){
24473     Roo.ColorPalette.superclass.constructor.call(this, config);
24474     this.addEvents({
24475         /**
24476              * @event select
24477              * Fires when a color is selected
24478              * @param {ColorPalette} this
24479              * @param {String} color The 6-digit color hex code (without the # symbol)
24480              */
24481         select: true
24482     });
24483
24484     if(this.handler){
24485         this.on("select", this.handler, this.scope, true);
24486     }
24487 };
24488 Roo.extend(Roo.ColorPalette, Roo.Component, {
24489     /**
24490      * @cfg {String} itemCls
24491      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24492      */
24493     itemCls : "x-color-palette",
24494     /**
24495      * @cfg {String} value
24496      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24497      * the hex codes are case-sensitive.
24498      */
24499     value : null,
24500     clickEvent:'click',
24501     // private
24502     ctype: "Roo.ColorPalette",
24503
24504     /**
24505      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24506      */
24507     allowReselect : false,
24508
24509     /**
24510      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24511      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24512      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24513      * of colors with the width setting until the box is symmetrical.</p>
24514      * <p>You can override individual colors if needed:</p>
24515      * <pre><code>
24516 var cp = new Roo.ColorPalette();
24517 cp.colors[0] = "FF0000";  // change the first box to red
24518 </code></pre>
24519
24520 Or you can provide a custom array of your own for complete control:
24521 <pre><code>
24522 var cp = new Roo.ColorPalette();
24523 cp.colors = ["000000", "993300", "333300"];
24524 </code></pre>
24525      * @type Array
24526      */
24527     colors : [
24528         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24529         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24530         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24531         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24532         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24533     ],
24534
24535     // private
24536     onRender : function(container, position){
24537         var t = new Roo.MasterTemplate(
24538             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24539         );
24540         var c = this.colors;
24541         for(var i = 0, len = c.length; i < len; i++){
24542             t.add([c[i]]);
24543         }
24544         var el = document.createElement("div");
24545         el.className = this.itemCls;
24546         t.overwrite(el);
24547         container.dom.insertBefore(el, position);
24548         this.el = Roo.get(el);
24549         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24550         if(this.clickEvent != 'click'){
24551             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24552         }
24553     },
24554
24555     // private
24556     afterRender : function(){
24557         Roo.ColorPalette.superclass.afterRender.call(this);
24558         if(this.value){
24559             var s = this.value;
24560             this.value = null;
24561             this.select(s);
24562         }
24563     },
24564
24565     // private
24566     handleClick : function(e, t){
24567         e.preventDefault();
24568         if(!this.disabled){
24569             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24570             this.select(c.toUpperCase());
24571         }
24572     },
24573
24574     /**
24575      * Selects the specified color in the palette (fires the select event)
24576      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24577      */
24578     select : function(color){
24579         color = color.replace("#", "");
24580         if(color != this.value || this.allowReselect){
24581             var el = this.el;
24582             if(this.value){
24583                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24584             }
24585             el.child("a.color-"+color).addClass("x-color-palette-sel");
24586             this.value = color;
24587             this.fireEvent("select", this, color);
24588         }
24589     }
24590 });/*
24591  * Based on:
24592  * Ext JS Library 1.1.1
24593  * Copyright(c) 2006-2007, Ext JS, LLC.
24594  *
24595  * Originally Released Under LGPL - original licence link has changed is not relivant.
24596  *
24597  * Fork - LGPL
24598  * <script type="text/javascript">
24599  */
24600  
24601 /**
24602  * @class Roo.DatePicker
24603  * @extends Roo.Component
24604  * Simple date picker class.
24605  * @constructor
24606  * Create a new DatePicker
24607  * @param {Object} config The config object
24608  */
24609 Roo.DatePicker = function(config){
24610     Roo.DatePicker.superclass.constructor.call(this, config);
24611
24612     this.value = config && config.value ?
24613                  config.value.clearTime() : new Date().clearTime();
24614
24615     this.addEvents({
24616         /**
24617              * @event select
24618              * Fires when a date is selected
24619              * @param {DatePicker} this
24620              * @param {Date} date The selected date
24621              */
24622         'select': true,
24623         /**
24624              * @event monthchange
24625              * Fires when the displayed month changes 
24626              * @param {DatePicker} this
24627              * @param {Date} date The selected month
24628              */
24629         'monthchange': true
24630     });
24631
24632     if(this.handler){
24633         this.on("select", this.handler,  this.scope || this);
24634     }
24635     // build the disabledDatesRE
24636     if(!this.disabledDatesRE && this.disabledDates){
24637         var dd = this.disabledDates;
24638         var re = "(?:";
24639         for(var i = 0; i < dd.length; i++){
24640             re += dd[i];
24641             if(i != dd.length-1) re += "|";
24642         }
24643         this.disabledDatesRE = new RegExp(re + ")");
24644     }
24645 };
24646
24647 Roo.extend(Roo.DatePicker, Roo.Component, {
24648     /**
24649      * @cfg {String} todayText
24650      * The text to display on the button that selects the current date (defaults to "Today")
24651      */
24652     todayText : "Today",
24653     /**
24654      * @cfg {String} okText
24655      * The text to display on the ok button
24656      */
24657     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24658     /**
24659      * @cfg {String} cancelText
24660      * The text to display on the cancel button
24661      */
24662     cancelText : "Cancel",
24663     /**
24664      * @cfg {String} todayTip
24665      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24666      */
24667     todayTip : "{0} (Spacebar)",
24668     /**
24669      * @cfg {Date} minDate
24670      * Minimum allowable date (JavaScript date object, defaults to null)
24671      */
24672     minDate : null,
24673     /**
24674      * @cfg {Date} maxDate
24675      * Maximum allowable date (JavaScript date object, defaults to null)
24676      */
24677     maxDate : null,
24678     /**
24679      * @cfg {String} minText
24680      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24681      */
24682     minText : "This date is before the minimum date",
24683     /**
24684      * @cfg {String} maxText
24685      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24686      */
24687     maxText : "This date is after the maximum date",
24688     /**
24689      * @cfg {String} format
24690      * The default date format string which can be overriden for localization support.  The format must be
24691      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24692      */
24693     format : "m/d/y",
24694     /**
24695      * @cfg {Array} disabledDays
24696      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24697      */
24698     disabledDays : null,
24699     /**
24700      * @cfg {String} disabledDaysText
24701      * The tooltip to display when the date falls on a disabled day (defaults to "")
24702      */
24703     disabledDaysText : "",
24704     /**
24705      * @cfg {RegExp} disabledDatesRE
24706      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24707      */
24708     disabledDatesRE : null,
24709     /**
24710      * @cfg {String} disabledDatesText
24711      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24712      */
24713     disabledDatesText : "",
24714     /**
24715      * @cfg {Boolean} constrainToViewport
24716      * True to constrain the date picker to the viewport (defaults to true)
24717      */
24718     constrainToViewport : true,
24719     /**
24720      * @cfg {Array} monthNames
24721      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24722      */
24723     monthNames : Date.monthNames,
24724     /**
24725      * @cfg {Array} dayNames
24726      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24727      */
24728     dayNames : Date.dayNames,
24729     /**
24730      * @cfg {String} nextText
24731      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24732      */
24733     nextText: 'Next Month (Control+Right)',
24734     /**
24735      * @cfg {String} prevText
24736      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24737      */
24738     prevText: 'Previous Month (Control+Left)',
24739     /**
24740      * @cfg {String} monthYearText
24741      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24742      */
24743     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24744     /**
24745      * @cfg {Number} startDay
24746      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24747      */
24748     startDay : 0,
24749     /**
24750      * @cfg {Bool} showClear
24751      * Show a clear button (usefull for date form elements that can be blank.)
24752      */
24753     
24754     showClear: false,
24755     
24756     /**
24757      * Sets the value of the date field
24758      * @param {Date} value The date to set
24759      */
24760     setValue : function(value){
24761         var old = this.value;
24762         this.value = value.clearTime(true);
24763         if(this.el){
24764             this.update(this.value);
24765         }
24766     },
24767
24768     /**
24769      * Gets the current selected value of the date field
24770      * @return {Date} The selected date
24771      */
24772     getValue : function(){
24773         return this.value;
24774     },
24775
24776     // private
24777     focus : function(){
24778         if(this.el){
24779             this.update(this.activeDate);
24780         }
24781     },
24782
24783     // private
24784     onRender : function(container, position){
24785         var m = [
24786              '<table cellspacing="0">',
24787                 '<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>',
24788                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24789         var dn = this.dayNames;
24790         for(var i = 0; i < 7; i++){
24791             var d = this.startDay+i;
24792             if(d > 6){
24793                 d = d-7;
24794             }
24795             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24796         }
24797         m[m.length] = "</tr></thead><tbody><tr>";
24798         for(var i = 0; i < 42; i++) {
24799             if(i % 7 == 0 && i != 0){
24800                 m[m.length] = "</tr><tr>";
24801             }
24802             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24803         }
24804         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24805             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24806
24807         var el = document.createElement("div");
24808         el.className = "x-date-picker";
24809         el.innerHTML = m.join("");
24810
24811         container.dom.insertBefore(el, position);
24812
24813         this.el = Roo.get(el);
24814         this.eventEl = Roo.get(el.firstChild);
24815
24816         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24817             handler: this.showPrevMonth,
24818             scope: this,
24819             preventDefault:true,
24820             stopDefault:true
24821         });
24822
24823         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24824             handler: this.showNextMonth,
24825             scope: this,
24826             preventDefault:true,
24827             stopDefault:true
24828         });
24829
24830         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24831
24832         this.monthPicker = this.el.down('div.x-date-mp');
24833         this.monthPicker.enableDisplayMode('block');
24834         
24835         var kn = new Roo.KeyNav(this.eventEl, {
24836             "left" : function(e){
24837                 e.ctrlKey ?
24838                     this.showPrevMonth() :
24839                     this.update(this.activeDate.add("d", -1));
24840             },
24841
24842             "right" : function(e){
24843                 e.ctrlKey ?
24844                     this.showNextMonth() :
24845                     this.update(this.activeDate.add("d", 1));
24846             },
24847
24848             "up" : function(e){
24849                 e.ctrlKey ?
24850                     this.showNextYear() :
24851                     this.update(this.activeDate.add("d", -7));
24852             },
24853
24854             "down" : function(e){
24855                 e.ctrlKey ?
24856                     this.showPrevYear() :
24857                     this.update(this.activeDate.add("d", 7));
24858             },
24859
24860             "pageUp" : function(e){
24861                 this.showNextMonth();
24862             },
24863
24864             "pageDown" : function(e){
24865                 this.showPrevMonth();
24866             },
24867
24868             "enter" : function(e){
24869                 e.stopPropagation();
24870                 return true;
24871             },
24872
24873             scope : this
24874         });
24875
24876         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24877
24878         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24879
24880         this.el.unselectable();
24881         
24882         this.cells = this.el.select("table.x-date-inner tbody td");
24883         this.textNodes = this.el.query("table.x-date-inner tbody span");
24884
24885         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24886             text: "&#160;",
24887             tooltip: this.monthYearText
24888         });
24889
24890         this.mbtn.on('click', this.showMonthPicker, this);
24891         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24892
24893
24894         var today = (new Date()).dateFormat(this.format);
24895         
24896         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24897         if (this.showClear) {
24898             baseTb.add( new Roo.Toolbar.Fill());
24899         }
24900         baseTb.add({
24901             text: String.format(this.todayText, today),
24902             tooltip: String.format(this.todayTip, today),
24903             handler: this.selectToday,
24904             scope: this
24905         });
24906         
24907         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24908             
24909         //});
24910         if (this.showClear) {
24911             
24912             baseTb.add( new Roo.Toolbar.Fill());
24913             baseTb.add({
24914                 text: '&#160;',
24915                 cls: 'x-btn-icon x-btn-clear',
24916                 handler: function() {
24917                     //this.value = '';
24918                     this.fireEvent("select", this, '');
24919                 },
24920                 scope: this
24921             });
24922         }
24923         
24924         
24925         if(Roo.isIE){
24926             this.el.repaint();
24927         }
24928         this.update(this.value);
24929     },
24930
24931     createMonthPicker : function(){
24932         if(!this.monthPicker.dom.firstChild){
24933             var buf = ['<table border="0" cellspacing="0">'];
24934             for(var i = 0; i < 6; i++){
24935                 buf.push(
24936                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24937                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24938                     i == 0 ?
24939                     '<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>' :
24940                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24941                 );
24942             }
24943             buf.push(
24944                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24945                     this.okText,
24946                     '</button><button type="button" class="x-date-mp-cancel">',
24947                     this.cancelText,
24948                     '</button></td></tr>',
24949                 '</table>'
24950             );
24951             this.monthPicker.update(buf.join(''));
24952             this.monthPicker.on('click', this.onMonthClick, this);
24953             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24954
24955             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24956             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24957
24958             this.mpMonths.each(function(m, a, i){
24959                 i += 1;
24960                 if((i%2) == 0){
24961                     m.dom.xmonth = 5 + Math.round(i * .5);
24962                 }else{
24963                     m.dom.xmonth = Math.round((i-1) * .5);
24964                 }
24965             });
24966         }
24967     },
24968
24969     showMonthPicker : function(){
24970         this.createMonthPicker();
24971         var size = this.el.getSize();
24972         this.monthPicker.setSize(size);
24973         this.monthPicker.child('table').setSize(size);
24974
24975         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24976         this.updateMPMonth(this.mpSelMonth);
24977         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24978         this.updateMPYear(this.mpSelYear);
24979
24980         this.monthPicker.slideIn('t', {duration:.2});
24981     },
24982
24983     updateMPYear : function(y){
24984         this.mpyear = y;
24985         var ys = this.mpYears.elements;
24986         for(var i = 1; i <= 10; i++){
24987             var td = ys[i-1], y2;
24988             if((i%2) == 0){
24989                 y2 = y + Math.round(i * .5);
24990                 td.firstChild.innerHTML = y2;
24991                 td.xyear = y2;
24992             }else{
24993                 y2 = y - (5-Math.round(i * .5));
24994                 td.firstChild.innerHTML = y2;
24995                 td.xyear = y2;
24996             }
24997             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24998         }
24999     },
25000
25001     updateMPMonth : function(sm){
25002         this.mpMonths.each(function(m, a, i){
25003             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25004         });
25005     },
25006
25007     selectMPMonth: function(m){
25008         
25009     },
25010
25011     onMonthClick : function(e, t){
25012         e.stopEvent();
25013         var el = new Roo.Element(t), pn;
25014         if(el.is('button.x-date-mp-cancel')){
25015             this.hideMonthPicker();
25016         }
25017         else if(el.is('button.x-date-mp-ok')){
25018             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25019             this.hideMonthPicker();
25020         }
25021         else if(pn = el.up('td.x-date-mp-month', 2)){
25022             this.mpMonths.removeClass('x-date-mp-sel');
25023             pn.addClass('x-date-mp-sel');
25024             this.mpSelMonth = pn.dom.xmonth;
25025         }
25026         else if(pn = el.up('td.x-date-mp-year', 2)){
25027             this.mpYears.removeClass('x-date-mp-sel');
25028             pn.addClass('x-date-mp-sel');
25029             this.mpSelYear = pn.dom.xyear;
25030         }
25031         else if(el.is('a.x-date-mp-prev')){
25032             this.updateMPYear(this.mpyear-10);
25033         }
25034         else if(el.is('a.x-date-mp-next')){
25035             this.updateMPYear(this.mpyear+10);
25036         }
25037     },
25038
25039     onMonthDblClick : function(e, t){
25040         e.stopEvent();
25041         var el = new Roo.Element(t), pn;
25042         if(pn = el.up('td.x-date-mp-month', 2)){
25043             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25044             this.hideMonthPicker();
25045         }
25046         else if(pn = el.up('td.x-date-mp-year', 2)){
25047             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25048             this.hideMonthPicker();
25049         }
25050     },
25051
25052     hideMonthPicker : function(disableAnim){
25053         if(this.monthPicker){
25054             if(disableAnim === true){
25055                 this.monthPicker.hide();
25056             }else{
25057                 this.monthPicker.slideOut('t', {duration:.2});
25058             }
25059         }
25060     },
25061
25062     // private
25063     showPrevMonth : function(e){
25064         this.update(this.activeDate.add("mo", -1));
25065     },
25066
25067     // private
25068     showNextMonth : function(e){
25069         this.update(this.activeDate.add("mo", 1));
25070     },
25071
25072     // private
25073     showPrevYear : function(){
25074         this.update(this.activeDate.add("y", -1));
25075     },
25076
25077     // private
25078     showNextYear : function(){
25079         this.update(this.activeDate.add("y", 1));
25080     },
25081
25082     // private
25083     handleMouseWheel : function(e){
25084         var delta = e.getWheelDelta();
25085         if(delta > 0){
25086             this.showPrevMonth();
25087             e.stopEvent();
25088         } else if(delta < 0){
25089             this.showNextMonth();
25090             e.stopEvent();
25091         }
25092     },
25093
25094     // private
25095     handleDateClick : function(e, t){
25096         e.stopEvent();
25097         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25098             this.setValue(new Date(t.dateValue));
25099             this.fireEvent("select", this, this.value);
25100         }
25101     },
25102
25103     // private
25104     selectToday : function(){
25105         this.setValue(new Date().clearTime());
25106         this.fireEvent("select", this, this.value);
25107     },
25108
25109     // private
25110     update : function(date)
25111     {
25112         var vd = this.activeDate;
25113         this.activeDate = date;
25114         if(vd && this.el){
25115             var t = date.getTime();
25116             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25117                 this.cells.removeClass("x-date-selected");
25118                 this.cells.each(function(c){
25119                    if(c.dom.firstChild.dateValue == t){
25120                        c.addClass("x-date-selected");
25121                        setTimeout(function(){
25122                             try{c.dom.firstChild.focus();}catch(e){}
25123                        }, 50);
25124                        return false;
25125                    }
25126                 });
25127                 return;
25128             }
25129         }
25130         
25131         var days = date.getDaysInMonth();
25132         var firstOfMonth = date.getFirstDateOfMonth();
25133         var startingPos = firstOfMonth.getDay()-this.startDay;
25134
25135         if(startingPos <= this.startDay){
25136             startingPos += 7;
25137         }
25138
25139         var pm = date.add("mo", -1);
25140         var prevStart = pm.getDaysInMonth()-startingPos;
25141
25142         var cells = this.cells.elements;
25143         var textEls = this.textNodes;
25144         days += startingPos;
25145
25146         // convert everything to numbers so it's fast
25147         var day = 86400000;
25148         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25149         var today = new Date().clearTime().getTime();
25150         var sel = date.clearTime().getTime();
25151         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25152         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25153         var ddMatch = this.disabledDatesRE;
25154         var ddText = this.disabledDatesText;
25155         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25156         var ddaysText = this.disabledDaysText;
25157         var format = this.format;
25158
25159         var setCellClass = function(cal, cell){
25160             cell.title = "";
25161             var t = d.getTime();
25162             cell.firstChild.dateValue = t;
25163             if(t == today){
25164                 cell.className += " x-date-today";
25165                 cell.title = cal.todayText;
25166             }
25167             if(t == sel){
25168                 cell.className += " x-date-selected";
25169                 setTimeout(function(){
25170                     try{cell.firstChild.focus();}catch(e){}
25171                 }, 50);
25172             }
25173             // disabling
25174             if(t < min) {
25175                 cell.className = " x-date-disabled";
25176                 cell.title = cal.minText;
25177                 return;
25178             }
25179             if(t > max) {
25180                 cell.className = " x-date-disabled";
25181                 cell.title = cal.maxText;
25182                 return;
25183             }
25184             if(ddays){
25185                 if(ddays.indexOf(d.getDay()) != -1){
25186                     cell.title = ddaysText;
25187                     cell.className = " x-date-disabled";
25188                 }
25189             }
25190             if(ddMatch && format){
25191                 var fvalue = d.dateFormat(format);
25192                 if(ddMatch.test(fvalue)){
25193                     cell.title = ddText.replace("%0", fvalue);
25194                     cell.className = " x-date-disabled";
25195                 }
25196             }
25197         };
25198
25199         var i = 0;
25200         for(; i < startingPos; i++) {
25201             textEls[i].innerHTML = (++prevStart);
25202             d.setDate(d.getDate()+1);
25203             cells[i].className = "x-date-prevday";
25204             setCellClass(this, cells[i]);
25205         }
25206         for(; i < days; i++){
25207             intDay = i - startingPos + 1;
25208             textEls[i].innerHTML = (intDay);
25209             d.setDate(d.getDate()+1);
25210             cells[i].className = "x-date-active";
25211             setCellClass(this, cells[i]);
25212         }
25213         var extraDays = 0;
25214         for(; i < 42; i++) {
25215              textEls[i].innerHTML = (++extraDays);
25216              d.setDate(d.getDate()+1);
25217              cells[i].className = "x-date-nextday";
25218              setCellClass(this, cells[i]);
25219         }
25220
25221         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25222         this.fireEvent('monthchange', this, date);
25223         
25224         if(!this.internalRender){
25225             var main = this.el.dom.firstChild;
25226             var w = main.offsetWidth;
25227             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25228             Roo.fly(main).setWidth(w);
25229             this.internalRender = true;
25230             // opera does not respect the auto grow header center column
25231             // then, after it gets a width opera refuses to recalculate
25232             // without a second pass
25233             if(Roo.isOpera && !this.secondPass){
25234                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25235                 this.secondPass = true;
25236                 this.update.defer(10, this, [date]);
25237             }
25238         }
25239         
25240         
25241     }
25242 });        /*
25243  * Based on:
25244  * Ext JS Library 1.1.1
25245  * Copyright(c) 2006-2007, Ext JS, LLC.
25246  *
25247  * Originally Released Under LGPL - original licence link has changed is not relivant.
25248  *
25249  * Fork - LGPL
25250  * <script type="text/javascript">
25251  */
25252 /**
25253  * @class Roo.TabPanel
25254  * @extends Roo.util.Observable
25255  * A lightweight tab container.
25256  * <br><br>
25257  * Usage:
25258  * <pre><code>
25259 // basic tabs 1, built from existing content
25260 var tabs = new Roo.TabPanel("tabs1");
25261 tabs.addTab("script", "View Script");
25262 tabs.addTab("markup", "View Markup");
25263 tabs.activate("script");
25264
25265 // more advanced tabs, built from javascript
25266 var jtabs = new Roo.TabPanel("jtabs");
25267 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25268
25269 // set up the UpdateManager
25270 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25271 var updater = tab2.getUpdateManager();
25272 updater.setDefaultUrl("ajax1.htm");
25273 tab2.on('activate', updater.refresh, updater, true);
25274
25275 // Use setUrl for Ajax loading
25276 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25277 tab3.setUrl("ajax2.htm", null, true);
25278
25279 // Disabled tab
25280 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25281 tab4.disable();
25282
25283 jtabs.activate("jtabs-1");
25284  * </code></pre>
25285  * @constructor
25286  * Create a new TabPanel.
25287  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25288  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25289  */
25290 Roo.TabPanel = function(container, config){
25291     /**
25292     * The container element for this TabPanel.
25293     * @type Roo.Element
25294     */
25295     this.el = Roo.get(container, true);
25296     if(config){
25297         if(typeof config == "boolean"){
25298             this.tabPosition = config ? "bottom" : "top";
25299         }else{
25300             Roo.apply(this, config);
25301         }
25302     }
25303     if(this.tabPosition == "bottom"){
25304         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25305         this.el.addClass("x-tabs-bottom");
25306     }
25307     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25308     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25309     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25310     if(Roo.isIE){
25311         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25312     }
25313     if(this.tabPosition != "bottom"){
25314         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25315          * @type Roo.Element
25316          */
25317         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25318         this.el.addClass("x-tabs-top");
25319     }
25320     this.items = [];
25321
25322     this.bodyEl.setStyle("position", "relative");
25323
25324     this.active = null;
25325     this.activateDelegate = this.activate.createDelegate(this);
25326
25327     this.addEvents({
25328         /**
25329          * @event tabchange
25330          * Fires when the active tab changes
25331          * @param {Roo.TabPanel} this
25332          * @param {Roo.TabPanelItem} activePanel The new active tab
25333          */
25334         "tabchange": true,
25335         /**
25336          * @event beforetabchange
25337          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25338          * @param {Roo.TabPanel} this
25339          * @param {Object} e Set cancel to true on this object to cancel the tab change
25340          * @param {Roo.TabPanelItem} tab The tab being changed to
25341          */
25342         "beforetabchange" : true
25343     });
25344
25345     Roo.EventManager.onWindowResize(this.onResize, this);
25346     this.cpad = this.el.getPadding("lr");
25347     this.hiddenCount = 0;
25348
25349
25350     // toolbar on the tabbar support...
25351     if (this.toolbar) {
25352         var tcfg = this.toolbar;
25353         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25354         this.toolbar = new Roo.Toolbar(tcfg);
25355         if (Roo.isSafari) {
25356             var tbl = tcfg.container.child('table', true);
25357             tbl.setAttribute('width', '100%');
25358         }
25359         
25360     }
25361    
25362
25363
25364     Roo.TabPanel.superclass.constructor.call(this);
25365 };
25366
25367 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25368     /*
25369      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25370      */
25371     tabPosition : "top",
25372     /*
25373      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25374      */
25375     currentTabWidth : 0,
25376     /*
25377      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25378      */
25379     minTabWidth : 40,
25380     /*
25381      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25382      */
25383     maxTabWidth : 250,
25384     /*
25385      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25386      */
25387     preferredTabWidth : 175,
25388     /*
25389      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25390      */
25391     resizeTabs : false,
25392     /*
25393      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25394      */
25395     monitorResize : true,
25396     /*
25397      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25398      */
25399     toolbar : false,
25400
25401     /**
25402      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25403      * @param {String} id The id of the div to use <b>or create</b>
25404      * @param {String} text The text for the tab
25405      * @param {String} content (optional) Content to put in the TabPanelItem body
25406      * @param {Boolean} closable (optional) True to create a close icon on the tab
25407      * @return {Roo.TabPanelItem} The created TabPanelItem
25408      */
25409     addTab : function(id, text, content, closable){
25410         var item = new Roo.TabPanelItem(this, id, text, closable);
25411         this.addTabItem(item);
25412         if(content){
25413             item.setContent(content);
25414         }
25415         return item;
25416     },
25417
25418     /**
25419      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25420      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25421      * @return {Roo.TabPanelItem}
25422      */
25423     getTab : function(id){
25424         return this.items[id];
25425     },
25426
25427     /**
25428      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25429      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25430      */
25431     hideTab : function(id){
25432         var t = this.items[id];
25433         if(!t.isHidden()){
25434            t.setHidden(true);
25435            this.hiddenCount++;
25436            this.autoSizeTabs();
25437         }
25438     },
25439
25440     /**
25441      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25442      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25443      */
25444     unhideTab : function(id){
25445         var t = this.items[id];
25446         if(t.isHidden()){
25447            t.setHidden(false);
25448            this.hiddenCount--;
25449            this.autoSizeTabs();
25450         }
25451     },
25452
25453     /**
25454      * Adds an existing {@link Roo.TabPanelItem}.
25455      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25456      */
25457     addTabItem : function(item){
25458         this.items[item.id] = item;
25459         this.items.push(item);
25460         if(this.resizeTabs){
25461            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25462            this.autoSizeTabs();
25463         }else{
25464             item.autoSize();
25465         }
25466     },
25467
25468     /**
25469      * Removes a {@link Roo.TabPanelItem}.
25470      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25471      */
25472     removeTab : function(id){
25473         var items = this.items;
25474         var tab = items[id];
25475         if(!tab) { return; }
25476         var index = items.indexOf(tab);
25477         if(this.active == tab && items.length > 1){
25478             var newTab = this.getNextAvailable(index);
25479             if(newTab) {
25480                 newTab.activate();
25481             }
25482         }
25483         this.stripEl.dom.removeChild(tab.pnode.dom);
25484         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25485             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25486         }
25487         items.splice(index, 1);
25488         delete this.items[tab.id];
25489         tab.fireEvent("close", tab);
25490         tab.purgeListeners();
25491         this.autoSizeTabs();
25492     },
25493
25494     getNextAvailable : function(start){
25495         var items = this.items;
25496         var index = start;
25497         // look for a next tab that will slide over to
25498         // replace the one being removed
25499         while(index < items.length){
25500             var item = items[++index];
25501             if(item && !item.isHidden()){
25502                 return item;
25503             }
25504         }
25505         // if one isn't found select the previous tab (on the left)
25506         index = start;
25507         while(index >= 0){
25508             var item = items[--index];
25509             if(item && !item.isHidden()){
25510                 return item;
25511             }
25512         }
25513         return null;
25514     },
25515
25516     /**
25517      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25518      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25519      */
25520     disableTab : function(id){
25521         var tab = this.items[id];
25522         if(tab && this.active != tab){
25523             tab.disable();
25524         }
25525     },
25526
25527     /**
25528      * Enables a {@link Roo.TabPanelItem} that is disabled.
25529      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25530      */
25531     enableTab : function(id){
25532         var tab = this.items[id];
25533         tab.enable();
25534     },
25535
25536     /**
25537      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25538      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25539      * @return {Roo.TabPanelItem} The TabPanelItem.
25540      */
25541     activate : function(id){
25542         var tab = this.items[id];
25543         if(!tab){
25544             return null;
25545         }
25546         if(tab == this.active || tab.disabled){
25547             return tab;
25548         }
25549         var e = {};
25550         this.fireEvent("beforetabchange", this, e, tab);
25551         if(e.cancel !== true && !tab.disabled){
25552             if(this.active){
25553                 this.active.hide();
25554             }
25555             this.active = this.items[id];
25556             this.active.show();
25557             this.fireEvent("tabchange", this, this.active);
25558         }
25559         return tab;
25560     },
25561
25562     /**
25563      * Gets the active {@link Roo.TabPanelItem}.
25564      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25565      */
25566     getActiveTab : function(){
25567         return this.active;
25568     },
25569
25570     /**
25571      * Updates the tab body element to fit the height of the container element
25572      * for overflow scrolling
25573      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25574      */
25575     syncHeight : function(targetHeight){
25576         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25577         var bm = this.bodyEl.getMargins();
25578         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25579         this.bodyEl.setHeight(newHeight);
25580         return newHeight;
25581     },
25582
25583     onResize : function(){
25584         if(this.monitorResize){
25585             this.autoSizeTabs();
25586         }
25587     },
25588
25589     /**
25590      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25591      */
25592     beginUpdate : function(){
25593         this.updating = true;
25594     },
25595
25596     /**
25597      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25598      */
25599     endUpdate : function(){
25600         this.updating = false;
25601         this.autoSizeTabs();
25602     },
25603
25604     /**
25605      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25606      */
25607     autoSizeTabs : function(){
25608         var count = this.items.length;
25609         var vcount = count - this.hiddenCount;
25610         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25611         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25612         var availWidth = Math.floor(w / vcount);
25613         var b = this.stripBody;
25614         if(b.getWidth() > w){
25615             var tabs = this.items;
25616             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25617             if(availWidth < this.minTabWidth){
25618                 /*if(!this.sleft){    // incomplete scrolling code
25619                     this.createScrollButtons();
25620                 }
25621                 this.showScroll();
25622                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25623             }
25624         }else{
25625             if(this.currentTabWidth < this.preferredTabWidth){
25626                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25627             }
25628         }
25629     },
25630
25631     /**
25632      * Returns the number of tabs in this TabPanel.
25633      * @return {Number}
25634      */
25635      getCount : function(){
25636          return this.items.length;
25637      },
25638
25639     /**
25640      * Resizes all the tabs to the passed width
25641      * @param {Number} The new width
25642      */
25643     setTabWidth : function(width){
25644         this.currentTabWidth = width;
25645         for(var i = 0, len = this.items.length; i < len; i++) {
25646                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25647         }
25648     },
25649
25650     /**
25651      * Destroys this TabPanel
25652      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25653      */
25654     destroy : function(removeEl){
25655         Roo.EventManager.removeResizeListener(this.onResize, this);
25656         for(var i = 0, len = this.items.length; i < len; i++){
25657             this.items[i].purgeListeners();
25658         }
25659         if(removeEl === true){
25660             this.el.update("");
25661             this.el.remove();
25662         }
25663     }
25664 });
25665
25666 /**
25667  * @class Roo.TabPanelItem
25668  * @extends Roo.util.Observable
25669  * Represents an individual item (tab plus body) in a TabPanel.
25670  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25671  * @param {String} id The id of this TabPanelItem
25672  * @param {String} text The text for the tab of this TabPanelItem
25673  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25674  */
25675 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25676     /**
25677      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25678      * @type Roo.TabPanel
25679      */
25680     this.tabPanel = tabPanel;
25681     /**
25682      * The id for this TabPanelItem
25683      * @type String
25684      */
25685     this.id = id;
25686     /** @private */
25687     this.disabled = false;
25688     /** @private */
25689     this.text = text;
25690     /** @private */
25691     this.loaded = false;
25692     this.closable = closable;
25693
25694     /**
25695      * The body element for this TabPanelItem.
25696      * @type Roo.Element
25697      */
25698     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25699     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25700     this.bodyEl.setStyle("display", "block");
25701     this.bodyEl.setStyle("zoom", "1");
25702     this.hideAction();
25703
25704     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25705     /** @private */
25706     this.el = Roo.get(els.el, true);
25707     this.inner = Roo.get(els.inner, true);
25708     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25709     this.pnode = Roo.get(els.el.parentNode, true);
25710     this.el.on("mousedown", this.onTabMouseDown, this);
25711     this.el.on("click", this.onTabClick, this);
25712     /** @private */
25713     if(closable){
25714         var c = Roo.get(els.close, true);
25715         c.dom.title = this.closeText;
25716         c.addClassOnOver("close-over");
25717         c.on("click", this.closeClick, this);
25718      }
25719
25720     this.addEvents({
25721          /**
25722          * @event activate
25723          * Fires when this tab becomes the active tab.
25724          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25725          * @param {Roo.TabPanelItem} this
25726          */
25727         "activate": true,
25728         /**
25729          * @event beforeclose
25730          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25731          * @param {Roo.TabPanelItem} this
25732          * @param {Object} e Set cancel to true on this object to cancel the close.
25733          */
25734         "beforeclose": true,
25735         /**
25736          * @event close
25737          * Fires when this tab is closed.
25738          * @param {Roo.TabPanelItem} this
25739          */
25740          "close": true,
25741         /**
25742          * @event deactivate
25743          * Fires when this tab is no longer the active tab.
25744          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25745          * @param {Roo.TabPanelItem} this
25746          */
25747          "deactivate" : true
25748     });
25749     this.hidden = false;
25750
25751     Roo.TabPanelItem.superclass.constructor.call(this);
25752 };
25753
25754 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25755     purgeListeners : function(){
25756        Roo.util.Observable.prototype.purgeListeners.call(this);
25757        this.el.removeAllListeners();
25758     },
25759     /**
25760      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25761      */
25762     show : function(){
25763         this.pnode.addClass("on");
25764         this.showAction();
25765         if(Roo.isOpera){
25766             this.tabPanel.stripWrap.repaint();
25767         }
25768         this.fireEvent("activate", this.tabPanel, this);
25769     },
25770
25771     /**
25772      * Returns true if this tab is the active tab.
25773      * @return {Boolean}
25774      */
25775     isActive : function(){
25776         return this.tabPanel.getActiveTab() == this;
25777     },
25778
25779     /**
25780      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25781      */
25782     hide : function(){
25783         this.pnode.removeClass("on");
25784         this.hideAction();
25785         this.fireEvent("deactivate", this.tabPanel, this);
25786     },
25787
25788     hideAction : function(){
25789         this.bodyEl.hide();
25790         this.bodyEl.setStyle("position", "absolute");
25791         this.bodyEl.setLeft("-20000px");
25792         this.bodyEl.setTop("-20000px");
25793     },
25794
25795     showAction : function(){
25796         this.bodyEl.setStyle("position", "relative");
25797         this.bodyEl.setTop("");
25798         this.bodyEl.setLeft("");
25799         this.bodyEl.show();
25800     },
25801
25802     /**
25803      * Set the tooltip for the tab.
25804      * @param {String} tooltip The tab's tooltip
25805      */
25806     setTooltip : function(text){
25807         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25808             this.textEl.dom.qtip = text;
25809             this.textEl.dom.removeAttribute('title');
25810         }else{
25811             this.textEl.dom.title = text;
25812         }
25813     },
25814
25815     onTabClick : function(e){
25816         e.preventDefault();
25817         this.tabPanel.activate(this.id);
25818     },
25819
25820     onTabMouseDown : function(e){
25821         e.preventDefault();
25822         this.tabPanel.activate(this.id);
25823     },
25824
25825     getWidth : function(){
25826         return this.inner.getWidth();
25827     },
25828
25829     setWidth : function(width){
25830         var iwidth = width - this.pnode.getPadding("lr");
25831         this.inner.setWidth(iwidth);
25832         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25833         this.pnode.setWidth(width);
25834     },
25835
25836     /**
25837      * Show or hide the tab
25838      * @param {Boolean} hidden True to hide or false to show.
25839      */
25840     setHidden : function(hidden){
25841         this.hidden = hidden;
25842         this.pnode.setStyle("display", hidden ? "none" : "");
25843     },
25844
25845     /**
25846      * Returns true if this tab is "hidden"
25847      * @return {Boolean}
25848      */
25849     isHidden : function(){
25850         return this.hidden;
25851     },
25852
25853     /**
25854      * Returns the text for this tab
25855      * @return {String}
25856      */
25857     getText : function(){
25858         return this.text;
25859     },
25860
25861     autoSize : function(){
25862         //this.el.beginMeasure();
25863         this.textEl.setWidth(1);
25864         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25865         //this.el.endMeasure();
25866     },
25867
25868     /**
25869      * Sets the text for the tab (Note: this also sets the tooltip text)
25870      * @param {String} text The tab's text and tooltip
25871      */
25872     setText : function(text){
25873         this.text = text;
25874         this.textEl.update(text);
25875         this.setTooltip(text);
25876         if(!this.tabPanel.resizeTabs){
25877             this.autoSize();
25878         }
25879     },
25880     /**
25881      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25882      */
25883     activate : function(){
25884         this.tabPanel.activate(this.id);
25885     },
25886
25887     /**
25888      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25889      */
25890     disable : function(){
25891         if(this.tabPanel.active != this){
25892             this.disabled = true;
25893             this.pnode.addClass("disabled");
25894         }
25895     },
25896
25897     /**
25898      * Enables this TabPanelItem if it was previously disabled.
25899      */
25900     enable : function(){
25901         this.disabled = false;
25902         this.pnode.removeClass("disabled");
25903     },
25904
25905     /**
25906      * Sets the content for this TabPanelItem.
25907      * @param {String} content The content
25908      * @param {Boolean} loadScripts true to look for and load scripts
25909      */
25910     setContent : function(content, loadScripts){
25911         this.bodyEl.update(content, loadScripts);
25912     },
25913
25914     /**
25915      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25916      * @return {Roo.UpdateManager} The UpdateManager
25917      */
25918     getUpdateManager : function(){
25919         return this.bodyEl.getUpdateManager();
25920     },
25921
25922     /**
25923      * Set a URL to be used to load the content for this TabPanelItem.
25924      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25925      * @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)
25926      * @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)
25927      * @return {Roo.UpdateManager} The UpdateManager
25928      */
25929     setUrl : function(url, params, loadOnce){
25930         if(this.refreshDelegate){
25931             this.un('activate', this.refreshDelegate);
25932         }
25933         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25934         this.on("activate", this.refreshDelegate);
25935         return this.bodyEl.getUpdateManager();
25936     },
25937
25938     /** @private */
25939     _handleRefresh : function(url, params, loadOnce){
25940         if(!loadOnce || !this.loaded){
25941             var updater = this.bodyEl.getUpdateManager();
25942             updater.update(url, params, this._setLoaded.createDelegate(this));
25943         }
25944     },
25945
25946     /**
25947      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25948      *   Will fail silently if the setUrl method has not been called.
25949      *   This does not activate the panel, just updates its content.
25950      */
25951     refresh : function(){
25952         if(this.refreshDelegate){
25953            this.loaded = false;
25954            this.refreshDelegate();
25955         }
25956     },
25957
25958     /** @private */
25959     _setLoaded : function(){
25960         this.loaded = true;
25961     },
25962
25963     /** @private */
25964     closeClick : function(e){
25965         var o = {};
25966         e.stopEvent();
25967         this.fireEvent("beforeclose", this, o);
25968         if(o.cancel !== true){
25969             this.tabPanel.removeTab(this.id);
25970         }
25971     },
25972     /**
25973      * The text displayed in the tooltip for the close icon.
25974      * @type String
25975      */
25976     closeText : "Close this tab"
25977 });
25978
25979 /** @private */
25980 Roo.TabPanel.prototype.createStrip = function(container){
25981     var strip = document.createElement("div");
25982     strip.className = "x-tabs-wrap";
25983     container.appendChild(strip);
25984     return strip;
25985 };
25986 /** @private */
25987 Roo.TabPanel.prototype.createStripList = function(strip){
25988     // div wrapper for retard IE
25989     // returns the "tr" element.
25990     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25991         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25992         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25993     return strip.firstChild.firstChild.firstChild.firstChild;
25994 };
25995 /** @private */
25996 Roo.TabPanel.prototype.createBody = function(container){
25997     var body = document.createElement("div");
25998     Roo.id(body, "tab-body");
25999     Roo.fly(body).addClass("x-tabs-body");
26000     container.appendChild(body);
26001     return body;
26002 };
26003 /** @private */
26004 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26005     var body = Roo.getDom(id);
26006     if(!body){
26007         body = document.createElement("div");
26008         body.id = id;
26009     }
26010     Roo.fly(body).addClass("x-tabs-item-body");
26011     bodyEl.insertBefore(body, bodyEl.firstChild);
26012     return body;
26013 };
26014 /** @private */
26015 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26016     var td = document.createElement("td");
26017     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26018     //stripEl.appendChild(td);
26019     if(closable){
26020         td.className = "x-tabs-closable";
26021         if(!this.closeTpl){
26022             this.closeTpl = new Roo.Template(
26023                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26024                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26025                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26026             );
26027         }
26028         var el = this.closeTpl.overwrite(td, {"text": text});
26029         var close = el.getElementsByTagName("div")[0];
26030         var inner = el.getElementsByTagName("em")[0];
26031         return {"el": el, "close": close, "inner": inner};
26032     } else {
26033         if(!this.tabTpl){
26034             this.tabTpl = new Roo.Template(
26035                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26036                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26037             );
26038         }
26039         var el = this.tabTpl.overwrite(td, {"text": text});
26040         var inner = el.getElementsByTagName("em")[0];
26041         return {"el": el, "inner": inner};
26042     }
26043 };/*
26044  * Based on:
26045  * Ext JS Library 1.1.1
26046  * Copyright(c) 2006-2007, Ext JS, LLC.
26047  *
26048  * Originally Released Under LGPL - original licence link has changed is not relivant.
26049  *
26050  * Fork - LGPL
26051  * <script type="text/javascript">
26052  */
26053
26054 /**
26055  * @class Roo.Button
26056  * @extends Roo.util.Observable
26057  * Simple Button class
26058  * @cfg {String} text The button text
26059  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26060  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26061  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26062  * @cfg {Object} scope The scope of the handler
26063  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26064  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26065  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26066  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26067  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26068  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26069    applies if enableToggle = true)
26070  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26071  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26072   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26073  * @constructor
26074  * Create a new button
26075  * @param {Object} config The config object
26076  */
26077 Roo.Button = function(renderTo, config)
26078 {
26079     if (!config) {
26080         config = renderTo;
26081         renderTo = config.renderTo || false;
26082     }
26083     
26084     Roo.apply(this, config);
26085     this.addEvents({
26086         /**
26087              * @event click
26088              * Fires when this button is clicked
26089              * @param {Button} this
26090              * @param {EventObject} e The click event
26091              */
26092             "click" : true,
26093         /**
26094              * @event toggle
26095              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26096              * @param {Button} this
26097              * @param {Boolean} pressed
26098              */
26099             "toggle" : true,
26100         /**
26101              * @event mouseover
26102              * Fires when the mouse hovers over the button
26103              * @param {Button} this
26104              * @param {Event} e The event object
26105              */
26106         'mouseover' : true,
26107         /**
26108              * @event mouseout
26109              * Fires when the mouse exits the button
26110              * @param {Button} this
26111              * @param {Event} e The event object
26112              */
26113         'mouseout': true,
26114          /**
26115              * @event render
26116              * Fires when the button is rendered
26117              * @param {Button} this
26118              */
26119         'render': true
26120     });
26121     if(this.menu){
26122         this.menu = Roo.menu.MenuMgr.get(this.menu);
26123     }
26124     // register listeners first!!  - so render can be captured..
26125     Roo.util.Observable.call(this);
26126     if(renderTo){
26127         this.render(renderTo);
26128     }
26129     
26130   
26131 };
26132
26133 Roo.extend(Roo.Button, Roo.util.Observable, {
26134     /**
26135      * 
26136      */
26137     
26138     /**
26139      * Read-only. True if this button is hidden
26140      * @type Boolean
26141      */
26142     hidden : false,
26143     /**
26144      * Read-only. True if this button is disabled
26145      * @type Boolean
26146      */
26147     disabled : false,
26148     /**
26149      * Read-only. True if this button is pressed (only if enableToggle = true)
26150      * @type Boolean
26151      */
26152     pressed : false,
26153
26154     /**
26155      * @cfg {Number} tabIndex 
26156      * The DOM tabIndex for this button (defaults to undefined)
26157      */
26158     tabIndex : undefined,
26159
26160     /**
26161      * @cfg {Boolean} enableToggle
26162      * True to enable pressed/not pressed toggling (defaults to false)
26163      */
26164     enableToggle: false,
26165     /**
26166      * @cfg {Mixed} menu
26167      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26168      */
26169     menu : undefined,
26170     /**
26171      * @cfg {String} menuAlign
26172      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26173      */
26174     menuAlign : "tl-bl?",
26175
26176     /**
26177      * @cfg {String} iconCls
26178      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26179      */
26180     iconCls : undefined,
26181     /**
26182      * @cfg {String} type
26183      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26184      */
26185     type : 'button',
26186
26187     // private
26188     menuClassTarget: 'tr',
26189
26190     /**
26191      * @cfg {String} clickEvent
26192      * The type of event to map to the button's event handler (defaults to 'click')
26193      */
26194     clickEvent : 'click',
26195
26196     /**
26197      * @cfg {Boolean} handleMouseEvents
26198      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26199      */
26200     handleMouseEvents : true,
26201
26202     /**
26203      * @cfg {String} tooltipType
26204      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26205      */
26206     tooltipType : 'qtip',
26207
26208     /**
26209      * @cfg {String} cls
26210      * A CSS class to apply to the button's main element.
26211      */
26212     
26213     /**
26214      * @cfg {Roo.Template} template (Optional)
26215      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26216      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26217      * require code modifications if required elements (e.g. a button) aren't present.
26218      */
26219
26220     // private
26221     render : function(renderTo){
26222         var btn;
26223         if(this.hideParent){
26224             this.parentEl = Roo.get(renderTo);
26225         }
26226         if(!this.dhconfig){
26227             if(!this.template){
26228                 if(!Roo.Button.buttonTemplate){
26229                     // hideous table template
26230                     Roo.Button.buttonTemplate = new Roo.Template(
26231                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26232                         '<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>',
26233                         "</tr></tbody></table>");
26234                 }
26235                 this.template = Roo.Button.buttonTemplate;
26236             }
26237             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26238             var btnEl = btn.child("button:first");
26239             btnEl.on('focus', this.onFocus, this);
26240             btnEl.on('blur', this.onBlur, this);
26241             if(this.cls){
26242                 btn.addClass(this.cls);
26243             }
26244             if(this.icon){
26245                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26246             }
26247             if(this.iconCls){
26248                 btnEl.addClass(this.iconCls);
26249                 if(!this.cls){
26250                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26251                 }
26252             }
26253             if(this.tabIndex !== undefined){
26254                 btnEl.dom.tabIndex = this.tabIndex;
26255             }
26256             if(this.tooltip){
26257                 if(typeof this.tooltip == 'object'){
26258                     Roo.QuickTips.tips(Roo.apply({
26259                           target: btnEl.id
26260                     }, this.tooltip));
26261                 } else {
26262                     btnEl.dom[this.tooltipType] = this.tooltip;
26263                 }
26264             }
26265         }else{
26266             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26267         }
26268         this.el = btn;
26269         if(this.id){
26270             this.el.dom.id = this.el.id = this.id;
26271         }
26272         if(this.menu){
26273             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26274             this.menu.on("show", this.onMenuShow, this);
26275             this.menu.on("hide", this.onMenuHide, this);
26276         }
26277         btn.addClass("x-btn");
26278         if(Roo.isIE && !Roo.isIE7){
26279             this.autoWidth.defer(1, this);
26280         }else{
26281             this.autoWidth();
26282         }
26283         if(this.handleMouseEvents){
26284             btn.on("mouseover", this.onMouseOver, this);
26285             btn.on("mouseout", this.onMouseOut, this);
26286             btn.on("mousedown", this.onMouseDown, this);
26287         }
26288         btn.on(this.clickEvent, this.onClick, this);
26289         //btn.on("mouseup", this.onMouseUp, this);
26290         if(this.hidden){
26291             this.hide();
26292         }
26293         if(this.disabled){
26294             this.disable();
26295         }
26296         Roo.ButtonToggleMgr.register(this);
26297         if(this.pressed){
26298             this.el.addClass("x-btn-pressed");
26299         }
26300         if(this.repeat){
26301             var repeater = new Roo.util.ClickRepeater(btn,
26302                 typeof this.repeat == "object" ? this.repeat : {}
26303             );
26304             repeater.on("click", this.onClick,  this);
26305         }
26306         
26307         this.fireEvent('render', this);
26308         
26309     },
26310     /**
26311      * Returns the button's underlying element
26312      * @return {Roo.Element} The element
26313      */
26314     getEl : function(){
26315         return this.el;  
26316     },
26317     
26318     /**
26319      * Destroys this Button and removes any listeners.
26320      */
26321     destroy : function(){
26322         Roo.ButtonToggleMgr.unregister(this);
26323         this.el.removeAllListeners();
26324         this.purgeListeners();
26325         this.el.remove();
26326     },
26327
26328     // private
26329     autoWidth : function(){
26330         if(this.el){
26331             this.el.setWidth("auto");
26332             if(Roo.isIE7 && Roo.isStrict){
26333                 var ib = this.el.child('button');
26334                 if(ib && ib.getWidth() > 20){
26335                     ib.clip();
26336                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26337                 }
26338             }
26339             if(this.minWidth){
26340                 if(this.hidden){
26341                     this.el.beginMeasure();
26342                 }
26343                 if(this.el.getWidth() < this.minWidth){
26344                     this.el.setWidth(this.minWidth);
26345                 }
26346                 if(this.hidden){
26347                     this.el.endMeasure();
26348                 }
26349             }
26350         }
26351     },
26352
26353     /**
26354      * Assigns this button's click handler
26355      * @param {Function} handler The function to call when the button is clicked
26356      * @param {Object} scope (optional) Scope for the function passed in
26357      */
26358     setHandler : function(handler, scope){
26359         this.handler = handler;
26360         this.scope = scope;  
26361     },
26362     
26363     /**
26364      * Sets this button's text
26365      * @param {String} text The button text
26366      */
26367     setText : function(text){
26368         this.text = text;
26369         if(this.el){
26370             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26371         }
26372         this.autoWidth();
26373     },
26374     
26375     /**
26376      * Gets the text for this button
26377      * @return {String} The button text
26378      */
26379     getText : function(){
26380         return this.text;  
26381     },
26382     
26383     /**
26384      * Show this button
26385      */
26386     show: function(){
26387         this.hidden = false;
26388         if(this.el){
26389             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26390         }
26391     },
26392     
26393     /**
26394      * Hide this button
26395      */
26396     hide: function(){
26397         this.hidden = true;
26398         if(this.el){
26399             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26400         }
26401     },
26402     
26403     /**
26404      * Convenience function for boolean show/hide
26405      * @param {Boolean} visible True to show, false to hide
26406      */
26407     setVisible: function(visible){
26408         if(visible) {
26409             this.show();
26410         }else{
26411             this.hide();
26412         }
26413     },
26414     
26415     /**
26416      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26417      * @param {Boolean} state (optional) Force a particular state
26418      */
26419     toggle : function(state){
26420         state = state === undefined ? !this.pressed : state;
26421         if(state != this.pressed){
26422             if(state){
26423                 this.el.addClass("x-btn-pressed");
26424                 this.pressed = true;
26425                 this.fireEvent("toggle", this, true);
26426             }else{
26427                 this.el.removeClass("x-btn-pressed");
26428                 this.pressed = false;
26429                 this.fireEvent("toggle", this, false);
26430             }
26431             if(this.toggleHandler){
26432                 this.toggleHandler.call(this.scope || this, this, state);
26433             }
26434         }
26435     },
26436     
26437     /**
26438      * Focus the button
26439      */
26440     focus : function(){
26441         this.el.child('button:first').focus();
26442     },
26443     
26444     /**
26445      * Disable this button
26446      */
26447     disable : function(){
26448         if(this.el){
26449             this.el.addClass("x-btn-disabled");
26450         }
26451         this.disabled = true;
26452     },
26453     
26454     /**
26455      * Enable this button
26456      */
26457     enable : function(){
26458         if(this.el){
26459             this.el.removeClass("x-btn-disabled");
26460         }
26461         this.disabled = false;
26462     },
26463
26464     /**
26465      * Convenience function for boolean enable/disable
26466      * @param {Boolean} enabled True to enable, false to disable
26467      */
26468     setDisabled : function(v){
26469         this[v !== true ? "enable" : "disable"]();
26470     },
26471
26472     // private
26473     onClick : function(e){
26474         if(e){
26475             e.preventDefault();
26476         }
26477         if(e.button != 0){
26478             return;
26479         }
26480         if(!this.disabled){
26481             if(this.enableToggle){
26482                 this.toggle();
26483             }
26484             if(this.menu && !this.menu.isVisible()){
26485                 this.menu.show(this.el, this.menuAlign);
26486             }
26487             this.fireEvent("click", this, e);
26488             if(this.handler){
26489                 this.el.removeClass("x-btn-over");
26490                 this.handler.call(this.scope || this, this, e);
26491             }
26492         }
26493     },
26494     // private
26495     onMouseOver : function(e){
26496         if(!this.disabled){
26497             this.el.addClass("x-btn-over");
26498             this.fireEvent('mouseover', this, e);
26499         }
26500     },
26501     // private
26502     onMouseOut : function(e){
26503         if(!e.within(this.el,  true)){
26504             this.el.removeClass("x-btn-over");
26505             this.fireEvent('mouseout', this, e);
26506         }
26507     },
26508     // private
26509     onFocus : function(e){
26510         if(!this.disabled){
26511             this.el.addClass("x-btn-focus");
26512         }
26513     },
26514     // private
26515     onBlur : function(e){
26516         this.el.removeClass("x-btn-focus");
26517     },
26518     // private
26519     onMouseDown : function(e){
26520         if(!this.disabled && e.button == 0){
26521             this.el.addClass("x-btn-click");
26522             Roo.get(document).on('mouseup', this.onMouseUp, this);
26523         }
26524     },
26525     // private
26526     onMouseUp : function(e){
26527         if(e.button == 0){
26528             this.el.removeClass("x-btn-click");
26529             Roo.get(document).un('mouseup', this.onMouseUp, this);
26530         }
26531     },
26532     // private
26533     onMenuShow : function(e){
26534         this.el.addClass("x-btn-menu-active");
26535     },
26536     // private
26537     onMenuHide : function(e){
26538         this.el.removeClass("x-btn-menu-active");
26539     }   
26540 });
26541
26542 // Private utility class used by Button
26543 Roo.ButtonToggleMgr = function(){
26544    var groups = {};
26545    
26546    function toggleGroup(btn, state){
26547        if(state){
26548            var g = groups[btn.toggleGroup];
26549            for(var i = 0, l = g.length; i < l; i++){
26550                if(g[i] != btn){
26551                    g[i].toggle(false);
26552                }
26553            }
26554        }
26555    }
26556    
26557    return {
26558        register : function(btn){
26559            if(!btn.toggleGroup){
26560                return;
26561            }
26562            var g = groups[btn.toggleGroup];
26563            if(!g){
26564                g = groups[btn.toggleGroup] = [];
26565            }
26566            g.push(btn);
26567            btn.on("toggle", toggleGroup);
26568        },
26569        
26570        unregister : function(btn){
26571            if(!btn.toggleGroup){
26572                return;
26573            }
26574            var g = groups[btn.toggleGroup];
26575            if(g){
26576                g.remove(btn);
26577                btn.un("toggle", toggleGroup);
26578            }
26579        }
26580    };
26581 }();/*
26582  * Based on:
26583  * Ext JS Library 1.1.1
26584  * Copyright(c) 2006-2007, Ext JS, LLC.
26585  *
26586  * Originally Released Under LGPL - original licence link has changed is not relivant.
26587  *
26588  * Fork - LGPL
26589  * <script type="text/javascript">
26590  */
26591  
26592 /**
26593  * @class Roo.SplitButton
26594  * @extends Roo.Button
26595  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26596  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26597  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26598  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26599  * @cfg {String} arrowTooltip The title attribute of the arrow
26600  * @constructor
26601  * Create a new menu button
26602  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26603  * @param {Object} config The config object
26604  */
26605 Roo.SplitButton = function(renderTo, config){
26606     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26607     /**
26608      * @event arrowclick
26609      * Fires when this button's arrow is clicked
26610      * @param {SplitButton} this
26611      * @param {EventObject} e The click event
26612      */
26613     this.addEvents({"arrowclick":true});
26614 };
26615
26616 Roo.extend(Roo.SplitButton, Roo.Button, {
26617     render : function(renderTo){
26618         // this is one sweet looking template!
26619         var tpl = new Roo.Template(
26620             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26621             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26622             '<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>',
26623             "</tbody></table></td><td>",
26624             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26625             '<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>',
26626             "</tbody></table></td></tr></table>"
26627         );
26628         var btn = tpl.append(renderTo, [this.text, this.type], true);
26629         var btnEl = btn.child("button");
26630         if(this.cls){
26631             btn.addClass(this.cls);
26632         }
26633         if(this.icon){
26634             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26635         }
26636         if(this.iconCls){
26637             btnEl.addClass(this.iconCls);
26638             if(!this.cls){
26639                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26640             }
26641         }
26642         this.el = btn;
26643         if(this.handleMouseEvents){
26644             btn.on("mouseover", this.onMouseOver, this);
26645             btn.on("mouseout", this.onMouseOut, this);
26646             btn.on("mousedown", this.onMouseDown, this);
26647             btn.on("mouseup", this.onMouseUp, this);
26648         }
26649         btn.on(this.clickEvent, this.onClick, this);
26650         if(this.tooltip){
26651             if(typeof this.tooltip == 'object'){
26652                 Roo.QuickTips.tips(Roo.apply({
26653                       target: btnEl.id
26654                 }, this.tooltip));
26655             } else {
26656                 btnEl.dom[this.tooltipType] = this.tooltip;
26657             }
26658         }
26659         if(this.arrowTooltip){
26660             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26661         }
26662         if(this.hidden){
26663             this.hide();
26664         }
26665         if(this.disabled){
26666             this.disable();
26667         }
26668         if(this.pressed){
26669             this.el.addClass("x-btn-pressed");
26670         }
26671         if(Roo.isIE && !Roo.isIE7){
26672             this.autoWidth.defer(1, this);
26673         }else{
26674             this.autoWidth();
26675         }
26676         if(this.menu){
26677             this.menu.on("show", this.onMenuShow, this);
26678             this.menu.on("hide", this.onMenuHide, this);
26679         }
26680         this.fireEvent('render', this);
26681     },
26682
26683     // private
26684     autoWidth : function(){
26685         if(this.el){
26686             var tbl = this.el.child("table:first");
26687             var tbl2 = this.el.child("table:last");
26688             this.el.setWidth("auto");
26689             tbl.setWidth("auto");
26690             if(Roo.isIE7 && Roo.isStrict){
26691                 var ib = this.el.child('button:first');
26692                 if(ib && ib.getWidth() > 20){
26693                     ib.clip();
26694                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26695                 }
26696             }
26697             if(this.minWidth){
26698                 if(this.hidden){
26699                     this.el.beginMeasure();
26700                 }
26701                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26702                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26703                 }
26704                 if(this.hidden){
26705                     this.el.endMeasure();
26706                 }
26707             }
26708             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26709         } 
26710     },
26711     /**
26712      * Sets this button's click handler
26713      * @param {Function} handler The function to call when the button is clicked
26714      * @param {Object} scope (optional) Scope for the function passed above
26715      */
26716     setHandler : function(handler, scope){
26717         this.handler = handler;
26718         this.scope = scope;  
26719     },
26720     
26721     /**
26722      * Sets this button's arrow click handler
26723      * @param {Function} handler The function to call when the arrow is clicked
26724      * @param {Object} scope (optional) Scope for the function passed above
26725      */
26726     setArrowHandler : function(handler, scope){
26727         this.arrowHandler = handler;
26728         this.scope = scope;  
26729     },
26730     
26731     /**
26732      * Focus the button
26733      */
26734     focus : function(){
26735         if(this.el){
26736             this.el.child("button:first").focus();
26737         }
26738     },
26739
26740     // private
26741     onClick : function(e){
26742         e.preventDefault();
26743         if(!this.disabled){
26744             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26745                 if(this.menu && !this.menu.isVisible()){
26746                     this.menu.show(this.el, this.menuAlign);
26747                 }
26748                 this.fireEvent("arrowclick", this, e);
26749                 if(this.arrowHandler){
26750                     this.arrowHandler.call(this.scope || this, this, e);
26751                 }
26752             }else{
26753                 this.fireEvent("click", this, e);
26754                 if(this.handler){
26755                     this.handler.call(this.scope || this, this, e);
26756                 }
26757             }
26758         }
26759     },
26760     // private
26761     onMouseDown : function(e){
26762         if(!this.disabled){
26763             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26764         }
26765     },
26766     // private
26767     onMouseUp : function(e){
26768         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26769     }   
26770 });
26771
26772
26773 // backwards compat
26774 Roo.MenuButton = Roo.SplitButton;/*
26775  * Based on:
26776  * Ext JS Library 1.1.1
26777  * Copyright(c) 2006-2007, Ext JS, LLC.
26778  *
26779  * Originally Released Under LGPL - original licence link has changed is not relivant.
26780  *
26781  * Fork - LGPL
26782  * <script type="text/javascript">
26783  */
26784
26785 /**
26786  * @class Roo.Toolbar
26787  * Basic Toolbar class.
26788  * @constructor
26789  * Creates a new Toolbar
26790  * @param {Object} container The config object
26791  */ 
26792 Roo.Toolbar = function(container, buttons, config)
26793 {
26794     /// old consturctor format still supported..
26795     if(container instanceof Array){ // omit the container for later rendering
26796         buttons = container;
26797         config = buttons;
26798         container = null;
26799     }
26800     if (typeof(container) == 'object' && container.xtype) {
26801         config = container;
26802         container = config.container;
26803         buttons = config.buttons || []; // not really - use items!!
26804     }
26805     var xitems = [];
26806     if (config && config.items) {
26807         xitems = config.items;
26808         delete config.items;
26809     }
26810     Roo.apply(this, config);
26811     this.buttons = buttons;
26812     
26813     if(container){
26814         this.render(container);
26815     }
26816     this.xitems = xitems;
26817     Roo.each(xitems, function(b) {
26818         this.add(b);
26819     }, this);
26820     
26821 };
26822
26823 Roo.Toolbar.prototype = {
26824     /**
26825      * @cfg {Array} items
26826      * array of button configs or elements to add (will be converted to a MixedCollection)
26827      */
26828     
26829     /**
26830      * @cfg {String/HTMLElement/Element} container
26831      * The id or element that will contain the toolbar
26832      */
26833     // private
26834     render : function(ct){
26835         this.el = Roo.get(ct);
26836         if(this.cls){
26837             this.el.addClass(this.cls);
26838         }
26839         // using a table allows for vertical alignment
26840         // 100% width is needed by Safari...
26841         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26842         this.tr = this.el.child("tr", true);
26843         var autoId = 0;
26844         this.items = new Roo.util.MixedCollection(false, function(o){
26845             return o.id || ("item" + (++autoId));
26846         });
26847         if(this.buttons){
26848             this.add.apply(this, this.buttons);
26849             delete this.buttons;
26850         }
26851     },
26852
26853     /**
26854      * Adds element(s) to the toolbar -- this function takes a variable number of 
26855      * arguments of mixed type and adds them to the toolbar.
26856      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26857      * <ul>
26858      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26859      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26860      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26861      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26862      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26863      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26864      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26865      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26866      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26867      * </ul>
26868      * @param {Mixed} arg2
26869      * @param {Mixed} etc.
26870      */
26871     add : function(){
26872         var a = arguments, l = a.length;
26873         for(var i = 0; i < l; i++){
26874             this._add(a[i]);
26875         }
26876     },
26877     // private..
26878     _add : function(el) {
26879         
26880         if (el.xtype) {
26881             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26882         }
26883         
26884         if (el.applyTo){ // some kind of form field
26885             return this.addField(el);
26886         } 
26887         if (el.render){ // some kind of Toolbar.Item
26888             return this.addItem(el);
26889         }
26890         if (typeof el == "string"){ // string
26891             if(el == "separator" || el == "-"){
26892                 return this.addSeparator();
26893             }
26894             if (el == " "){
26895                 return this.addSpacer();
26896             }
26897             if(el == "->"){
26898                 return this.addFill();
26899             }
26900             return this.addText(el);
26901             
26902         }
26903         if(el.tagName){ // element
26904             return this.addElement(el);
26905         }
26906         if(typeof el == "object"){ // must be button config?
26907             return this.addButton(el);
26908         }
26909         // and now what?!?!
26910         return false;
26911         
26912     },
26913     
26914     /**
26915      * Add an Xtype element
26916      * @param {Object} xtype Xtype Object
26917      * @return {Object} created Object
26918      */
26919     addxtype : function(e){
26920         return this.add(e);  
26921     },
26922     
26923     /**
26924      * Returns the Element for this toolbar.
26925      * @return {Roo.Element}
26926      */
26927     getEl : function(){
26928         return this.el;  
26929     },
26930     
26931     /**
26932      * Adds a separator
26933      * @return {Roo.Toolbar.Item} The separator item
26934      */
26935     addSeparator : function(){
26936         return this.addItem(new Roo.Toolbar.Separator());
26937     },
26938
26939     /**
26940      * Adds a spacer element
26941      * @return {Roo.Toolbar.Spacer} The spacer item
26942      */
26943     addSpacer : function(){
26944         return this.addItem(new Roo.Toolbar.Spacer());
26945     },
26946
26947     /**
26948      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26949      * @return {Roo.Toolbar.Fill} The fill item
26950      */
26951     addFill : function(){
26952         return this.addItem(new Roo.Toolbar.Fill());
26953     },
26954
26955     /**
26956      * Adds any standard HTML element to the toolbar
26957      * @param {String/HTMLElement/Element} el The element or id of the element to add
26958      * @return {Roo.Toolbar.Item} The element's item
26959      */
26960     addElement : function(el){
26961         return this.addItem(new Roo.Toolbar.Item(el));
26962     },
26963     /**
26964      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26965      * @type Roo.util.MixedCollection  
26966      */
26967     items : false,
26968      
26969     /**
26970      * Adds any Toolbar.Item or subclass
26971      * @param {Roo.Toolbar.Item} item
26972      * @return {Roo.Toolbar.Item} The item
26973      */
26974     addItem : function(item){
26975         var td = this.nextBlock();
26976         item.render(td);
26977         this.items.add(item);
26978         return item;
26979     },
26980     
26981     /**
26982      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26983      * @param {Object/Array} config A button config or array of configs
26984      * @return {Roo.Toolbar.Button/Array}
26985      */
26986     addButton : function(config){
26987         if(config instanceof Array){
26988             var buttons = [];
26989             for(var i = 0, len = config.length; i < len; i++) {
26990                 buttons.push(this.addButton(config[i]));
26991             }
26992             return buttons;
26993         }
26994         var b = config;
26995         if(!(config instanceof Roo.Toolbar.Button)){
26996             b = config.split ?
26997                 new Roo.Toolbar.SplitButton(config) :
26998                 new Roo.Toolbar.Button(config);
26999         }
27000         var td = this.nextBlock();
27001         b.render(td);
27002         this.items.add(b);
27003         return b;
27004     },
27005     
27006     /**
27007      * Adds text to the toolbar
27008      * @param {String} text The text to add
27009      * @return {Roo.Toolbar.Item} The element's item
27010      */
27011     addText : function(text){
27012         return this.addItem(new Roo.Toolbar.TextItem(text));
27013     },
27014     
27015     /**
27016      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27017      * @param {Number} index The index where the item is to be inserted
27018      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27019      * @return {Roo.Toolbar.Button/Item}
27020      */
27021     insertButton : function(index, item){
27022         if(item instanceof Array){
27023             var buttons = [];
27024             for(var i = 0, len = item.length; i < len; i++) {
27025                buttons.push(this.insertButton(index + i, item[i]));
27026             }
27027             return buttons;
27028         }
27029         if (!(item instanceof Roo.Toolbar.Button)){
27030            item = new Roo.Toolbar.Button(item);
27031         }
27032         var td = document.createElement("td");
27033         this.tr.insertBefore(td, this.tr.childNodes[index]);
27034         item.render(td);
27035         this.items.insert(index, item);
27036         return item;
27037     },
27038     
27039     /**
27040      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27041      * @param {Object} config
27042      * @return {Roo.Toolbar.Item} The element's item
27043      */
27044     addDom : function(config, returnEl){
27045         var td = this.nextBlock();
27046         Roo.DomHelper.overwrite(td, config);
27047         var ti = new Roo.Toolbar.Item(td.firstChild);
27048         ti.render(td);
27049         this.items.add(ti);
27050         return ti;
27051     },
27052
27053     /**
27054      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27055      * @type Roo.util.MixedCollection  
27056      */
27057     fields : false,
27058     
27059     /**
27060      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27061      * Note: the field should not have been rendered yet. For a field that has already been
27062      * rendered, use {@link #addElement}.
27063      * @param {Roo.form.Field} field
27064      * @return {Roo.ToolbarItem}
27065      */
27066      
27067       
27068     addField : function(field) {
27069         if (!this.fields) {
27070             var autoId = 0;
27071             this.fields = new Roo.util.MixedCollection(false, function(o){
27072                 return o.id || ("item" + (++autoId));
27073             });
27074
27075         }
27076         
27077         var td = this.nextBlock();
27078         field.render(td);
27079         var ti = new Roo.Toolbar.Item(td.firstChild);
27080         ti.render(td);
27081         this.items.add(ti);
27082         this.fields.add(field);
27083         return ti;
27084     },
27085     /**
27086      * Hide the toolbar
27087      * @method hide
27088      */
27089      
27090       
27091     hide : function()
27092     {
27093         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27094         this.el.child('div').hide();
27095     },
27096     /**
27097      * Show the toolbar
27098      * @method show
27099      */
27100     show : function()
27101     {
27102         this.el.child('div').show();
27103     },
27104       
27105     // private
27106     nextBlock : function(){
27107         var td = document.createElement("td");
27108         this.tr.appendChild(td);
27109         return td;
27110     },
27111
27112     // private
27113     destroy : function(){
27114         if(this.items){ // rendered?
27115             Roo.destroy.apply(Roo, this.items.items);
27116         }
27117         if(this.fields){ // rendered?
27118             Roo.destroy.apply(Roo, this.fields.items);
27119         }
27120         Roo.Element.uncache(this.el, this.tr);
27121     }
27122 };
27123
27124 /**
27125  * @class Roo.Toolbar.Item
27126  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27127  * @constructor
27128  * Creates a new Item
27129  * @param {HTMLElement} el 
27130  */
27131 Roo.Toolbar.Item = function(el){
27132     this.el = Roo.getDom(el);
27133     this.id = Roo.id(this.el);
27134     this.hidden = false;
27135 };
27136
27137 Roo.Toolbar.Item.prototype = {
27138     
27139     /**
27140      * Get this item's HTML Element
27141      * @return {HTMLElement}
27142      */
27143     getEl : function(){
27144        return this.el;  
27145     },
27146
27147     // private
27148     render : function(td){
27149         this.td = td;
27150         td.appendChild(this.el);
27151     },
27152     
27153     /**
27154      * Removes and destroys this item.
27155      */
27156     destroy : function(){
27157         this.td.parentNode.removeChild(this.td);
27158     },
27159     
27160     /**
27161      * Shows this item.
27162      */
27163     show: function(){
27164         this.hidden = false;
27165         this.td.style.display = "";
27166     },
27167     
27168     /**
27169      * Hides this item.
27170      */
27171     hide: function(){
27172         this.hidden = true;
27173         this.td.style.display = "none";
27174     },
27175     
27176     /**
27177      * Convenience function for boolean show/hide.
27178      * @param {Boolean} visible true to show/false to hide
27179      */
27180     setVisible: function(visible){
27181         if(visible) {
27182             this.show();
27183         }else{
27184             this.hide();
27185         }
27186     },
27187     
27188     /**
27189      * Try to focus this item.
27190      */
27191     focus : function(){
27192         Roo.fly(this.el).focus();
27193     },
27194     
27195     /**
27196      * Disables this item.
27197      */
27198     disable : function(){
27199         Roo.fly(this.td).addClass("x-item-disabled");
27200         this.disabled = true;
27201         this.el.disabled = true;
27202     },
27203     
27204     /**
27205      * Enables this item.
27206      */
27207     enable : function(){
27208         Roo.fly(this.td).removeClass("x-item-disabled");
27209         this.disabled = false;
27210         this.el.disabled = false;
27211     }
27212 };
27213
27214
27215 /**
27216  * @class Roo.Toolbar.Separator
27217  * @extends Roo.Toolbar.Item
27218  * A simple toolbar separator class
27219  * @constructor
27220  * Creates a new Separator
27221  */
27222 Roo.Toolbar.Separator = function(){
27223     var s = document.createElement("span");
27224     s.className = "ytb-sep";
27225     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27226 };
27227 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27228     enable:Roo.emptyFn,
27229     disable:Roo.emptyFn,
27230     focus:Roo.emptyFn
27231 });
27232
27233 /**
27234  * @class Roo.Toolbar.Spacer
27235  * @extends Roo.Toolbar.Item
27236  * A simple element that adds extra horizontal space to a toolbar.
27237  * @constructor
27238  * Creates a new Spacer
27239  */
27240 Roo.Toolbar.Spacer = function(){
27241     var s = document.createElement("div");
27242     s.className = "ytb-spacer";
27243     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27244 };
27245 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27246     enable:Roo.emptyFn,
27247     disable:Roo.emptyFn,
27248     focus:Roo.emptyFn
27249 });
27250
27251 /**
27252  * @class Roo.Toolbar.Fill
27253  * @extends Roo.Toolbar.Spacer
27254  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27255  * @constructor
27256  * Creates a new Spacer
27257  */
27258 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27259     // private
27260     render : function(td){
27261         td.style.width = '100%';
27262         Roo.Toolbar.Fill.superclass.render.call(this, td);
27263     }
27264 });
27265
27266 /**
27267  * @class Roo.Toolbar.TextItem
27268  * @extends Roo.Toolbar.Item
27269  * A simple class that renders text directly into a toolbar.
27270  * @constructor
27271  * Creates a new TextItem
27272  * @param {String} text
27273  */
27274 Roo.Toolbar.TextItem = function(text){
27275     if (typeof(text) == 'object') {
27276         text = text.text;
27277     }
27278     var s = document.createElement("span");
27279     s.className = "ytb-text";
27280     s.innerHTML = text;
27281     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27282 };
27283 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27284     enable:Roo.emptyFn,
27285     disable:Roo.emptyFn,
27286     focus:Roo.emptyFn
27287 });
27288
27289 /**
27290  * @class Roo.Toolbar.Button
27291  * @extends Roo.Button
27292  * A button that renders into a toolbar.
27293  * @constructor
27294  * Creates a new Button
27295  * @param {Object} config A standard {@link Roo.Button} config object
27296  */
27297 Roo.Toolbar.Button = function(config){
27298     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27299 };
27300 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27301     render : function(td){
27302         this.td = td;
27303         Roo.Toolbar.Button.superclass.render.call(this, td);
27304     },
27305     
27306     /**
27307      * Removes and destroys this button
27308      */
27309     destroy : function(){
27310         Roo.Toolbar.Button.superclass.destroy.call(this);
27311         this.td.parentNode.removeChild(this.td);
27312     },
27313     
27314     /**
27315      * Shows this button
27316      */
27317     show: function(){
27318         this.hidden = false;
27319         this.td.style.display = "";
27320     },
27321     
27322     /**
27323      * Hides this button
27324      */
27325     hide: function(){
27326         this.hidden = true;
27327         this.td.style.display = "none";
27328     },
27329
27330     /**
27331      * Disables this item
27332      */
27333     disable : function(){
27334         Roo.fly(this.td).addClass("x-item-disabled");
27335         this.disabled = true;
27336     },
27337
27338     /**
27339      * Enables this item
27340      */
27341     enable : function(){
27342         Roo.fly(this.td).removeClass("x-item-disabled");
27343         this.disabled = false;
27344     }
27345 });
27346 // backwards compat
27347 Roo.ToolbarButton = Roo.Toolbar.Button;
27348
27349 /**
27350  * @class Roo.Toolbar.SplitButton
27351  * @extends Roo.SplitButton
27352  * A menu button that renders into a toolbar.
27353  * @constructor
27354  * Creates a new SplitButton
27355  * @param {Object} config A standard {@link Roo.SplitButton} config object
27356  */
27357 Roo.Toolbar.SplitButton = function(config){
27358     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27359 };
27360 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27361     render : function(td){
27362         this.td = td;
27363         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27364     },
27365     
27366     /**
27367      * Removes and destroys this button
27368      */
27369     destroy : function(){
27370         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27371         this.td.parentNode.removeChild(this.td);
27372     },
27373     
27374     /**
27375      * Shows this button
27376      */
27377     show: function(){
27378         this.hidden = false;
27379         this.td.style.display = "";
27380     },
27381     
27382     /**
27383      * Hides this button
27384      */
27385     hide: function(){
27386         this.hidden = true;
27387         this.td.style.display = "none";
27388     }
27389 });
27390
27391 // backwards compat
27392 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27393  * Based on:
27394  * Ext JS Library 1.1.1
27395  * Copyright(c) 2006-2007, Ext JS, LLC.
27396  *
27397  * Originally Released Under LGPL - original licence link has changed is not relivant.
27398  *
27399  * Fork - LGPL
27400  * <script type="text/javascript">
27401  */
27402  
27403 /**
27404  * @class Roo.PagingToolbar
27405  * @extends Roo.Toolbar
27406  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27407  * @constructor
27408  * Create a new PagingToolbar
27409  * @param {Object} config The config object
27410  */
27411 Roo.PagingToolbar = function(el, ds, config)
27412 {
27413     // old args format still supported... - xtype is prefered..
27414     if (typeof(el) == 'object' && el.xtype) {
27415         // created from xtype...
27416         config = el;
27417         ds = el.dataSource;
27418         el = config.container;
27419     }
27420     var items = [];
27421     if (config.items) {
27422         items = config.items;
27423         config.items = [];
27424     }
27425     
27426     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27427     this.ds = ds;
27428     this.cursor = 0;
27429     this.renderButtons(this.el);
27430     this.bind(ds);
27431     
27432     // supprot items array.
27433    
27434     Roo.each(items, function(e) {
27435         this.add(Roo.factory(e));
27436     },this);
27437     
27438 };
27439
27440 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27441     /**
27442      * @cfg {Roo.data.Store} dataSource
27443      * The underlying data store providing the paged data
27444      */
27445     /**
27446      * @cfg {String/HTMLElement/Element} container
27447      * container The id or element that will contain the toolbar
27448      */
27449     /**
27450      * @cfg {Boolean} displayInfo
27451      * True to display the displayMsg (defaults to false)
27452      */
27453     /**
27454      * @cfg {Number} pageSize
27455      * The number of records to display per page (defaults to 20)
27456      */
27457     pageSize: 20,
27458     /**
27459      * @cfg {String} displayMsg
27460      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27461      */
27462     displayMsg : 'Displaying {0} - {1} of {2}',
27463     /**
27464      * @cfg {String} emptyMsg
27465      * The message to display when no records are found (defaults to "No data to display")
27466      */
27467     emptyMsg : 'No data to display',
27468     /**
27469      * Customizable piece of the default paging text (defaults to "Page")
27470      * @type String
27471      */
27472     beforePageText : "Page",
27473     /**
27474      * Customizable piece of the default paging text (defaults to "of %0")
27475      * @type String
27476      */
27477     afterPageText : "of {0}",
27478     /**
27479      * Customizable piece of the default paging text (defaults to "First Page")
27480      * @type String
27481      */
27482     firstText : "First Page",
27483     /**
27484      * Customizable piece of the default paging text (defaults to "Previous Page")
27485      * @type String
27486      */
27487     prevText : "Previous Page",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "Next Page")
27490      * @type String
27491      */
27492     nextText : "Next Page",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Last Page")
27495      * @type String
27496      */
27497     lastText : "Last Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Refresh")
27500      * @type String
27501      */
27502     refreshText : "Refresh",
27503
27504     // private
27505     renderButtons : function(el){
27506         Roo.PagingToolbar.superclass.render.call(this, el);
27507         this.first = this.addButton({
27508             tooltip: this.firstText,
27509             cls: "x-btn-icon x-grid-page-first",
27510             disabled: true,
27511             handler: this.onClick.createDelegate(this, ["first"])
27512         });
27513         this.prev = this.addButton({
27514             tooltip: this.prevText,
27515             cls: "x-btn-icon x-grid-page-prev",
27516             disabled: true,
27517             handler: this.onClick.createDelegate(this, ["prev"])
27518         });
27519         //this.addSeparator();
27520         this.add(this.beforePageText);
27521         this.field = Roo.get(this.addDom({
27522            tag: "input",
27523            type: "text",
27524            size: "3",
27525            value: "1",
27526            cls: "x-grid-page-number"
27527         }).el);
27528         this.field.on("keydown", this.onPagingKeydown, this);
27529         this.field.on("focus", function(){this.dom.select();});
27530         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27531         this.field.setHeight(18);
27532         //this.addSeparator();
27533         this.next = this.addButton({
27534             tooltip: this.nextText,
27535             cls: "x-btn-icon x-grid-page-next",
27536             disabled: true,
27537             handler: this.onClick.createDelegate(this, ["next"])
27538         });
27539         this.last = this.addButton({
27540             tooltip: this.lastText,
27541             cls: "x-btn-icon x-grid-page-last",
27542             disabled: true,
27543             handler: this.onClick.createDelegate(this, ["last"])
27544         });
27545         //this.addSeparator();
27546         this.loading = this.addButton({
27547             tooltip: this.refreshText,
27548             cls: "x-btn-icon x-grid-loading",
27549             handler: this.onClick.createDelegate(this, ["refresh"])
27550         });
27551
27552         if(this.displayInfo){
27553             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27554         }
27555     },
27556
27557     // private
27558     updateInfo : function(){
27559         if(this.displayEl){
27560             var count = this.ds.getCount();
27561             var msg = count == 0 ?
27562                 this.emptyMsg :
27563                 String.format(
27564                     this.displayMsg,
27565                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27566                 );
27567             this.displayEl.update(msg);
27568         }
27569     },
27570
27571     // private
27572     onLoad : function(ds, r, o){
27573        this.cursor = o.params ? o.params.start : 0;
27574        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27575
27576        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27577        this.field.dom.value = ap;
27578        this.first.setDisabled(ap == 1);
27579        this.prev.setDisabled(ap == 1);
27580        this.next.setDisabled(ap == ps);
27581        this.last.setDisabled(ap == ps);
27582        this.loading.enable();
27583        this.updateInfo();
27584     },
27585
27586     // private
27587     getPageData : function(){
27588         var total = this.ds.getTotalCount();
27589         return {
27590             total : total,
27591             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27592             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27593         };
27594     },
27595
27596     // private
27597     onLoadError : function(){
27598         this.loading.enable();
27599     },
27600
27601     // private
27602     onPagingKeydown : function(e){
27603         var k = e.getKey();
27604         var d = this.getPageData();
27605         if(k == e.RETURN){
27606             var v = this.field.dom.value, pageNum;
27607             if(!v || isNaN(pageNum = parseInt(v, 10))){
27608                 this.field.dom.value = d.activePage;
27609                 return;
27610             }
27611             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27612             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27613             e.stopEvent();
27614         }
27615         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))
27616         {
27617           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27618           this.field.dom.value = pageNum;
27619           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27620           e.stopEvent();
27621         }
27622         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27623         {
27624           var v = this.field.dom.value, pageNum; 
27625           var increment = (e.shiftKey) ? 10 : 1;
27626           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27627             increment *= -1;
27628           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27629             this.field.dom.value = d.activePage;
27630             return;
27631           }
27632           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27633           {
27634             this.field.dom.value = parseInt(v, 10) + increment;
27635             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27636             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27637           }
27638           e.stopEvent();
27639         }
27640     },
27641
27642     // private
27643     beforeLoad : function(){
27644         if(this.loading){
27645             this.loading.disable();
27646         }
27647     },
27648
27649     // private
27650     onClick : function(which){
27651         var ds = this.ds;
27652         switch(which){
27653             case "first":
27654                 ds.load({params:{start: 0, limit: this.pageSize}});
27655             break;
27656             case "prev":
27657                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27658             break;
27659             case "next":
27660                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27661             break;
27662             case "last":
27663                 var total = ds.getTotalCount();
27664                 var extra = total % this.pageSize;
27665                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27666                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27667             break;
27668             case "refresh":
27669                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27670             break;
27671         }
27672     },
27673
27674     /**
27675      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27676      * @param {Roo.data.Store} store The data store to unbind
27677      */
27678     unbind : function(ds){
27679         ds.un("beforeload", this.beforeLoad, this);
27680         ds.un("load", this.onLoad, this);
27681         ds.un("loadexception", this.onLoadError, this);
27682         ds.un("remove", this.updateInfo, this);
27683         ds.un("add", this.updateInfo, this);
27684         this.ds = undefined;
27685     },
27686
27687     /**
27688      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27689      * @param {Roo.data.Store} store The data store to bind
27690      */
27691     bind : function(ds){
27692         ds.on("beforeload", this.beforeLoad, this);
27693         ds.on("load", this.onLoad, this);
27694         ds.on("loadexception", this.onLoadError, this);
27695         ds.on("remove", this.updateInfo, this);
27696         ds.on("add", this.updateInfo, this);
27697         this.ds = ds;
27698     }
27699 });/*
27700  * Based on:
27701  * Ext JS Library 1.1.1
27702  * Copyright(c) 2006-2007, Ext JS, LLC.
27703  *
27704  * Originally Released Under LGPL - original licence link has changed is not relivant.
27705  *
27706  * Fork - LGPL
27707  * <script type="text/javascript">
27708  */
27709
27710 /**
27711  * @class Roo.Resizable
27712  * @extends Roo.util.Observable
27713  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27714  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27715  * 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
27716  * the element will be wrapped for you automatically.</p>
27717  * <p>Here is the list of valid resize handles:</p>
27718  * <pre>
27719 Value   Description
27720 ------  -------------------
27721  'n'     north
27722  's'     south
27723  'e'     east
27724  'w'     west
27725  'nw'    northwest
27726  'sw'    southwest
27727  'se'    southeast
27728  'ne'    northeast
27729  'hd'    horizontal drag
27730  'all'   all
27731 </pre>
27732  * <p>Here's an example showing the creation of a typical Resizable:</p>
27733  * <pre><code>
27734 var resizer = new Roo.Resizable("element-id", {
27735     handles: 'all',
27736     minWidth: 200,
27737     minHeight: 100,
27738     maxWidth: 500,
27739     maxHeight: 400,
27740     pinned: true
27741 });
27742 resizer.on("resize", myHandler);
27743 </code></pre>
27744  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27745  * resizer.east.setDisplayed(false);</p>
27746  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27747  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27748  * resize operation's new size (defaults to [0, 0])
27749  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27750  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27751  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27752  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27753  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27754  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27755  * @cfg {Number} width The width of the element in pixels (defaults to null)
27756  * @cfg {Number} height The height of the element in pixels (defaults to null)
27757  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27758  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27759  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27760  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27761  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27762  * in favor of the handles config option (defaults to false)
27763  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27764  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27765  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27766  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27767  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27768  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27769  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27770  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27771  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27772  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27773  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27774  * @constructor
27775  * Create a new resizable component
27776  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27777  * @param {Object} config configuration options
27778   */
27779 Roo.Resizable = function(el, config)
27780 {
27781     this.el = Roo.get(el);
27782
27783     if(config && config.wrap){
27784         config.resizeChild = this.el;
27785         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27786         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27787         this.el.setStyle("overflow", "hidden");
27788         this.el.setPositioning(config.resizeChild.getPositioning());
27789         config.resizeChild.clearPositioning();
27790         if(!config.width || !config.height){
27791             var csize = config.resizeChild.getSize();
27792             this.el.setSize(csize.width, csize.height);
27793         }
27794         if(config.pinned && !config.adjustments){
27795             config.adjustments = "auto";
27796         }
27797     }
27798
27799     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27800     this.proxy.unselectable();
27801     this.proxy.enableDisplayMode('block');
27802
27803     Roo.apply(this, config);
27804
27805     if(this.pinned){
27806         this.disableTrackOver = true;
27807         this.el.addClass("x-resizable-pinned");
27808     }
27809     // if the element isn't positioned, make it relative
27810     var position = this.el.getStyle("position");
27811     if(position != "absolute" && position != "fixed"){
27812         this.el.setStyle("position", "relative");
27813     }
27814     if(!this.handles){ // no handles passed, must be legacy style
27815         this.handles = 's,e,se';
27816         if(this.multiDirectional){
27817             this.handles += ',n,w';
27818         }
27819     }
27820     if(this.handles == "all"){
27821         this.handles = "n s e w ne nw se sw";
27822     }
27823     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27824     var ps = Roo.Resizable.positions;
27825     for(var i = 0, len = hs.length; i < len; i++){
27826         if(hs[i] && ps[hs[i]]){
27827             var pos = ps[hs[i]];
27828             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27829         }
27830     }
27831     // legacy
27832     this.corner = this.southeast;
27833     
27834     // updateBox = the box can move..
27835     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27836         this.updateBox = true;
27837     }
27838
27839     this.activeHandle = null;
27840
27841     if(this.resizeChild){
27842         if(typeof this.resizeChild == "boolean"){
27843             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27844         }else{
27845             this.resizeChild = Roo.get(this.resizeChild, true);
27846         }
27847     }
27848     
27849     if(this.adjustments == "auto"){
27850         var rc = this.resizeChild;
27851         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27852         if(rc && (hw || hn)){
27853             rc.position("relative");
27854             rc.setLeft(hw ? hw.el.getWidth() : 0);
27855             rc.setTop(hn ? hn.el.getHeight() : 0);
27856         }
27857         this.adjustments = [
27858             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27859             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27860         ];
27861     }
27862
27863     if(this.draggable){
27864         this.dd = this.dynamic ?
27865             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27866         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27867     }
27868
27869     // public events
27870     this.addEvents({
27871         /**
27872          * @event beforeresize
27873          * Fired before resize is allowed. Set enabled to false to cancel resize.
27874          * @param {Roo.Resizable} this
27875          * @param {Roo.EventObject} e The mousedown event
27876          */
27877         "beforeresize" : true,
27878         /**
27879          * @event resize
27880          * Fired after a resize.
27881          * @param {Roo.Resizable} this
27882          * @param {Number} width The new width
27883          * @param {Number} height The new height
27884          * @param {Roo.EventObject} e The mouseup event
27885          */
27886         "resize" : true
27887     });
27888
27889     if(this.width !== null && this.height !== null){
27890         this.resizeTo(this.width, this.height);
27891     }else{
27892         this.updateChildSize();
27893     }
27894     if(Roo.isIE){
27895         this.el.dom.style.zoom = 1;
27896     }
27897     Roo.Resizable.superclass.constructor.call(this);
27898 };
27899
27900 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27901         resizeChild : false,
27902         adjustments : [0, 0],
27903         minWidth : 5,
27904         minHeight : 5,
27905         maxWidth : 10000,
27906         maxHeight : 10000,
27907         enabled : true,
27908         animate : false,
27909         duration : .35,
27910         dynamic : false,
27911         handles : false,
27912         multiDirectional : false,
27913         disableTrackOver : false,
27914         easing : 'easeOutStrong',
27915         widthIncrement : 0,
27916         heightIncrement : 0,
27917         pinned : false,
27918         width : null,
27919         height : null,
27920         preserveRatio : false,
27921         transparent: false,
27922         minX: 0,
27923         minY: 0,
27924         draggable: false,
27925
27926         /**
27927          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27928          */
27929         constrainTo: undefined,
27930         /**
27931          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27932          */
27933         resizeRegion: undefined,
27934
27935
27936     /**
27937      * Perform a manual resize
27938      * @param {Number} width
27939      * @param {Number} height
27940      */
27941     resizeTo : function(width, height){
27942         this.el.setSize(width, height);
27943         this.updateChildSize();
27944         this.fireEvent("resize", this, width, height, null);
27945     },
27946
27947     // private
27948     startSizing : function(e, handle){
27949         this.fireEvent("beforeresize", this, e);
27950         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27951
27952             if(!this.overlay){
27953                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27954                 this.overlay.unselectable();
27955                 this.overlay.enableDisplayMode("block");
27956                 this.overlay.on("mousemove", this.onMouseMove, this);
27957                 this.overlay.on("mouseup", this.onMouseUp, this);
27958             }
27959             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27960
27961             this.resizing = true;
27962             this.startBox = this.el.getBox();
27963             this.startPoint = e.getXY();
27964             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27965                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27966
27967             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27968             this.overlay.show();
27969
27970             if(this.constrainTo) {
27971                 var ct = Roo.get(this.constrainTo);
27972                 this.resizeRegion = ct.getRegion().adjust(
27973                     ct.getFrameWidth('t'),
27974                     ct.getFrameWidth('l'),
27975                     -ct.getFrameWidth('b'),
27976                     -ct.getFrameWidth('r')
27977                 );
27978             }
27979
27980             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27981             this.proxy.show();
27982             this.proxy.setBox(this.startBox);
27983             if(!this.dynamic){
27984                 this.proxy.setStyle('visibility', 'visible');
27985             }
27986         }
27987     },
27988
27989     // private
27990     onMouseDown : function(handle, e){
27991         if(this.enabled){
27992             e.stopEvent();
27993             this.activeHandle = handle;
27994             this.startSizing(e, handle);
27995         }
27996     },
27997
27998     // private
27999     onMouseUp : function(e){
28000         var size = this.resizeElement();
28001         this.resizing = false;
28002         this.handleOut();
28003         this.overlay.hide();
28004         this.proxy.hide();
28005         this.fireEvent("resize", this, size.width, size.height, e);
28006     },
28007
28008     // private
28009     updateChildSize : function(){
28010         if(this.resizeChild){
28011             var el = this.el;
28012             var child = this.resizeChild;
28013             var adj = this.adjustments;
28014             if(el.dom.offsetWidth){
28015                 var b = el.getSize(true);
28016                 child.setSize(b.width+adj[0], b.height+adj[1]);
28017             }
28018             // Second call here for IE
28019             // The first call enables instant resizing and
28020             // the second call corrects scroll bars if they
28021             // exist
28022             if(Roo.isIE){
28023                 setTimeout(function(){
28024                     if(el.dom.offsetWidth){
28025                         var b = el.getSize(true);
28026                         child.setSize(b.width+adj[0], b.height+adj[1]);
28027                     }
28028                 }, 10);
28029             }
28030         }
28031     },
28032
28033     // private
28034     snap : function(value, inc, min){
28035         if(!inc || !value) return value;
28036         var newValue = value;
28037         var m = value % inc;
28038         if(m > 0){
28039             if(m > (inc/2)){
28040                 newValue = value + (inc-m);
28041             }else{
28042                 newValue = value - m;
28043             }
28044         }
28045         return Math.max(min, newValue);
28046     },
28047
28048     // private
28049     resizeElement : function(){
28050         var box = this.proxy.getBox();
28051         if(this.updateBox){
28052             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28053         }else{
28054             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28055         }
28056         this.updateChildSize();
28057         if(!this.dynamic){
28058             this.proxy.hide();
28059         }
28060         return box;
28061     },
28062
28063     // private
28064     constrain : function(v, diff, m, mx){
28065         if(v - diff < m){
28066             diff = v - m;
28067         }else if(v - diff > mx){
28068             diff = mx - v;
28069         }
28070         return diff;
28071     },
28072
28073     // private
28074     onMouseMove : function(e){
28075         if(this.enabled){
28076             try{// try catch so if something goes wrong the user doesn't get hung
28077
28078             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28079                 return;
28080             }
28081
28082             //var curXY = this.startPoint;
28083             var curSize = this.curSize || this.startBox;
28084             var x = this.startBox.x, y = this.startBox.y;
28085             var ox = x, oy = y;
28086             var w = curSize.width, h = curSize.height;
28087             var ow = w, oh = h;
28088             var mw = this.minWidth, mh = this.minHeight;
28089             var mxw = this.maxWidth, mxh = this.maxHeight;
28090             var wi = this.widthIncrement;
28091             var hi = this.heightIncrement;
28092
28093             var eventXY = e.getXY();
28094             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28095             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28096
28097             var pos = this.activeHandle.position;
28098
28099             switch(pos){
28100                 case "east":
28101                     w += diffX;
28102                     w = Math.min(Math.max(mw, w), mxw);
28103                     break;
28104              
28105                 case "south":
28106                     h += diffY;
28107                     h = Math.min(Math.max(mh, h), mxh);
28108                     break;
28109                 case "southeast":
28110                     w += diffX;
28111                     h += diffY;
28112                     w = Math.min(Math.max(mw, w), mxw);
28113                     h = Math.min(Math.max(mh, h), mxh);
28114                     break;
28115                 case "north":
28116                     diffY = this.constrain(h, diffY, mh, mxh);
28117                     y += diffY;
28118                     h -= diffY;
28119                     break;
28120                 case "hdrag":
28121                     
28122                     if (wi) {
28123                         var adiffX = Math.abs(diffX);
28124                         var sub = (adiffX % wi); // how much 
28125                         if (sub > (wi/2)) { // far enough to snap
28126                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28127                         } else {
28128                             // remove difference.. 
28129                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28130                         }
28131                     }
28132                     x += diffX;
28133                     x = Math.max(this.minX, x);
28134                     break;
28135                 case "west":
28136                     diffX = this.constrain(w, diffX, mw, mxw);
28137                     x += diffX;
28138                     w -= diffX;
28139                     break;
28140                 case "northeast":
28141                     w += diffX;
28142                     w = Math.min(Math.max(mw, w), mxw);
28143                     diffY = this.constrain(h, diffY, mh, mxh);
28144                     y += diffY;
28145                     h -= diffY;
28146                     break;
28147                 case "northwest":
28148                     diffX = this.constrain(w, diffX, mw, mxw);
28149                     diffY = this.constrain(h, diffY, mh, mxh);
28150                     y += diffY;
28151                     h -= diffY;
28152                     x += diffX;
28153                     w -= diffX;
28154                     break;
28155                case "southwest":
28156                     diffX = this.constrain(w, diffX, mw, mxw);
28157                     h += diffY;
28158                     h = Math.min(Math.max(mh, h), mxh);
28159                     x += diffX;
28160                     w -= diffX;
28161                     break;
28162             }
28163
28164             var sw = this.snap(w, wi, mw);
28165             var sh = this.snap(h, hi, mh);
28166             if(sw != w || sh != h){
28167                 switch(pos){
28168                     case "northeast":
28169                         y -= sh - h;
28170                     break;
28171                     case "north":
28172                         y -= sh - h;
28173                         break;
28174                     case "southwest":
28175                         x -= sw - w;
28176                     break;
28177                     case "west":
28178                         x -= sw - w;
28179                         break;
28180                     case "northwest":
28181                         x -= sw - w;
28182                         y -= sh - h;
28183                     break;
28184                 }
28185                 w = sw;
28186                 h = sh;
28187             }
28188
28189             if(this.preserveRatio){
28190                 switch(pos){
28191                     case "southeast":
28192                     case "east":
28193                         h = oh * (w/ow);
28194                         h = Math.min(Math.max(mh, h), mxh);
28195                         w = ow * (h/oh);
28196                        break;
28197                     case "south":
28198                         w = ow * (h/oh);
28199                         w = Math.min(Math.max(mw, w), mxw);
28200                         h = oh * (w/ow);
28201                         break;
28202                     case "northeast":
28203                         w = ow * (h/oh);
28204                         w = Math.min(Math.max(mw, w), mxw);
28205                         h = oh * (w/ow);
28206                     break;
28207                     case "north":
28208                         var tw = w;
28209                         w = ow * (h/oh);
28210                         w = Math.min(Math.max(mw, w), mxw);
28211                         h = oh * (w/ow);
28212                         x += (tw - w) / 2;
28213                         break;
28214                     case "southwest":
28215                         h = oh * (w/ow);
28216                         h = Math.min(Math.max(mh, h), mxh);
28217                         var tw = w;
28218                         w = ow * (h/oh);
28219                         x += tw - w;
28220                         break;
28221                     case "west":
28222                         var th = h;
28223                         h = oh * (w/ow);
28224                         h = Math.min(Math.max(mh, h), mxh);
28225                         y += (th - h) / 2;
28226                         var tw = w;
28227                         w = ow * (h/oh);
28228                         x += tw - w;
28229                        break;
28230                     case "northwest":
28231                         var tw = w;
28232                         var th = h;
28233                         h = oh * (w/ow);
28234                         h = Math.min(Math.max(mh, h), mxh);
28235                         w = ow * (h/oh);
28236                         y += th - h;
28237                         x += tw - w;
28238                        break;
28239
28240                 }
28241             }
28242             if (pos == 'hdrag') {
28243                 w = ow;
28244             }
28245             this.proxy.setBounds(x, y, w, h);
28246             if(this.dynamic){
28247                 this.resizeElement();
28248             }
28249             }catch(e){}
28250         }
28251     },
28252
28253     // private
28254     handleOver : function(){
28255         if(this.enabled){
28256             this.el.addClass("x-resizable-over");
28257         }
28258     },
28259
28260     // private
28261     handleOut : function(){
28262         if(!this.resizing){
28263             this.el.removeClass("x-resizable-over");
28264         }
28265     },
28266
28267     /**
28268      * Returns the element this component is bound to.
28269      * @return {Roo.Element}
28270      */
28271     getEl : function(){
28272         return this.el;
28273     },
28274
28275     /**
28276      * Returns the resizeChild element (or null).
28277      * @return {Roo.Element}
28278      */
28279     getResizeChild : function(){
28280         return this.resizeChild;
28281     },
28282
28283     /**
28284      * Destroys this resizable. If the element was wrapped and
28285      * removeEl is not true then the element remains.
28286      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28287      */
28288     destroy : function(removeEl){
28289         this.proxy.remove();
28290         if(this.overlay){
28291             this.overlay.removeAllListeners();
28292             this.overlay.remove();
28293         }
28294         var ps = Roo.Resizable.positions;
28295         for(var k in ps){
28296             if(typeof ps[k] != "function" && this[ps[k]]){
28297                 var h = this[ps[k]];
28298                 h.el.removeAllListeners();
28299                 h.el.remove();
28300             }
28301         }
28302         if(removeEl){
28303             this.el.update("");
28304             this.el.remove();
28305         }
28306     }
28307 });
28308
28309 // private
28310 // hash to map config positions to true positions
28311 Roo.Resizable.positions = {
28312     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28313     hd: "hdrag"
28314 };
28315
28316 // private
28317 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28318     if(!this.tpl){
28319         // only initialize the template if resizable is used
28320         var tpl = Roo.DomHelper.createTemplate(
28321             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28322         );
28323         tpl.compile();
28324         Roo.Resizable.Handle.prototype.tpl = tpl;
28325     }
28326     this.position = pos;
28327     this.rz = rz;
28328     // show north drag fro topdra
28329     var handlepos = pos == 'hdrag' ? 'north' : pos;
28330     
28331     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28332     if (pos == 'hdrag') {
28333         this.el.setStyle('cursor', 'pointer');
28334     }
28335     this.el.unselectable();
28336     if(transparent){
28337         this.el.setOpacity(0);
28338     }
28339     this.el.on("mousedown", this.onMouseDown, this);
28340     if(!disableTrackOver){
28341         this.el.on("mouseover", this.onMouseOver, this);
28342         this.el.on("mouseout", this.onMouseOut, this);
28343     }
28344 };
28345
28346 // private
28347 Roo.Resizable.Handle.prototype = {
28348     afterResize : function(rz){
28349         // do nothing
28350     },
28351     // private
28352     onMouseDown : function(e){
28353         this.rz.onMouseDown(this, e);
28354     },
28355     // private
28356     onMouseOver : function(e){
28357         this.rz.handleOver(this, e);
28358     },
28359     // private
28360     onMouseOut : function(e){
28361         this.rz.handleOut(this, e);
28362     }
28363 };/*
28364  * Based on:
28365  * Ext JS Library 1.1.1
28366  * Copyright(c) 2006-2007, Ext JS, LLC.
28367  *
28368  * Originally Released Under LGPL - original licence link has changed is not relivant.
28369  *
28370  * Fork - LGPL
28371  * <script type="text/javascript">
28372  */
28373
28374 /**
28375  * @class Roo.Editor
28376  * @extends Roo.Component
28377  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28378  * @constructor
28379  * Create a new Editor
28380  * @param {Roo.form.Field} field The Field object (or descendant)
28381  * @param {Object} config The config object
28382  */
28383 Roo.Editor = function(field, config){
28384     Roo.Editor.superclass.constructor.call(this, config);
28385     this.field = field;
28386     this.addEvents({
28387         /**
28388              * @event beforestartedit
28389              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28390              * false from the handler of this event.
28391              * @param {Editor} this
28392              * @param {Roo.Element} boundEl The underlying element bound to this editor
28393              * @param {Mixed} value The field value being set
28394              */
28395         "beforestartedit" : true,
28396         /**
28397              * @event startedit
28398              * Fires when this editor is displayed
28399              * @param {Roo.Element} boundEl The underlying element bound to this editor
28400              * @param {Mixed} value The starting field value
28401              */
28402         "startedit" : true,
28403         /**
28404              * @event beforecomplete
28405              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28406              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28407              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28408              * event will not fire since no edit actually occurred.
28409              * @param {Editor} this
28410              * @param {Mixed} value The current field value
28411              * @param {Mixed} startValue The original field value
28412              */
28413         "beforecomplete" : true,
28414         /**
28415              * @event complete
28416              * Fires after editing is complete and any changed value has been written to the underlying field.
28417              * @param {Editor} this
28418              * @param {Mixed} value The current field value
28419              * @param {Mixed} startValue The original field value
28420              */
28421         "complete" : true,
28422         /**
28423          * @event specialkey
28424          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28425          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28426          * @param {Roo.form.Field} this
28427          * @param {Roo.EventObject} e The event object
28428          */
28429         "specialkey" : true
28430     });
28431 };
28432
28433 Roo.extend(Roo.Editor, Roo.Component, {
28434     /**
28435      * @cfg {Boolean/String} autosize
28436      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28437      * or "height" to adopt the height only (defaults to false)
28438      */
28439     /**
28440      * @cfg {Boolean} revertInvalid
28441      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28442      * validation fails (defaults to true)
28443      */
28444     /**
28445      * @cfg {Boolean} ignoreNoChange
28446      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28447      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28448      * will never be ignored.
28449      */
28450     /**
28451      * @cfg {Boolean} hideEl
28452      * False to keep the bound element visible while the editor is displayed (defaults to true)
28453      */
28454     /**
28455      * @cfg {Mixed} value
28456      * The data value of the underlying field (defaults to "")
28457      */
28458     value : "",
28459     /**
28460      * @cfg {String} alignment
28461      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28462      */
28463     alignment: "c-c?",
28464     /**
28465      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28466      * for bottom-right shadow (defaults to "frame")
28467      */
28468     shadow : "frame",
28469     /**
28470      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28471      */
28472     constrain : false,
28473     /**
28474      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28475      */
28476     completeOnEnter : false,
28477     /**
28478      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28479      */
28480     cancelOnEsc : false,
28481     /**
28482      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28483      */
28484     updateEl : false,
28485
28486     // private
28487     onRender : function(ct, position){
28488         this.el = new Roo.Layer({
28489             shadow: this.shadow,
28490             cls: "x-editor",
28491             parentEl : ct,
28492             shim : this.shim,
28493             shadowOffset:4,
28494             id: this.id,
28495             constrain: this.constrain
28496         });
28497         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28498         if(this.field.msgTarget != 'title'){
28499             this.field.msgTarget = 'qtip';
28500         }
28501         this.field.render(this.el);
28502         if(Roo.isGecko){
28503             this.field.el.dom.setAttribute('autocomplete', 'off');
28504         }
28505         this.field.on("specialkey", this.onSpecialKey, this);
28506         if(this.swallowKeys){
28507             this.field.el.swallowEvent(['keydown','keypress']);
28508         }
28509         this.field.show();
28510         this.field.on("blur", this.onBlur, this);
28511         if(this.field.grow){
28512             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28513         }
28514     },
28515
28516     onSpecialKey : function(field, e)
28517     {
28518         //Roo.log('editor onSpecialKey');
28519         if(this.completeOnEnter && e.getKey() == e.ENTER){
28520             e.stopEvent();
28521             this.completeEdit();
28522             return;
28523         }
28524         // do not fire special key otherwise it might hide close the editor...
28525         if(e.getKey() == e.ENTER){    
28526             return;
28527         }
28528         if(this.cancelOnEsc && e.getKey() == e.ESC){
28529             this.cancelEdit();
28530             return;
28531         } 
28532         this.fireEvent('specialkey', field, e);
28533     
28534     },
28535
28536     /**
28537      * Starts the editing process and shows the editor.
28538      * @param {String/HTMLElement/Element} el The element to edit
28539      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28540       * to the innerHTML of el.
28541      */
28542     startEdit : function(el, value){
28543         if(this.editing){
28544             this.completeEdit();
28545         }
28546         this.boundEl = Roo.get(el);
28547         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28548         if(!this.rendered){
28549             this.render(this.parentEl || document.body);
28550         }
28551         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28552             return;
28553         }
28554         this.startValue = v;
28555         this.field.setValue(v);
28556         if(this.autoSize){
28557             var sz = this.boundEl.getSize();
28558             switch(this.autoSize){
28559                 case "width":
28560                 this.setSize(sz.width,  "");
28561                 break;
28562                 case "height":
28563                 this.setSize("",  sz.height);
28564                 break;
28565                 default:
28566                 this.setSize(sz.width,  sz.height);
28567             }
28568         }
28569         this.el.alignTo(this.boundEl, this.alignment);
28570         this.editing = true;
28571         if(Roo.QuickTips){
28572             Roo.QuickTips.disable();
28573         }
28574         this.show();
28575     },
28576
28577     /**
28578      * Sets the height and width of this editor.
28579      * @param {Number} width The new width
28580      * @param {Number} height The new height
28581      */
28582     setSize : function(w, h){
28583         this.field.setSize(w, h);
28584         if(this.el){
28585             this.el.sync();
28586         }
28587     },
28588
28589     /**
28590      * Realigns the editor to the bound field based on the current alignment config value.
28591      */
28592     realign : function(){
28593         this.el.alignTo(this.boundEl, this.alignment);
28594     },
28595
28596     /**
28597      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28598      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28599      */
28600     completeEdit : function(remainVisible){
28601         if(!this.editing){
28602             return;
28603         }
28604         var v = this.getValue();
28605         if(this.revertInvalid !== false && !this.field.isValid()){
28606             v = this.startValue;
28607             this.cancelEdit(true);
28608         }
28609         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28610             this.editing = false;
28611             this.hide();
28612             return;
28613         }
28614         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28615             this.editing = false;
28616             if(this.updateEl && this.boundEl){
28617                 this.boundEl.update(v);
28618             }
28619             if(remainVisible !== true){
28620                 this.hide();
28621             }
28622             this.fireEvent("complete", this, v, this.startValue);
28623         }
28624     },
28625
28626     // private
28627     onShow : function(){
28628         this.el.show();
28629         if(this.hideEl !== false){
28630             this.boundEl.hide();
28631         }
28632         this.field.show();
28633         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28634             this.fixIEFocus = true;
28635             this.deferredFocus.defer(50, this);
28636         }else{
28637             this.field.focus();
28638         }
28639         this.fireEvent("startedit", this.boundEl, this.startValue);
28640     },
28641
28642     deferredFocus : function(){
28643         if(this.editing){
28644             this.field.focus();
28645         }
28646     },
28647
28648     /**
28649      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28650      * reverted to the original starting value.
28651      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28652      * cancel (defaults to false)
28653      */
28654     cancelEdit : function(remainVisible){
28655         if(this.editing){
28656             this.setValue(this.startValue);
28657             if(remainVisible !== true){
28658                 this.hide();
28659             }
28660         }
28661     },
28662
28663     // private
28664     onBlur : function(){
28665         if(this.allowBlur !== true && this.editing){
28666             this.completeEdit();
28667         }
28668     },
28669
28670     // private
28671     onHide : function(){
28672         if(this.editing){
28673             this.completeEdit();
28674             return;
28675         }
28676         this.field.blur();
28677         if(this.field.collapse){
28678             this.field.collapse();
28679         }
28680         this.el.hide();
28681         if(this.hideEl !== false){
28682             this.boundEl.show();
28683         }
28684         if(Roo.QuickTips){
28685             Roo.QuickTips.enable();
28686         }
28687     },
28688
28689     /**
28690      * Sets the data value of the editor
28691      * @param {Mixed} value Any valid value supported by the underlying field
28692      */
28693     setValue : function(v){
28694         this.field.setValue(v);
28695     },
28696
28697     /**
28698      * Gets the data value of the editor
28699      * @return {Mixed} The data value
28700      */
28701     getValue : function(){
28702         return this.field.getValue();
28703     }
28704 });/*
28705  * Based on:
28706  * Ext JS Library 1.1.1
28707  * Copyright(c) 2006-2007, Ext JS, LLC.
28708  *
28709  * Originally Released Under LGPL - original licence link has changed is not relivant.
28710  *
28711  * Fork - LGPL
28712  * <script type="text/javascript">
28713  */
28714  
28715 /**
28716  * @class Roo.BasicDialog
28717  * @extends Roo.util.Observable
28718  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28719  * <pre><code>
28720 var dlg = new Roo.BasicDialog("my-dlg", {
28721     height: 200,
28722     width: 300,
28723     minHeight: 100,
28724     minWidth: 150,
28725     modal: true,
28726     proxyDrag: true,
28727     shadow: true
28728 });
28729 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28730 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28731 dlg.addButton('Cancel', dlg.hide, dlg);
28732 dlg.show();
28733 </code></pre>
28734   <b>A Dialog should always be a direct child of the body element.</b>
28735  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28736  * @cfg {String} title Default text to display in the title bar (defaults to null)
28737  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28738  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28739  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28740  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28741  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28742  * (defaults to null with no animation)
28743  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28744  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28745  * property for valid values (defaults to 'all')
28746  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28747  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28748  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28749  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28750  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28751  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28752  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28753  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28754  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28755  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28756  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28757  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28758  * draggable = true (defaults to false)
28759  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28760  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28761  * shadow (defaults to false)
28762  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28763  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28764  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28765  * @cfg {Array} buttons Array of buttons
28766  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28767  * @constructor
28768  * Create a new BasicDialog.
28769  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28770  * @param {Object} config Configuration options
28771  */
28772 Roo.BasicDialog = function(el, config){
28773     this.el = Roo.get(el);
28774     var dh = Roo.DomHelper;
28775     if(!this.el && config && config.autoCreate){
28776         if(typeof config.autoCreate == "object"){
28777             if(!config.autoCreate.id){
28778                 config.autoCreate.id = el;
28779             }
28780             this.el = dh.append(document.body,
28781                         config.autoCreate, true);
28782         }else{
28783             this.el = dh.append(document.body,
28784                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28785         }
28786     }
28787     el = this.el;
28788     el.setDisplayed(true);
28789     el.hide = this.hideAction;
28790     this.id = el.id;
28791     el.addClass("x-dlg");
28792
28793     Roo.apply(this, config);
28794
28795     this.proxy = el.createProxy("x-dlg-proxy");
28796     this.proxy.hide = this.hideAction;
28797     this.proxy.setOpacity(.5);
28798     this.proxy.hide();
28799
28800     if(config.width){
28801         el.setWidth(config.width);
28802     }
28803     if(config.height){
28804         el.setHeight(config.height);
28805     }
28806     this.size = el.getSize();
28807     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28808         this.xy = [config.x,config.y];
28809     }else{
28810         this.xy = el.getCenterXY(true);
28811     }
28812     /** The header element @type Roo.Element */
28813     this.header = el.child("> .x-dlg-hd");
28814     /** The body element @type Roo.Element */
28815     this.body = el.child("> .x-dlg-bd");
28816     /** The footer element @type Roo.Element */
28817     this.footer = el.child("> .x-dlg-ft");
28818
28819     if(!this.header){
28820         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28821     }
28822     if(!this.body){
28823         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28824     }
28825
28826     this.header.unselectable();
28827     if(this.title){
28828         this.header.update(this.title);
28829     }
28830     // this element allows the dialog to be focused for keyboard event
28831     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28832     this.focusEl.swallowEvent("click", true);
28833
28834     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28835
28836     // wrap the body and footer for special rendering
28837     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28838     if(this.footer){
28839         this.bwrap.dom.appendChild(this.footer.dom);
28840     }
28841
28842     this.bg = this.el.createChild({
28843         tag: "div", cls:"x-dlg-bg",
28844         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28845     });
28846     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28847
28848
28849     if(this.autoScroll !== false && !this.autoTabs){
28850         this.body.setStyle("overflow", "auto");
28851     }
28852
28853     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28854
28855     if(this.closable !== false){
28856         this.el.addClass("x-dlg-closable");
28857         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28858         this.close.on("click", this.closeClick, this);
28859         this.close.addClassOnOver("x-dlg-close-over");
28860     }
28861     if(this.collapsible !== false){
28862         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28863         this.collapseBtn.on("click", this.collapseClick, this);
28864         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28865         this.header.on("dblclick", this.collapseClick, this);
28866     }
28867     if(this.resizable !== false){
28868         this.el.addClass("x-dlg-resizable");
28869         this.resizer = new Roo.Resizable(el, {
28870             minWidth: this.minWidth || 80,
28871             minHeight:this.minHeight || 80,
28872             handles: this.resizeHandles || "all",
28873             pinned: true
28874         });
28875         this.resizer.on("beforeresize", this.beforeResize, this);
28876         this.resizer.on("resize", this.onResize, this);
28877     }
28878     if(this.draggable !== false){
28879         el.addClass("x-dlg-draggable");
28880         if (!this.proxyDrag) {
28881             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28882         }
28883         else {
28884             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28885         }
28886         dd.setHandleElId(this.header.id);
28887         dd.endDrag = this.endMove.createDelegate(this);
28888         dd.startDrag = this.startMove.createDelegate(this);
28889         dd.onDrag = this.onDrag.createDelegate(this);
28890         dd.scroll = false;
28891         this.dd = dd;
28892     }
28893     if(this.modal){
28894         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28895         this.mask.enableDisplayMode("block");
28896         this.mask.hide();
28897         this.el.addClass("x-dlg-modal");
28898     }
28899     if(this.shadow){
28900         this.shadow = new Roo.Shadow({
28901             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28902             offset : this.shadowOffset
28903         });
28904     }else{
28905         this.shadowOffset = 0;
28906     }
28907     if(Roo.useShims && this.shim !== false){
28908         this.shim = this.el.createShim();
28909         this.shim.hide = this.hideAction;
28910         this.shim.hide();
28911     }else{
28912         this.shim = false;
28913     }
28914     if(this.autoTabs){
28915         this.initTabs();
28916     }
28917     if (this.buttons) { 
28918         var bts= this.buttons;
28919         this.buttons = [];
28920         Roo.each(bts, function(b) {
28921             this.addButton(b);
28922         }, this);
28923     }
28924     
28925     
28926     this.addEvents({
28927         /**
28928          * @event keydown
28929          * Fires when a key is pressed
28930          * @param {Roo.BasicDialog} this
28931          * @param {Roo.EventObject} e
28932          */
28933         "keydown" : true,
28934         /**
28935          * @event move
28936          * Fires when this dialog is moved by the user.
28937          * @param {Roo.BasicDialog} this
28938          * @param {Number} x The new page X
28939          * @param {Number} y The new page Y
28940          */
28941         "move" : true,
28942         /**
28943          * @event resize
28944          * Fires when this dialog is resized by the user.
28945          * @param {Roo.BasicDialog} this
28946          * @param {Number} width The new width
28947          * @param {Number} height The new height
28948          */
28949         "resize" : true,
28950         /**
28951          * @event beforehide
28952          * Fires before this dialog is hidden.
28953          * @param {Roo.BasicDialog} this
28954          */
28955         "beforehide" : true,
28956         /**
28957          * @event hide
28958          * Fires when this dialog is hidden.
28959          * @param {Roo.BasicDialog} this
28960          */
28961         "hide" : true,
28962         /**
28963          * @event beforeshow
28964          * Fires before this dialog is shown.
28965          * @param {Roo.BasicDialog} this
28966          */
28967         "beforeshow" : true,
28968         /**
28969          * @event show
28970          * Fires when this dialog is shown.
28971          * @param {Roo.BasicDialog} this
28972          */
28973         "show" : true
28974     });
28975     el.on("keydown", this.onKeyDown, this);
28976     el.on("mousedown", this.toFront, this);
28977     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28978     this.el.hide();
28979     Roo.DialogManager.register(this);
28980     Roo.BasicDialog.superclass.constructor.call(this);
28981 };
28982
28983 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28984     shadowOffset: Roo.isIE ? 6 : 5,
28985     minHeight: 80,
28986     minWidth: 200,
28987     minButtonWidth: 75,
28988     defaultButton: null,
28989     buttonAlign: "right",
28990     tabTag: 'div',
28991     firstShow: true,
28992
28993     /**
28994      * Sets the dialog title text
28995      * @param {String} text The title text to display
28996      * @return {Roo.BasicDialog} this
28997      */
28998     setTitle : function(text){
28999         this.header.update(text);
29000         return this;
29001     },
29002
29003     // private
29004     closeClick : function(){
29005         this.hide();
29006     },
29007
29008     // private
29009     collapseClick : function(){
29010         this[this.collapsed ? "expand" : "collapse"]();
29011     },
29012
29013     /**
29014      * Collapses the dialog to its minimized state (only the title bar is visible).
29015      * Equivalent to the user clicking the collapse dialog button.
29016      */
29017     collapse : function(){
29018         if(!this.collapsed){
29019             this.collapsed = true;
29020             this.el.addClass("x-dlg-collapsed");
29021             this.restoreHeight = this.el.getHeight();
29022             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29023         }
29024     },
29025
29026     /**
29027      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29028      * clicking the expand dialog button.
29029      */
29030     expand : function(){
29031         if(this.collapsed){
29032             this.collapsed = false;
29033             this.el.removeClass("x-dlg-collapsed");
29034             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29035         }
29036     },
29037
29038     /**
29039      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29040      * @return {Roo.TabPanel} The tabs component
29041      */
29042     initTabs : function(){
29043         var tabs = this.getTabs();
29044         while(tabs.getTab(0)){
29045             tabs.removeTab(0);
29046         }
29047         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29048             var dom = el.dom;
29049             tabs.addTab(Roo.id(dom), dom.title);
29050             dom.title = "";
29051         });
29052         tabs.activate(0);
29053         return tabs;
29054     },
29055
29056     // private
29057     beforeResize : function(){
29058         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29059     },
29060
29061     // private
29062     onResize : function(){
29063         this.refreshSize();
29064         this.syncBodyHeight();
29065         this.adjustAssets();
29066         this.focus();
29067         this.fireEvent("resize", this, this.size.width, this.size.height);
29068     },
29069
29070     // private
29071     onKeyDown : function(e){
29072         if(this.isVisible()){
29073             this.fireEvent("keydown", this, e);
29074         }
29075     },
29076
29077     /**
29078      * Resizes the dialog.
29079      * @param {Number} width
29080      * @param {Number} height
29081      * @return {Roo.BasicDialog} this
29082      */
29083     resizeTo : function(width, height){
29084         this.el.setSize(width, height);
29085         this.size = {width: width, height: height};
29086         this.syncBodyHeight();
29087         if(this.fixedcenter){
29088             this.center();
29089         }
29090         if(this.isVisible()){
29091             this.constrainXY();
29092             this.adjustAssets();
29093         }
29094         this.fireEvent("resize", this, width, height);
29095         return this;
29096     },
29097
29098
29099     /**
29100      * Resizes the dialog to fit the specified content size.
29101      * @param {Number} width
29102      * @param {Number} height
29103      * @return {Roo.BasicDialog} this
29104      */
29105     setContentSize : function(w, h){
29106         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29107         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29108         //if(!this.el.isBorderBox()){
29109             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29110             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29111         //}
29112         if(this.tabs){
29113             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29114             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29115         }
29116         this.resizeTo(w, h);
29117         return this;
29118     },
29119
29120     /**
29121      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29122      * executed in response to a particular key being pressed while the dialog is active.
29123      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29124      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29125      * @param {Function} fn The function to call
29126      * @param {Object} scope (optional) The scope of the function
29127      * @return {Roo.BasicDialog} this
29128      */
29129     addKeyListener : function(key, fn, scope){
29130         var keyCode, shift, ctrl, alt;
29131         if(typeof key == "object" && !(key instanceof Array)){
29132             keyCode = key["key"];
29133             shift = key["shift"];
29134             ctrl = key["ctrl"];
29135             alt = key["alt"];
29136         }else{
29137             keyCode = key;
29138         }
29139         var handler = function(dlg, e){
29140             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29141                 var k = e.getKey();
29142                 if(keyCode instanceof Array){
29143                     for(var i = 0, len = keyCode.length; i < len; i++){
29144                         if(keyCode[i] == k){
29145                           fn.call(scope || window, dlg, k, e);
29146                           return;
29147                         }
29148                     }
29149                 }else{
29150                     if(k == keyCode){
29151                         fn.call(scope || window, dlg, k, e);
29152                     }
29153                 }
29154             }
29155         };
29156         this.on("keydown", handler);
29157         return this;
29158     },
29159
29160     /**
29161      * Returns the TabPanel component (creates it if it doesn't exist).
29162      * Note: If you wish to simply check for the existence of tabs without creating them,
29163      * check for a null 'tabs' property.
29164      * @return {Roo.TabPanel} The tabs component
29165      */
29166     getTabs : function(){
29167         if(!this.tabs){
29168             this.el.addClass("x-dlg-auto-tabs");
29169             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29170             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29171         }
29172         return this.tabs;
29173     },
29174
29175     /**
29176      * Adds a button to the footer section of the dialog.
29177      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29178      * object or a valid Roo.DomHelper element config
29179      * @param {Function} handler The function called when the button is clicked
29180      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29181      * @return {Roo.Button} The new button
29182      */
29183     addButton : function(config, handler, scope){
29184         var dh = Roo.DomHelper;
29185         if(!this.footer){
29186             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29187         }
29188         if(!this.btnContainer){
29189             var tb = this.footer.createChild({
29190
29191                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29192                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29193             }, null, true);
29194             this.btnContainer = tb.firstChild.firstChild.firstChild;
29195         }
29196         var bconfig = {
29197             handler: handler,
29198             scope: scope,
29199             minWidth: this.minButtonWidth,
29200             hideParent:true
29201         };
29202         if(typeof config == "string"){
29203             bconfig.text = config;
29204         }else{
29205             if(config.tag){
29206                 bconfig.dhconfig = config;
29207             }else{
29208                 Roo.apply(bconfig, config);
29209             }
29210         }
29211         var fc = false;
29212         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29213             bconfig.position = Math.max(0, bconfig.position);
29214             fc = this.btnContainer.childNodes[bconfig.position];
29215         }
29216          
29217         var btn = new Roo.Button(
29218             fc ? 
29219                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29220                 : this.btnContainer.appendChild(document.createElement("td")),
29221             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29222             bconfig
29223         );
29224         this.syncBodyHeight();
29225         if(!this.buttons){
29226             /**
29227              * Array of all the buttons that have been added to this dialog via addButton
29228              * @type Array
29229              */
29230             this.buttons = [];
29231         }
29232         this.buttons.push(btn);
29233         return btn;
29234     },
29235
29236     /**
29237      * Sets the default button to be focused when the dialog is displayed.
29238      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29239      * @return {Roo.BasicDialog} this
29240      */
29241     setDefaultButton : function(btn){
29242         this.defaultButton = btn;
29243         return this;
29244     },
29245
29246     // private
29247     getHeaderFooterHeight : function(safe){
29248         var height = 0;
29249         if(this.header){
29250            height += this.header.getHeight();
29251         }
29252         if(this.footer){
29253            var fm = this.footer.getMargins();
29254             height += (this.footer.getHeight()+fm.top+fm.bottom);
29255         }
29256         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29257         height += this.centerBg.getPadding("tb");
29258         return height;
29259     },
29260
29261     // private
29262     syncBodyHeight : function(){
29263         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29264         var height = this.size.height - this.getHeaderFooterHeight(false);
29265         bd.setHeight(height-bd.getMargins("tb"));
29266         var hh = this.header.getHeight();
29267         var h = this.size.height-hh;
29268         cb.setHeight(h);
29269         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29270         bw.setHeight(h-cb.getPadding("tb"));
29271         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29272         bd.setWidth(bw.getWidth(true));
29273         if(this.tabs){
29274             this.tabs.syncHeight();
29275             if(Roo.isIE){
29276                 this.tabs.el.repaint();
29277             }
29278         }
29279     },
29280
29281     /**
29282      * Restores the previous state of the dialog if Roo.state is configured.
29283      * @return {Roo.BasicDialog} this
29284      */
29285     restoreState : function(){
29286         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29287         if(box && box.width){
29288             this.xy = [box.x, box.y];
29289             this.resizeTo(box.width, box.height);
29290         }
29291         return this;
29292     },
29293
29294     // private
29295     beforeShow : function(){
29296         this.expand();
29297         if(this.fixedcenter){
29298             this.xy = this.el.getCenterXY(true);
29299         }
29300         if(this.modal){
29301             Roo.get(document.body).addClass("x-body-masked");
29302             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29303             this.mask.show();
29304         }
29305         this.constrainXY();
29306     },
29307
29308     // private
29309     animShow : function(){
29310         var b = Roo.get(this.animateTarget).getBox();
29311         this.proxy.setSize(b.width, b.height);
29312         this.proxy.setLocation(b.x, b.y);
29313         this.proxy.show();
29314         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29315                     true, .35, this.showEl.createDelegate(this));
29316     },
29317
29318     /**
29319      * Shows the dialog.
29320      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29321      * @return {Roo.BasicDialog} this
29322      */
29323     show : function(animateTarget){
29324         if (this.fireEvent("beforeshow", this) === false){
29325             return;
29326         }
29327         if(this.syncHeightBeforeShow){
29328             this.syncBodyHeight();
29329         }else if(this.firstShow){
29330             this.firstShow = false;
29331             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29332         }
29333         this.animateTarget = animateTarget || this.animateTarget;
29334         if(!this.el.isVisible()){
29335             this.beforeShow();
29336             if(this.animateTarget && Roo.get(this.animateTarget)){
29337                 this.animShow();
29338             }else{
29339                 this.showEl();
29340             }
29341         }
29342         return this;
29343     },
29344
29345     // private
29346     showEl : function(){
29347         this.proxy.hide();
29348         this.el.setXY(this.xy);
29349         this.el.show();
29350         this.adjustAssets(true);
29351         this.toFront();
29352         this.focus();
29353         // IE peekaboo bug - fix found by Dave Fenwick
29354         if(Roo.isIE){
29355             this.el.repaint();
29356         }
29357         this.fireEvent("show", this);
29358     },
29359
29360     /**
29361      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29362      * dialog itself will receive focus.
29363      */
29364     focus : function(){
29365         if(this.defaultButton){
29366             this.defaultButton.focus();
29367         }else{
29368             this.focusEl.focus();
29369         }
29370     },
29371
29372     // private
29373     constrainXY : function(){
29374         if(this.constraintoviewport !== false){
29375             if(!this.viewSize){
29376                 if(this.container){
29377                     var s = this.container.getSize();
29378                     this.viewSize = [s.width, s.height];
29379                 }else{
29380                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29381                 }
29382             }
29383             var s = Roo.get(this.container||document).getScroll();
29384
29385             var x = this.xy[0], y = this.xy[1];
29386             var w = this.size.width, h = this.size.height;
29387             var vw = this.viewSize[0], vh = this.viewSize[1];
29388             // only move it if it needs it
29389             var moved = false;
29390             // first validate right/bottom
29391             if(x + w > vw+s.left){
29392                 x = vw - w;
29393                 moved = true;
29394             }
29395             if(y + h > vh+s.top){
29396                 y = vh - h;
29397                 moved = true;
29398             }
29399             // then make sure top/left isn't negative
29400             if(x < s.left){
29401                 x = s.left;
29402                 moved = true;
29403             }
29404             if(y < s.top){
29405                 y = s.top;
29406                 moved = true;
29407             }
29408             if(moved){
29409                 // cache xy
29410                 this.xy = [x, y];
29411                 if(this.isVisible()){
29412                     this.el.setLocation(x, y);
29413                     this.adjustAssets();
29414                 }
29415             }
29416         }
29417     },
29418
29419     // private
29420     onDrag : function(){
29421         if(!this.proxyDrag){
29422             this.xy = this.el.getXY();
29423             this.adjustAssets();
29424         }
29425     },
29426
29427     // private
29428     adjustAssets : function(doShow){
29429         var x = this.xy[0], y = this.xy[1];
29430         var w = this.size.width, h = this.size.height;
29431         if(doShow === true){
29432             if(this.shadow){
29433                 this.shadow.show(this.el);
29434             }
29435             if(this.shim){
29436                 this.shim.show();
29437             }
29438         }
29439         if(this.shadow && this.shadow.isVisible()){
29440             this.shadow.show(this.el);
29441         }
29442         if(this.shim && this.shim.isVisible()){
29443             this.shim.setBounds(x, y, w, h);
29444         }
29445     },
29446
29447     // private
29448     adjustViewport : function(w, h){
29449         if(!w || !h){
29450             w = Roo.lib.Dom.getViewWidth();
29451             h = Roo.lib.Dom.getViewHeight();
29452         }
29453         // cache the size
29454         this.viewSize = [w, h];
29455         if(this.modal && this.mask.isVisible()){
29456             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29457             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29458         }
29459         if(this.isVisible()){
29460             this.constrainXY();
29461         }
29462     },
29463
29464     /**
29465      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29466      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29467      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29468      */
29469     destroy : function(removeEl){
29470         if(this.isVisible()){
29471             this.animateTarget = null;
29472             this.hide();
29473         }
29474         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29475         if(this.tabs){
29476             this.tabs.destroy(removeEl);
29477         }
29478         Roo.destroy(
29479              this.shim,
29480              this.proxy,
29481              this.resizer,
29482              this.close,
29483              this.mask
29484         );
29485         if(this.dd){
29486             this.dd.unreg();
29487         }
29488         if(this.buttons){
29489            for(var i = 0, len = this.buttons.length; i < len; i++){
29490                this.buttons[i].destroy();
29491            }
29492         }
29493         this.el.removeAllListeners();
29494         if(removeEl === true){
29495             this.el.update("");
29496             this.el.remove();
29497         }
29498         Roo.DialogManager.unregister(this);
29499     },
29500
29501     // private
29502     startMove : function(){
29503         if(this.proxyDrag){
29504             this.proxy.show();
29505         }
29506         if(this.constraintoviewport !== false){
29507             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29508         }
29509     },
29510
29511     // private
29512     endMove : function(){
29513         if(!this.proxyDrag){
29514             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29515         }else{
29516             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29517             this.proxy.hide();
29518         }
29519         this.refreshSize();
29520         this.adjustAssets();
29521         this.focus();
29522         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29523     },
29524
29525     /**
29526      * Brings this dialog to the front of any other visible dialogs
29527      * @return {Roo.BasicDialog} this
29528      */
29529     toFront : function(){
29530         Roo.DialogManager.bringToFront(this);
29531         return this;
29532     },
29533
29534     /**
29535      * Sends this dialog to the back (under) of any other visible dialogs
29536      * @return {Roo.BasicDialog} this
29537      */
29538     toBack : function(){
29539         Roo.DialogManager.sendToBack(this);
29540         return this;
29541     },
29542
29543     /**
29544      * Centers this dialog in the viewport
29545      * @return {Roo.BasicDialog} this
29546      */
29547     center : function(){
29548         var xy = this.el.getCenterXY(true);
29549         this.moveTo(xy[0], xy[1]);
29550         return this;
29551     },
29552
29553     /**
29554      * Moves the dialog's top-left corner to the specified point
29555      * @param {Number} x
29556      * @param {Number} y
29557      * @return {Roo.BasicDialog} this
29558      */
29559     moveTo : function(x, y){
29560         this.xy = [x,y];
29561         if(this.isVisible()){
29562             this.el.setXY(this.xy);
29563             this.adjustAssets();
29564         }
29565         return this;
29566     },
29567
29568     /**
29569      * Aligns the dialog to the specified element
29570      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29571      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29572      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29573      * @return {Roo.BasicDialog} this
29574      */
29575     alignTo : function(element, position, offsets){
29576         this.xy = this.el.getAlignToXY(element, position, offsets);
29577         if(this.isVisible()){
29578             this.el.setXY(this.xy);
29579             this.adjustAssets();
29580         }
29581         return this;
29582     },
29583
29584     /**
29585      * Anchors an element to another element and realigns it when the window is resized.
29586      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29587      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29588      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29589      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29590      * is a number, it is used as the buffer delay (defaults to 50ms).
29591      * @return {Roo.BasicDialog} this
29592      */
29593     anchorTo : function(el, alignment, offsets, monitorScroll){
29594         var action = function(){
29595             this.alignTo(el, alignment, offsets);
29596         };
29597         Roo.EventManager.onWindowResize(action, this);
29598         var tm = typeof monitorScroll;
29599         if(tm != 'undefined'){
29600             Roo.EventManager.on(window, 'scroll', action, this,
29601                 {buffer: tm == 'number' ? monitorScroll : 50});
29602         }
29603         action.call(this);
29604         return this;
29605     },
29606
29607     /**
29608      * Returns true if the dialog is visible
29609      * @return {Boolean}
29610      */
29611     isVisible : function(){
29612         return this.el.isVisible();
29613     },
29614
29615     // private
29616     animHide : function(callback){
29617         var b = Roo.get(this.animateTarget).getBox();
29618         this.proxy.show();
29619         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29620         this.el.hide();
29621         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29622                     this.hideEl.createDelegate(this, [callback]));
29623     },
29624
29625     /**
29626      * Hides the dialog.
29627      * @param {Function} callback (optional) Function to call when the dialog is hidden
29628      * @return {Roo.BasicDialog} this
29629      */
29630     hide : function(callback){
29631         if (this.fireEvent("beforehide", this) === false){
29632             return;
29633         }
29634         if(this.shadow){
29635             this.shadow.hide();
29636         }
29637         if(this.shim) {
29638           this.shim.hide();
29639         }
29640         // sometimes animateTarget seems to get set.. causing problems...
29641         // this just double checks..
29642         if(this.animateTarget && Roo.get(this.animateTarget)) {
29643            this.animHide(callback);
29644         }else{
29645             this.el.hide();
29646             this.hideEl(callback);
29647         }
29648         return this;
29649     },
29650
29651     // private
29652     hideEl : function(callback){
29653         this.proxy.hide();
29654         if(this.modal){
29655             this.mask.hide();
29656             Roo.get(document.body).removeClass("x-body-masked");
29657         }
29658         this.fireEvent("hide", this);
29659         if(typeof callback == "function"){
29660             callback();
29661         }
29662     },
29663
29664     // private
29665     hideAction : function(){
29666         this.setLeft("-10000px");
29667         this.setTop("-10000px");
29668         this.setStyle("visibility", "hidden");
29669     },
29670
29671     // private
29672     refreshSize : function(){
29673         this.size = this.el.getSize();
29674         this.xy = this.el.getXY();
29675         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29676     },
29677
29678     // private
29679     // z-index is managed by the DialogManager and may be overwritten at any time
29680     setZIndex : function(index){
29681         if(this.modal){
29682             this.mask.setStyle("z-index", index);
29683         }
29684         if(this.shim){
29685             this.shim.setStyle("z-index", ++index);
29686         }
29687         if(this.shadow){
29688             this.shadow.setZIndex(++index);
29689         }
29690         this.el.setStyle("z-index", ++index);
29691         if(this.proxy){
29692             this.proxy.setStyle("z-index", ++index);
29693         }
29694         if(this.resizer){
29695             this.resizer.proxy.setStyle("z-index", ++index);
29696         }
29697
29698         this.lastZIndex = index;
29699     },
29700
29701     /**
29702      * Returns the element for this dialog
29703      * @return {Roo.Element} The underlying dialog Element
29704      */
29705     getEl : function(){
29706         return this.el;
29707     }
29708 });
29709
29710 /**
29711  * @class Roo.DialogManager
29712  * Provides global access to BasicDialogs that have been created and
29713  * support for z-indexing (layering) multiple open dialogs.
29714  */
29715 Roo.DialogManager = function(){
29716     var list = {};
29717     var accessList = [];
29718     var front = null;
29719
29720     // private
29721     var sortDialogs = function(d1, d2){
29722         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29723     };
29724
29725     // private
29726     var orderDialogs = function(){
29727         accessList.sort(sortDialogs);
29728         var seed = Roo.DialogManager.zseed;
29729         for(var i = 0, len = accessList.length; i < len; i++){
29730             var dlg = accessList[i];
29731             if(dlg){
29732                 dlg.setZIndex(seed + (i*10));
29733             }
29734         }
29735     };
29736
29737     return {
29738         /**
29739          * The starting z-index for BasicDialogs (defaults to 9000)
29740          * @type Number The z-index value
29741          */
29742         zseed : 9000,
29743
29744         // private
29745         register : function(dlg){
29746             list[dlg.id] = dlg;
29747             accessList.push(dlg);
29748         },
29749
29750         // private
29751         unregister : function(dlg){
29752             delete list[dlg.id];
29753             var i=0;
29754             var len=0;
29755             if(!accessList.indexOf){
29756                 for(  i = 0, len = accessList.length; i < len; i++){
29757                     if(accessList[i] == dlg){
29758                         accessList.splice(i, 1);
29759                         return;
29760                     }
29761                 }
29762             }else{
29763                  i = accessList.indexOf(dlg);
29764                 if(i != -1){
29765                     accessList.splice(i, 1);
29766                 }
29767             }
29768         },
29769
29770         /**
29771          * Gets a registered dialog by id
29772          * @param {String/Object} id The id of the dialog or a dialog
29773          * @return {Roo.BasicDialog} this
29774          */
29775         get : function(id){
29776             return typeof id == "object" ? id : list[id];
29777         },
29778
29779         /**
29780          * Brings the specified dialog to the front
29781          * @param {String/Object} dlg The id of the dialog or a dialog
29782          * @return {Roo.BasicDialog} this
29783          */
29784         bringToFront : function(dlg){
29785             dlg = this.get(dlg);
29786             if(dlg != front){
29787                 front = dlg;
29788                 dlg._lastAccess = new Date().getTime();
29789                 orderDialogs();
29790             }
29791             return dlg;
29792         },
29793
29794         /**
29795          * Sends the specified dialog to the back
29796          * @param {String/Object} dlg The id of the dialog or a dialog
29797          * @return {Roo.BasicDialog} this
29798          */
29799         sendToBack : function(dlg){
29800             dlg = this.get(dlg);
29801             dlg._lastAccess = -(new Date().getTime());
29802             orderDialogs();
29803             return dlg;
29804         },
29805
29806         /**
29807          * Hides all dialogs
29808          */
29809         hideAll : function(){
29810             for(var id in list){
29811                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29812                     list[id].hide();
29813                 }
29814             }
29815         }
29816     };
29817 }();
29818
29819 /**
29820  * @class Roo.LayoutDialog
29821  * @extends Roo.BasicDialog
29822  * Dialog which provides adjustments for working with a layout in a Dialog.
29823  * Add your necessary layout config options to the dialog's config.<br>
29824  * Example usage (including a nested layout):
29825  * <pre><code>
29826 if(!dialog){
29827     dialog = new Roo.LayoutDialog("download-dlg", {
29828         modal: true,
29829         width:600,
29830         height:450,
29831         shadow:true,
29832         minWidth:500,
29833         minHeight:350,
29834         autoTabs:true,
29835         proxyDrag:true,
29836         // layout config merges with the dialog config
29837         center:{
29838             tabPosition: "top",
29839             alwaysShowTabs: true
29840         }
29841     });
29842     dialog.addKeyListener(27, dialog.hide, dialog);
29843     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29844     dialog.addButton("Build It!", this.getDownload, this);
29845
29846     // we can even add nested layouts
29847     var innerLayout = new Roo.BorderLayout("dl-inner", {
29848         east: {
29849             initialSize: 200,
29850             autoScroll:true,
29851             split:true
29852         },
29853         center: {
29854             autoScroll:true
29855         }
29856     });
29857     innerLayout.beginUpdate();
29858     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29859     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29860     innerLayout.endUpdate(true);
29861
29862     var layout = dialog.getLayout();
29863     layout.beginUpdate();
29864     layout.add("center", new Roo.ContentPanel("standard-panel",
29865                         {title: "Download the Source", fitToFrame:true}));
29866     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29867                {title: "Build your own roo.js"}));
29868     layout.getRegion("center").showPanel(sp);
29869     layout.endUpdate();
29870 }
29871 </code></pre>
29872     * @constructor
29873     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29874     * @param {Object} config configuration options
29875   */
29876 Roo.LayoutDialog = function(el, cfg){
29877     
29878     var config=  cfg;
29879     if (typeof(cfg) == 'undefined') {
29880         config = Roo.apply({}, el);
29881         // not sure why we use documentElement here.. - it should always be body.
29882         // IE7 borks horribly if we use documentElement.
29883         // webkit also does not like documentElement - it creates a body element...
29884         el = Roo.get( document.body || document.documentElement ).createChild();
29885         //config.autoCreate = true;
29886     }
29887     
29888     
29889     config.autoTabs = false;
29890     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29891     this.body.setStyle({overflow:"hidden", position:"relative"});
29892     this.layout = new Roo.BorderLayout(this.body.dom, config);
29893     this.layout.monitorWindowResize = false;
29894     this.el.addClass("x-dlg-auto-layout");
29895     // fix case when center region overwrites center function
29896     this.center = Roo.BasicDialog.prototype.center;
29897     this.on("show", this.layout.layout, this.layout, true);
29898     if (config.items) {
29899         var xitems = config.items;
29900         delete config.items;
29901         Roo.each(xitems, this.addxtype, this);
29902     }
29903     
29904     
29905 };
29906 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29907     /**
29908      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29909      * @deprecated
29910      */
29911     endUpdate : function(){
29912         this.layout.endUpdate();
29913     },
29914
29915     /**
29916      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29917      *  @deprecated
29918      */
29919     beginUpdate : function(){
29920         this.layout.beginUpdate();
29921     },
29922
29923     /**
29924      * Get the BorderLayout for this dialog
29925      * @return {Roo.BorderLayout}
29926      */
29927     getLayout : function(){
29928         return this.layout;
29929     },
29930
29931     showEl : function(){
29932         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29933         if(Roo.isIE7){
29934             this.layout.layout();
29935         }
29936     },
29937
29938     // private
29939     // Use the syncHeightBeforeShow config option to control this automatically
29940     syncBodyHeight : function(){
29941         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29942         if(this.layout){this.layout.layout();}
29943     },
29944     
29945       /**
29946      * Add an xtype element (actually adds to the layout.)
29947      * @return {Object} xdata xtype object data.
29948      */
29949     
29950     addxtype : function(c) {
29951         return this.layout.addxtype(c);
29952     }
29953 });/*
29954  * Based on:
29955  * Ext JS Library 1.1.1
29956  * Copyright(c) 2006-2007, Ext JS, LLC.
29957  *
29958  * Originally Released Under LGPL - original licence link has changed is not relivant.
29959  *
29960  * Fork - LGPL
29961  * <script type="text/javascript">
29962  */
29963  
29964 /**
29965  * @class Roo.MessageBox
29966  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29967  * Example usage:
29968  *<pre><code>
29969 // Basic alert:
29970 Roo.Msg.alert('Status', 'Changes saved successfully.');
29971
29972 // Prompt for user data:
29973 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29974     if (btn == 'ok'){
29975         // process text value...
29976     }
29977 });
29978
29979 // Show a dialog using config options:
29980 Roo.Msg.show({
29981    title:'Save Changes?',
29982    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29983    buttons: Roo.Msg.YESNOCANCEL,
29984    fn: processResult,
29985    animEl: 'elId'
29986 });
29987 </code></pre>
29988  * @singleton
29989  */
29990 Roo.MessageBox = function(){
29991     var dlg, opt, mask, waitTimer;
29992     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29993     var buttons, activeTextEl, bwidth;
29994
29995     // private
29996     var handleButton = function(button){
29997         dlg.hide();
29998         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29999     };
30000
30001     // private
30002     var handleHide = function(){
30003         if(opt && opt.cls){
30004             dlg.el.removeClass(opt.cls);
30005         }
30006         if(waitTimer){
30007             Roo.TaskMgr.stop(waitTimer);
30008             waitTimer = null;
30009         }
30010     };
30011
30012     // private
30013     var updateButtons = function(b){
30014         var width = 0;
30015         if(!b){
30016             buttons["ok"].hide();
30017             buttons["cancel"].hide();
30018             buttons["yes"].hide();
30019             buttons["no"].hide();
30020             dlg.footer.dom.style.display = 'none';
30021             return width;
30022         }
30023         dlg.footer.dom.style.display = '';
30024         for(var k in buttons){
30025             if(typeof buttons[k] != "function"){
30026                 if(b[k]){
30027                     buttons[k].show();
30028                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30029                     width += buttons[k].el.getWidth()+15;
30030                 }else{
30031                     buttons[k].hide();
30032                 }
30033             }
30034         }
30035         return width;
30036     };
30037
30038     // private
30039     var handleEsc = function(d, k, e){
30040         if(opt && opt.closable !== false){
30041             dlg.hide();
30042         }
30043         if(e){
30044             e.stopEvent();
30045         }
30046     };
30047
30048     return {
30049         /**
30050          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30051          * @return {Roo.BasicDialog} The BasicDialog element
30052          */
30053         getDialog : function(){
30054            if(!dlg){
30055                 dlg = new Roo.BasicDialog("x-msg-box", {
30056                     autoCreate : true,
30057                     shadow: true,
30058                     draggable: true,
30059                     resizable:false,
30060                     constraintoviewport:false,
30061                     fixedcenter:true,
30062                     collapsible : false,
30063                     shim:true,
30064                     modal: true,
30065                     width:400, height:100,
30066                     buttonAlign:"center",
30067                     closeClick : function(){
30068                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30069                             handleButton("no");
30070                         }else{
30071                             handleButton("cancel");
30072                         }
30073                     }
30074                 });
30075                 dlg.on("hide", handleHide);
30076                 mask = dlg.mask;
30077                 dlg.addKeyListener(27, handleEsc);
30078                 buttons = {};
30079                 var bt = this.buttonText;
30080                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30081                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30082                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30083                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30084                 bodyEl = dlg.body.createChild({
30085
30086                     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>'
30087                 });
30088                 msgEl = bodyEl.dom.firstChild;
30089                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30090                 textboxEl.enableDisplayMode();
30091                 textboxEl.addKeyListener([10,13], function(){
30092                     if(dlg.isVisible() && opt && opt.buttons){
30093                         if(opt.buttons.ok){
30094                             handleButton("ok");
30095                         }else if(opt.buttons.yes){
30096                             handleButton("yes");
30097                         }
30098                     }
30099                 });
30100                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30101                 textareaEl.enableDisplayMode();
30102                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30103                 progressEl.enableDisplayMode();
30104                 var pf = progressEl.dom.firstChild;
30105                 if (pf) {
30106                     pp = Roo.get(pf.firstChild);
30107                     pp.setHeight(pf.offsetHeight);
30108                 }
30109                 
30110             }
30111             return dlg;
30112         },
30113
30114         /**
30115          * Updates the message box body text
30116          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30117          * the XHTML-compliant non-breaking space character '&amp;#160;')
30118          * @return {Roo.MessageBox} This message box
30119          */
30120         updateText : function(text){
30121             if(!dlg.isVisible() && !opt.width){
30122                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30123             }
30124             msgEl.innerHTML = text || '&#160;';
30125             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30126                         Math.max(opt.minWidth || this.minWidth, bwidth));
30127             if(opt.prompt){
30128                 activeTextEl.setWidth(w);
30129             }
30130             if(dlg.isVisible()){
30131                 dlg.fixedcenter = false;
30132             }
30133             dlg.setContentSize(w, bodyEl.getHeight());
30134             if(dlg.isVisible()){
30135                 dlg.fixedcenter = true;
30136             }
30137             return this;
30138         },
30139
30140         /**
30141          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30142          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30143          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30144          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30145          * @return {Roo.MessageBox} This message box
30146          */
30147         updateProgress : function(value, text){
30148             if(text){
30149                 this.updateText(text);
30150             }
30151             if (pp) { // weird bug on my firefox - for some reason this is not defined
30152                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30153             }
30154             return this;
30155         },        
30156
30157         /**
30158          * Returns true if the message box is currently displayed
30159          * @return {Boolean} True if the message box is visible, else false
30160          */
30161         isVisible : function(){
30162             return dlg && dlg.isVisible();  
30163         },
30164
30165         /**
30166          * Hides the message box if it is displayed
30167          */
30168         hide : function(){
30169             if(this.isVisible()){
30170                 dlg.hide();
30171             }  
30172         },
30173
30174         /**
30175          * Displays a new message box, or reinitializes an existing message box, based on the config options
30176          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30177          * The following config object properties are supported:
30178          * <pre>
30179 Property    Type             Description
30180 ----------  ---------------  ------------------------------------------------------------------------------------
30181 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30182                                    closes (defaults to undefined)
30183 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30184                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30185 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30186                                    progress and wait dialogs will ignore this property and always hide the
30187                                    close button as they can only be closed programmatically.
30188 cls               String           A custom CSS class to apply to the message box element
30189 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30190                                    displayed (defaults to 75)
30191 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30192                                    function will be btn (the name of the button that was clicked, if applicable,
30193                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30194                                    Progress and wait dialogs will ignore this option since they do not respond to
30195                                    user actions and can only be closed programmatically, so any required function
30196                                    should be called by the same code after it closes the dialog.
30197 icon              String           A CSS class that provides a background image to be used as an icon for
30198                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30199 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30200 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30201 modal             Boolean          False to allow user interaction with the page while the message box is
30202                                    displayed (defaults to true)
30203 msg               String           A string that will replace the existing message box body text (defaults
30204                                    to the XHTML-compliant non-breaking space character '&#160;')
30205 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30206 progress          Boolean          True to display a progress bar (defaults to false)
30207 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30208 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30209 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30210 title             String           The title text
30211 value             String           The string value to set into the active textbox element if displayed
30212 wait              Boolean          True to display a progress bar (defaults to false)
30213 width             Number           The width of the dialog in pixels
30214 </pre>
30215          *
30216          * Example usage:
30217          * <pre><code>
30218 Roo.Msg.show({
30219    title: 'Address',
30220    msg: 'Please enter your address:',
30221    width: 300,
30222    buttons: Roo.MessageBox.OKCANCEL,
30223    multiline: true,
30224    fn: saveAddress,
30225    animEl: 'addAddressBtn'
30226 });
30227 </code></pre>
30228          * @param {Object} config Configuration options
30229          * @return {Roo.MessageBox} This message box
30230          */
30231         show : function(options)
30232         {
30233             
30234             // this causes nightmares if you show one dialog after another
30235             // especially on callbacks..
30236              
30237             if(this.isVisible()){
30238                 
30239                 this.hide();
30240                 this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30241                 throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30242                 
30243             }
30244             var d = this.getDialog();
30245             opt = options;
30246             d.setTitle(opt.title || "&#160;");
30247             d.close.setDisplayed(opt.closable !== false);
30248             activeTextEl = textboxEl;
30249             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30250             if(opt.prompt){
30251                 if(opt.multiline){
30252                     textboxEl.hide();
30253                     textareaEl.show();
30254                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30255                         opt.multiline : this.defaultTextHeight);
30256                     activeTextEl = textareaEl;
30257                 }else{
30258                     textboxEl.show();
30259                     textareaEl.hide();
30260                 }
30261             }else{
30262                 textboxEl.hide();
30263                 textareaEl.hide();
30264             }
30265             progressEl.setDisplayed(opt.progress === true);
30266             this.updateProgress(0);
30267             activeTextEl.dom.value = opt.value || "";
30268             if(opt.prompt){
30269                 dlg.setDefaultButton(activeTextEl);
30270             }else{
30271                 var bs = opt.buttons;
30272                 var db = null;
30273                 if(bs && bs.ok){
30274                     db = buttons["ok"];
30275                 }else if(bs && bs.yes){
30276                     db = buttons["yes"];
30277                 }
30278                 dlg.setDefaultButton(db);
30279             }
30280             bwidth = updateButtons(opt.buttons);
30281             this.updateText(opt.msg);
30282             if(opt.cls){
30283                 d.el.addClass(opt.cls);
30284             }
30285             d.proxyDrag = opt.proxyDrag === true;
30286             d.modal = opt.modal !== false;
30287             d.mask = opt.modal !== false ? mask : false;
30288             if(!d.isVisible()){
30289                 // force it to the end of the z-index stack so it gets a cursor in FF
30290                 document.body.appendChild(dlg.el.dom);
30291                 d.animateTarget = null;
30292                 d.show(options.animEl);
30293             }
30294             return this;
30295         },
30296
30297         /**
30298          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30299          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30300          * and closing the message box when the process is complete.
30301          * @param {String} title The title bar text
30302          * @param {String} msg The message box body text
30303          * @return {Roo.MessageBox} This message box
30304          */
30305         progress : function(title, msg){
30306             this.show({
30307                 title : title,
30308                 msg : msg,
30309                 buttons: false,
30310                 progress:true,
30311                 closable:false,
30312                 minWidth: this.minProgressWidth,
30313                 modal : true
30314             });
30315             return this;
30316         },
30317
30318         /**
30319          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30320          * If a callback function is passed it will be called after the user clicks the button, and the
30321          * id of the button that was clicked will be passed as the only parameter to the callback
30322          * (could also be the top-right close button).
30323          * @param {String} title The title bar text
30324          * @param {String} msg The message box body text
30325          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30326          * @param {Object} scope (optional) The scope of the callback function
30327          * @return {Roo.MessageBox} This message box
30328          */
30329         alert : function(title, msg, fn, scope){
30330             this.show({
30331                 title : title,
30332                 msg : msg,
30333                 buttons: this.OK,
30334                 fn: fn,
30335                 scope : scope,
30336                 modal : true
30337             });
30338             return this;
30339         },
30340
30341         /**
30342          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30343          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30344          * You are responsible for closing the message box when the process is complete.
30345          * @param {String} msg The message box body text
30346          * @param {String} title (optional) The title bar text
30347          * @return {Roo.MessageBox} This message box
30348          */
30349         wait : function(msg, title){
30350             this.show({
30351                 title : title,
30352                 msg : msg,
30353                 buttons: false,
30354                 closable:false,
30355                 progress:true,
30356                 modal:true,
30357                 width:300,
30358                 wait:true
30359             });
30360             waitTimer = Roo.TaskMgr.start({
30361                 run: function(i){
30362                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30363                 },
30364                 interval: 1000
30365             });
30366             return this;
30367         },
30368
30369         /**
30370          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30371          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30372          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30373          * @param {String} title The title bar text
30374          * @param {String} msg The message box body text
30375          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30376          * @param {Object} scope (optional) The scope of the callback function
30377          * @return {Roo.MessageBox} This message box
30378          */
30379         confirm : function(title, msg, fn, scope){
30380             this.show({
30381                 title : title,
30382                 msg : msg,
30383                 buttons: this.YESNO,
30384                 fn: fn,
30385                 scope : scope,
30386                 modal : true
30387             });
30388             return this;
30389         },
30390
30391         /**
30392          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30393          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30394          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30395          * (could also be the top-right close button) and the text that was entered will be passed as the two
30396          * parameters to the callback.
30397          * @param {String} title The title bar text
30398          * @param {String} msg The message box body text
30399          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30400          * @param {Object} scope (optional) The scope of the callback function
30401          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30402          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30403          * @return {Roo.MessageBox} This message box
30404          */
30405         prompt : function(title, msg, fn, scope, multiline){
30406             this.show({
30407                 title : title,
30408                 msg : msg,
30409                 buttons: this.OKCANCEL,
30410                 fn: fn,
30411                 minWidth:250,
30412                 scope : scope,
30413                 prompt:true,
30414                 multiline: multiline,
30415                 modal : true
30416             });
30417             return this;
30418         },
30419
30420         /**
30421          * Button config that displays a single OK button
30422          * @type Object
30423          */
30424         OK : {ok:true},
30425         /**
30426          * Button config that displays Yes and No buttons
30427          * @type Object
30428          */
30429         YESNO : {yes:true, no:true},
30430         /**
30431          * Button config that displays OK and Cancel buttons
30432          * @type Object
30433          */
30434         OKCANCEL : {ok:true, cancel:true},
30435         /**
30436          * Button config that displays Yes, No and Cancel buttons
30437          * @type Object
30438          */
30439         YESNOCANCEL : {yes:true, no:true, cancel:true},
30440
30441         /**
30442          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30443          * @type Number
30444          */
30445         defaultTextHeight : 75,
30446         /**
30447          * The maximum width in pixels of the message box (defaults to 600)
30448          * @type Number
30449          */
30450         maxWidth : 600,
30451         /**
30452          * The minimum width in pixels of the message box (defaults to 100)
30453          * @type Number
30454          */
30455         minWidth : 100,
30456         /**
30457          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30458          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30459          * @type Number
30460          */
30461         minProgressWidth : 250,
30462         /**
30463          * An object containing the default button text strings that can be overriden for localized language support.
30464          * Supported properties are: ok, cancel, yes and no.
30465          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30466          * @type Object
30467          */
30468         buttonText : {
30469             ok : "OK",
30470             cancel : "Cancel",
30471             yes : "Yes",
30472             no : "No"
30473         }
30474     };
30475 }();
30476
30477 /**
30478  * Shorthand for {@link Roo.MessageBox}
30479  */
30480 Roo.Msg = Roo.MessageBox;/*
30481  * Based on:
30482  * Ext JS Library 1.1.1
30483  * Copyright(c) 2006-2007, Ext JS, LLC.
30484  *
30485  * Originally Released Under LGPL - original licence link has changed is not relivant.
30486  *
30487  * Fork - LGPL
30488  * <script type="text/javascript">
30489  */
30490 /**
30491  * @class Roo.QuickTips
30492  * Provides attractive and customizable tooltips for any element.
30493  * @singleton
30494  */
30495 Roo.QuickTips = function(){
30496     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30497     var ce, bd, xy, dd;
30498     var visible = false, disabled = true, inited = false;
30499     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30500     
30501     var onOver = function(e){
30502         if(disabled){
30503             return;
30504         }
30505         var t = e.getTarget();
30506         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30507             return;
30508         }
30509         if(ce && t == ce.el){
30510             clearTimeout(hideProc);
30511             return;
30512         }
30513         if(t && tagEls[t.id]){
30514             tagEls[t.id].el = t;
30515             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30516             return;
30517         }
30518         var ttp, et = Roo.fly(t);
30519         var ns = cfg.namespace;
30520         if(tm.interceptTitles && t.title){
30521             ttp = t.title;
30522             t.qtip = ttp;
30523             t.removeAttribute("title");
30524             e.preventDefault();
30525         }else{
30526             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30527         }
30528         if(ttp){
30529             showProc = show.defer(tm.showDelay, tm, [{
30530                 el: t, 
30531                 text: ttp, 
30532                 width: et.getAttributeNS(ns, cfg.width),
30533                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30534                 title: et.getAttributeNS(ns, cfg.title),
30535                     cls: et.getAttributeNS(ns, cfg.cls)
30536             }]);
30537         }
30538     };
30539     
30540     var onOut = function(e){
30541         clearTimeout(showProc);
30542         var t = e.getTarget();
30543         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30544             hideProc = setTimeout(hide, tm.hideDelay);
30545         }
30546     };
30547     
30548     var onMove = function(e){
30549         if(disabled){
30550             return;
30551         }
30552         xy = e.getXY();
30553         xy[1] += 18;
30554         if(tm.trackMouse && ce){
30555             el.setXY(xy);
30556         }
30557     };
30558     
30559     var onDown = function(e){
30560         clearTimeout(showProc);
30561         clearTimeout(hideProc);
30562         if(!e.within(el)){
30563             if(tm.hideOnClick){
30564                 hide();
30565                 tm.disable();
30566                 tm.enable.defer(100, tm);
30567             }
30568         }
30569     };
30570     
30571     var getPad = function(){
30572         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30573     };
30574
30575     var show = function(o){
30576         if(disabled){
30577             return;
30578         }
30579         clearTimeout(dismissProc);
30580         ce = o;
30581         if(removeCls){ // in case manually hidden
30582             el.removeClass(removeCls);
30583             removeCls = null;
30584         }
30585         if(ce.cls){
30586             el.addClass(ce.cls);
30587             removeCls = ce.cls;
30588         }
30589         if(ce.title){
30590             tipTitle.update(ce.title);
30591             tipTitle.show();
30592         }else{
30593             tipTitle.update('');
30594             tipTitle.hide();
30595         }
30596         el.dom.style.width  = tm.maxWidth+'px';
30597         //tipBody.dom.style.width = '';
30598         tipBodyText.update(o.text);
30599         var p = getPad(), w = ce.width;
30600         if(!w){
30601             var td = tipBodyText.dom;
30602             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30603             if(aw > tm.maxWidth){
30604                 w = tm.maxWidth;
30605             }else if(aw < tm.minWidth){
30606                 w = tm.minWidth;
30607             }else{
30608                 w = aw;
30609             }
30610         }
30611         //tipBody.setWidth(w);
30612         el.setWidth(parseInt(w, 10) + p);
30613         if(ce.autoHide === false){
30614             close.setDisplayed(true);
30615             if(dd){
30616                 dd.unlock();
30617             }
30618         }else{
30619             close.setDisplayed(false);
30620             if(dd){
30621                 dd.lock();
30622             }
30623         }
30624         if(xy){
30625             el.avoidY = xy[1]-18;
30626             el.setXY(xy);
30627         }
30628         if(tm.animate){
30629             el.setOpacity(.1);
30630             el.setStyle("visibility", "visible");
30631             el.fadeIn({callback: afterShow});
30632         }else{
30633             afterShow();
30634         }
30635     };
30636     
30637     var afterShow = function(){
30638         if(ce){
30639             el.show();
30640             esc.enable();
30641             if(tm.autoDismiss && ce.autoHide !== false){
30642                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30643             }
30644         }
30645     };
30646     
30647     var hide = function(noanim){
30648         clearTimeout(dismissProc);
30649         clearTimeout(hideProc);
30650         ce = null;
30651         if(el.isVisible()){
30652             esc.disable();
30653             if(noanim !== true && tm.animate){
30654                 el.fadeOut({callback: afterHide});
30655             }else{
30656                 afterHide();
30657             } 
30658         }
30659     };
30660     
30661     var afterHide = function(){
30662         el.hide();
30663         if(removeCls){
30664             el.removeClass(removeCls);
30665             removeCls = null;
30666         }
30667     };
30668     
30669     return {
30670         /**
30671         * @cfg {Number} minWidth
30672         * The minimum width of the quick tip (defaults to 40)
30673         */
30674        minWidth : 40,
30675         /**
30676         * @cfg {Number} maxWidth
30677         * The maximum width of the quick tip (defaults to 300)
30678         */
30679        maxWidth : 300,
30680         /**
30681         * @cfg {Boolean} interceptTitles
30682         * True to automatically use the element's DOM title value if available (defaults to false)
30683         */
30684        interceptTitles : false,
30685         /**
30686         * @cfg {Boolean} trackMouse
30687         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30688         */
30689        trackMouse : false,
30690         /**
30691         * @cfg {Boolean} hideOnClick
30692         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30693         */
30694        hideOnClick : true,
30695         /**
30696         * @cfg {Number} showDelay
30697         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30698         */
30699        showDelay : 500,
30700         /**
30701         * @cfg {Number} hideDelay
30702         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30703         */
30704        hideDelay : 200,
30705         /**
30706         * @cfg {Boolean} autoHide
30707         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30708         * Used in conjunction with hideDelay.
30709         */
30710        autoHide : true,
30711         /**
30712         * @cfg {Boolean}
30713         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30714         * (defaults to true).  Used in conjunction with autoDismissDelay.
30715         */
30716        autoDismiss : true,
30717         /**
30718         * @cfg {Number}
30719         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30720         */
30721        autoDismissDelay : 5000,
30722        /**
30723         * @cfg {Boolean} animate
30724         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30725         */
30726        animate : false,
30727
30728        /**
30729         * @cfg {String} title
30730         * Title text to display (defaults to '').  This can be any valid HTML markup.
30731         */
30732         title: '',
30733        /**
30734         * @cfg {String} text
30735         * Body text to display (defaults to '').  This can be any valid HTML markup.
30736         */
30737         text : '',
30738        /**
30739         * @cfg {String} cls
30740         * A CSS class to apply to the base quick tip element (defaults to '').
30741         */
30742         cls : '',
30743        /**
30744         * @cfg {Number} width
30745         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30746         * minWidth or maxWidth.
30747         */
30748         width : null,
30749
30750     /**
30751      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30752      * or display QuickTips in a page.
30753      */
30754        init : function(){
30755           tm = Roo.QuickTips;
30756           cfg = tm.tagConfig;
30757           if(!inited){
30758               if(!Roo.isReady){ // allow calling of init() before onReady
30759                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30760                   return;
30761               }
30762               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30763               el.fxDefaults = {stopFx: true};
30764               // maximum custom styling
30765               //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>');
30766               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>');              
30767               tipTitle = el.child('h3');
30768               tipTitle.enableDisplayMode("block");
30769               tipBody = el.child('div.x-tip-bd');
30770               tipBodyText = el.child('div.x-tip-bd-inner');
30771               //bdLeft = el.child('div.x-tip-bd-left');
30772               //bdRight = el.child('div.x-tip-bd-right');
30773               close = el.child('div.x-tip-close');
30774               close.enableDisplayMode("block");
30775               close.on("click", hide);
30776               var d = Roo.get(document);
30777               d.on("mousedown", onDown);
30778               d.on("mouseover", onOver);
30779               d.on("mouseout", onOut);
30780               d.on("mousemove", onMove);
30781               esc = d.addKeyListener(27, hide);
30782               esc.disable();
30783               if(Roo.dd.DD){
30784                   dd = el.initDD("default", null, {
30785                       onDrag : function(){
30786                           el.sync();  
30787                       }
30788                   });
30789                   dd.setHandleElId(tipTitle.id);
30790                   dd.lock();
30791               }
30792               inited = true;
30793           }
30794           this.enable(); 
30795        },
30796
30797     /**
30798      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30799      * are supported:
30800      * <pre>
30801 Property    Type                   Description
30802 ----------  ---------------------  ------------------------------------------------------------------------
30803 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30804      * </ul>
30805      * @param {Object} config The config object
30806      */
30807        register : function(config){
30808            var cs = config instanceof Array ? config : arguments;
30809            for(var i = 0, len = cs.length; i < len; i++) {
30810                var c = cs[i];
30811                var target = c.target;
30812                if(target){
30813                    if(target instanceof Array){
30814                        for(var j = 0, jlen = target.length; j < jlen; j++){
30815                            tagEls[target[j]] = c;
30816                        }
30817                    }else{
30818                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30819                    }
30820                }
30821            }
30822        },
30823
30824     /**
30825      * Removes this quick tip from its element and destroys it.
30826      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30827      */
30828        unregister : function(el){
30829            delete tagEls[Roo.id(el)];
30830        },
30831
30832     /**
30833      * Enable this quick tip.
30834      */
30835        enable : function(){
30836            if(inited && disabled){
30837                locks.pop();
30838                if(locks.length < 1){
30839                    disabled = false;
30840                }
30841            }
30842        },
30843
30844     /**
30845      * Disable this quick tip.
30846      */
30847        disable : function(){
30848           disabled = true;
30849           clearTimeout(showProc);
30850           clearTimeout(hideProc);
30851           clearTimeout(dismissProc);
30852           if(ce){
30853               hide(true);
30854           }
30855           locks.push(1);
30856        },
30857
30858     /**
30859      * Returns true if the quick tip is enabled, else false.
30860      */
30861        isEnabled : function(){
30862             return !disabled;
30863        },
30864
30865         // private
30866        tagConfig : {
30867            namespace : "ext",
30868            attribute : "qtip",
30869            width : "width",
30870            target : "target",
30871            title : "qtitle",
30872            hide : "hide",
30873            cls : "qclass"
30874        }
30875    };
30876 }();
30877
30878 // backwards compat
30879 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30880  * Based on:
30881  * Ext JS Library 1.1.1
30882  * Copyright(c) 2006-2007, Ext JS, LLC.
30883  *
30884  * Originally Released Under LGPL - original licence link has changed is not relivant.
30885  *
30886  * Fork - LGPL
30887  * <script type="text/javascript">
30888  */
30889  
30890
30891 /**
30892  * @class Roo.tree.TreePanel
30893  * @extends Roo.data.Tree
30894
30895  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30896  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30897  * @cfg {Boolean} enableDD true to enable drag and drop
30898  * @cfg {Boolean} enableDrag true to enable just drag
30899  * @cfg {Boolean} enableDrop true to enable just drop
30900  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30901  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30902  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30903  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30904  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30905  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30906  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30907  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30908  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30909  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30910  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30911  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30912  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30913  * @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>
30914  * @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>
30915  * 
30916  * @constructor
30917  * @param {String/HTMLElement/Element} el The container element
30918  * @param {Object} config
30919  */
30920 Roo.tree.TreePanel = function(el, config){
30921     var root = false;
30922     var loader = false;
30923     if (config.root) {
30924         root = config.root;
30925         delete config.root;
30926     }
30927     if (config.loader) {
30928         loader = config.loader;
30929         delete config.loader;
30930     }
30931     
30932     Roo.apply(this, config);
30933     Roo.tree.TreePanel.superclass.constructor.call(this);
30934     this.el = Roo.get(el);
30935     this.el.addClass('x-tree');
30936     //console.log(root);
30937     if (root) {
30938         this.setRootNode( Roo.factory(root, Roo.tree));
30939     }
30940     if (loader) {
30941         this.loader = Roo.factory(loader, Roo.tree);
30942     }
30943    /**
30944     * Read-only. The id of the container element becomes this TreePanel's id.
30945     */
30946    this.id = this.el.id;
30947    this.addEvents({
30948         /**
30949         * @event beforeload
30950         * Fires before a node is loaded, return false to cancel
30951         * @param {Node} node The node being loaded
30952         */
30953         "beforeload" : true,
30954         /**
30955         * @event load
30956         * Fires when a node is loaded
30957         * @param {Node} node The node that was loaded
30958         */
30959         "load" : true,
30960         /**
30961         * @event textchange
30962         * Fires when the text for a node is changed
30963         * @param {Node} node The node
30964         * @param {String} text The new text
30965         * @param {String} oldText The old text
30966         */
30967         "textchange" : true,
30968         /**
30969         * @event beforeexpand
30970         * Fires before a node is expanded, return false to cancel.
30971         * @param {Node} node The node
30972         * @param {Boolean} deep
30973         * @param {Boolean} anim
30974         */
30975         "beforeexpand" : true,
30976         /**
30977         * @event beforecollapse
30978         * Fires before a node is collapsed, return false to cancel.
30979         * @param {Node} node The node
30980         * @param {Boolean} deep
30981         * @param {Boolean} anim
30982         */
30983         "beforecollapse" : true,
30984         /**
30985         * @event expand
30986         * Fires when a node is expanded
30987         * @param {Node} node The node
30988         */
30989         "expand" : true,
30990         /**
30991         * @event disabledchange
30992         * Fires when the disabled status of a node changes
30993         * @param {Node} node The node
30994         * @param {Boolean} disabled
30995         */
30996         "disabledchange" : true,
30997         /**
30998         * @event collapse
30999         * Fires when a node is collapsed
31000         * @param {Node} node The node
31001         */
31002         "collapse" : true,
31003         /**
31004         * @event beforeclick
31005         * Fires before click processing on a node. Return false to cancel the default action.
31006         * @param {Node} node The node
31007         * @param {Roo.EventObject} e The event object
31008         */
31009         "beforeclick":true,
31010         /**
31011         * @event checkchange
31012         * Fires when a node with a checkbox's checked property changes
31013         * @param {Node} this This node
31014         * @param {Boolean} checked
31015         */
31016         "checkchange":true,
31017         /**
31018         * @event click
31019         * Fires when a node is clicked
31020         * @param {Node} node The node
31021         * @param {Roo.EventObject} e The event object
31022         */
31023         "click":true,
31024         /**
31025         * @event dblclick
31026         * Fires when a node is double clicked
31027         * @param {Node} node The node
31028         * @param {Roo.EventObject} e The event object
31029         */
31030         "dblclick":true,
31031         /**
31032         * @event contextmenu
31033         * Fires when a node is right clicked
31034         * @param {Node} node The node
31035         * @param {Roo.EventObject} e The event object
31036         */
31037         "contextmenu":true,
31038         /**
31039         * @event beforechildrenrendered
31040         * Fires right before the child nodes for a node are rendered
31041         * @param {Node} node The node
31042         */
31043         "beforechildrenrendered":true,
31044        /**
31045              * @event startdrag
31046              * Fires when a node starts being dragged
31047              * @param {Roo.tree.TreePanel} this
31048              * @param {Roo.tree.TreeNode} node
31049              * @param {event} e The raw browser event
31050              */ 
31051             "startdrag" : true,
31052             /**
31053              * @event enddrag
31054              * Fires when a drag operation is complete
31055              * @param {Roo.tree.TreePanel} this
31056              * @param {Roo.tree.TreeNode} node
31057              * @param {event} e The raw browser event
31058              */
31059             "enddrag" : true,
31060             /**
31061              * @event dragdrop
31062              * Fires when a dragged node is dropped on a valid DD target
31063              * @param {Roo.tree.TreePanel} this
31064              * @param {Roo.tree.TreeNode} node
31065              * @param {DD} dd The dd it was dropped on
31066              * @param {event} e The raw browser event
31067              */
31068             "dragdrop" : true,
31069             /**
31070              * @event beforenodedrop
31071              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31072              * passed to handlers has the following properties:<br />
31073              * <ul style="padding:5px;padding-left:16px;">
31074              * <li>tree - The TreePanel</li>
31075              * <li>target - The node being targeted for the drop</li>
31076              * <li>data - The drag data from the drag source</li>
31077              * <li>point - The point of the drop - append, above or below</li>
31078              * <li>source - The drag source</li>
31079              * <li>rawEvent - Raw mouse event</li>
31080              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31081              * to be inserted by setting them on this object.</li>
31082              * <li>cancel - Set this to true to cancel the drop.</li>
31083              * </ul>
31084              * @param {Object} dropEvent
31085              */
31086             "beforenodedrop" : true,
31087             /**
31088              * @event nodedrop
31089              * Fires after a DD object is dropped on a node in this tree. The dropEvent
31090              * passed to handlers has the following properties:<br />
31091              * <ul style="padding:5px;padding-left:16px;">
31092              * <li>tree - The TreePanel</li>
31093              * <li>target - The node being targeted for the drop</li>
31094              * <li>data - The drag data from the drag source</li>
31095              * <li>point - The point of the drop - append, above or below</li>
31096              * <li>source - The drag source</li>
31097              * <li>rawEvent - Raw mouse event</li>
31098              * <li>dropNode - Dropped node(s).</li>
31099              * </ul>
31100              * @param {Object} dropEvent
31101              */
31102             "nodedrop" : true,
31103              /**
31104              * @event nodedragover
31105              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31106              * passed to handlers has the following properties:<br />
31107              * <ul style="padding:5px;padding-left:16px;">
31108              * <li>tree - The TreePanel</li>
31109              * <li>target - The node being targeted for the drop</li>
31110              * <li>data - The drag data from the drag source</li>
31111              * <li>point - The point of the drop - append, above or below</li>
31112              * <li>source - The drag source</li>
31113              * <li>rawEvent - Raw mouse event</li>
31114              * <li>dropNode - Drop node(s) provided by the source.</li>
31115              * <li>cancel - Set this to true to signal drop not allowed.</li>
31116              * </ul>
31117              * @param {Object} dragOverEvent
31118              */
31119             "nodedragover" : true
31120         
31121    });
31122    if(this.singleExpand){
31123        this.on("beforeexpand", this.restrictExpand, this);
31124    }
31125 };
31126 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31127     rootVisible : true,
31128     animate: Roo.enableFx,
31129     lines : true,
31130     enableDD : false,
31131     hlDrop : Roo.enableFx,
31132   
31133     renderer: false,
31134     
31135     rendererTip: false,
31136     // private
31137     restrictExpand : function(node){
31138         var p = node.parentNode;
31139         if(p){
31140             if(p.expandedChild && p.expandedChild.parentNode == p){
31141                 p.expandedChild.collapse();
31142             }
31143             p.expandedChild = node;
31144         }
31145     },
31146
31147     // private override
31148     setRootNode : function(node){
31149         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31150         if(!this.rootVisible){
31151             node.ui = new Roo.tree.RootTreeNodeUI(node);
31152         }
31153         return node;
31154     },
31155
31156     /**
31157      * Returns the container element for this TreePanel
31158      */
31159     getEl : function(){
31160         return this.el;
31161     },
31162
31163     /**
31164      * Returns the default TreeLoader for this TreePanel
31165      */
31166     getLoader : function(){
31167         return this.loader;
31168     },
31169
31170     /**
31171      * Expand all nodes
31172      */
31173     expandAll : function(){
31174         this.root.expand(true);
31175     },
31176
31177     /**
31178      * Collapse all nodes
31179      */
31180     collapseAll : function(){
31181         this.root.collapse(true);
31182     },
31183
31184     /**
31185      * Returns the selection model used by this TreePanel
31186      */
31187     getSelectionModel : function(){
31188         if(!this.selModel){
31189             this.selModel = new Roo.tree.DefaultSelectionModel();
31190         }
31191         return this.selModel;
31192     },
31193
31194     /**
31195      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31196      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31197      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31198      * @return {Array}
31199      */
31200     getChecked : function(a, startNode){
31201         startNode = startNode || this.root;
31202         var r = [];
31203         var f = function(){
31204             if(this.attributes.checked){
31205                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31206             }
31207         }
31208         startNode.cascade(f);
31209         return r;
31210     },
31211
31212     /**
31213      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31214      * @param {String} path
31215      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31216      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31217      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31218      */
31219     expandPath : function(path, attr, callback){
31220         attr = attr || "id";
31221         var keys = path.split(this.pathSeparator);
31222         var curNode = this.root;
31223         if(curNode.attributes[attr] != keys[1]){ // invalid root
31224             if(callback){
31225                 callback(false, null);
31226             }
31227             return;
31228         }
31229         var index = 1;
31230         var f = function(){
31231             if(++index == keys.length){
31232                 if(callback){
31233                     callback(true, curNode);
31234                 }
31235                 return;
31236             }
31237             var c = curNode.findChild(attr, keys[index]);
31238             if(!c){
31239                 if(callback){
31240                     callback(false, curNode);
31241                 }
31242                 return;
31243             }
31244             curNode = c;
31245             c.expand(false, false, f);
31246         };
31247         curNode.expand(false, false, f);
31248     },
31249
31250     /**
31251      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31252      * @param {String} path
31253      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31254      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31255      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31256      */
31257     selectPath : function(path, attr, callback){
31258         attr = attr || "id";
31259         var keys = path.split(this.pathSeparator);
31260         var v = keys.pop();
31261         if(keys.length > 0){
31262             var f = function(success, node){
31263                 if(success && node){
31264                     var n = node.findChild(attr, v);
31265                     if(n){
31266                         n.select();
31267                         if(callback){
31268                             callback(true, n);
31269                         }
31270                     }else if(callback){
31271                         callback(false, n);
31272                     }
31273                 }else{
31274                     if(callback){
31275                         callback(false, n);
31276                     }
31277                 }
31278             };
31279             this.expandPath(keys.join(this.pathSeparator), attr, f);
31280         }else{
31281             this.root.select();
31282             if(callback){
31283                 callback(true, this.root);
31284             }
31285         }
31286     },
31287
31288     getTreeEl : function(){
31289         return this.el;
31290     },
31291
31292     /**
31293      * Trigger rendering of this TreePanel
31294      */
31295     render : function(){
31296         if (this.innerCt) {
31297             return this; // stop it rendering more than once!!
31298         }
31299         
31300         this.innerCt = this.el.createChild({tag:"ul",
31301                cls:"x-tree-root-ct " +
31302                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31303
31304         if(this.containerScroll){
31305             Roo.dd.ScrollManager.register(this.el);
31306         }
31307         if((this.enableDD || this.enableDrop) && !this.dropZone){
31308            /**
31309             * The dropZone used by this tree if drop is enabled
31310             * @type Roo.tree.TreeDropZone
31311             */
31312              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31313                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31314            });
31315         }
31316         if((this.enableDD || this.enableDrag) && !this.dragZone){
31317            /**
31318             * The dragZone used by this tree if drag is enabled
31319             * @type Roo.tree.TreeDragZone
31320             */
31321             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31322                ddGroup: this.ddGroup || "TreeDD",
31323                scroll: this.ddScroll
31324            });
31325         }
31326         this.getSelectionModel().init(this);
31327         if (!this.root) {
31328             console.log("ROOT not set in tree");
31329             return;
31330         }
31331         this.root.render();
31332         if(!this.rootVisible){
31333             this.root.renderChildren();
31334         }
31335         return this;
31336     }
31337 });/*
31338  * Based on:
31339  * Ext JS Library 1.1.1
31340  * Copyright(c) 2006-2007, Ext JS, LLC.
31341  *
31342  * Originally Released Under LGPL - original licence link has changed is not relivant.
31343  *
31344  * Fork - LGPL
31345  * <script type="text/javascript">
31346  */
31347  
31348
31349 /**
31350  * @class Roo.tree.DefaultSelectionModel
31351  * @extends Roo.util.Observable
31352  * The default single selection for a TreePanel.
31353  */
31354 Roo.tree.DefaultSelectionModel = function(){
31355    this.selNode = null;
31356    
31357    this.addEvents({
31358        /**
31359         * @event selectionchange
31360         * Fires when the selected node changes
31361         * @param {DefaultSelectionModel} this
31362         * @param {TreeNode} node the new selection
31363         */
31364        "selectionchange" : true,
31365
31366        /**
31367         * @event beforeselect
31368         * Fires before the selected node changes, return false to cancel the change
31369         * @param {DefaultSelectionModel} this
31370         * @param {TreeNode} node the new selection
31371         * @param {TreeNode} node the old selection
31372         */
31373        "beforeselect" : true
31374    });
31375 };
31376
31377 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31378     init : function(tree){
31379         this.tree = tree;
31380         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31381         tree.on("click", this.onNodeClick, this);
31382     },
31383     
31384     onNodeClick : function(node, e){
31385         if (e.ctrlKey && this.selNode == node)  {
31386             this.unselect(node);
31387             return;
31388         }
31389         this.select(node);
31390     },
31391     
31392     /**
31393      * Select a node.
31394      * @param {TreeNode} node The node to select
31395      * @return {TreeNode} The selected node
31396      */
31397     select : function(node){
31398         var last = this.selNode;
31399         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31400             if(last){
31401                 last.ui.onSelectedChange(false);
31402             }
31403             this.selNode = node;
31404             node.ui.onSelectedChange(true);
31405             this.fireEvent("selectionchange", this, node, last);
31406         }
31407         return node;
31408     },
31409     
31410     /**
31411      * Deselect a node.
31412      * @param {TreeNode} node The node to unselect
31413      */
31414     unselect : function(node){
31415         if(this.selNode == node){
31416             this.clearSelections();
31417         }    
31418     },
31419     
31420     /**
31421      * Clear all selections
31422      */
31423     clearSelections : function(){
31424         var n = this.selNode;
31425         if(n){
31426             n.ui.onSelectedChange(false);
31427             this.selNode = null;
31428             this.fireEvent("selectionchange", this, null);
31429         }
31430         return n;
31431     },
31432     
31433     /**
31434      * Get the selected node
31435      * @return {TreeNode} The selected node
31436      */
31437     getSelectedNode : function(){
31438         return this.selNode;    
31439     },
31440     
31441     /**
31442      * Returns true if the node is selected
31443      * @param {TreeNode} node The node to check
31444      * @return {Boolean}
31445      */
31446     isSelected : function(node){
31447         return this.selNode == node;  
31448     },
31449
31450     /**
31451      * Selects the node above the selected node in the tree, intelligently walking the nodes
31452      * @return TreeNode The new selection
31453      */
31454     selectPrevious : function(){
31455         var s = this.selNode || this.lastSelNode;
31456         if(!s){
31457             return null;
31458         }
31459         var ps = s.previousSibling;
31460         if(ps){
31461             if(!ps.isExpanded() || ps.childNodes.length < 1){
31462                 return this.select(ps);
31463             } else{
31464                 var lc = ps.lastChild;
31465                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31466                     lc = lc.lastChild;
31467                 }
31468                 return this.select(lc);
31469             }
31470         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31471             return this.select(s.parentNode);
31472         }
31473         return null;
31474     },
31475
31476     /**
31477      * Selects the node above the selected node in the tree, intelligently walking the nodes
31478      * @return TreeNode The new selection
31479      */
31480     selectNext : function(){
31481         var s = this.selNode || this.lastSelNode;
31482         if(!s){
31483             return null;
31484         }
31485         if(s.firstChild && s.isExpanded()){
31486              return this.select(s.firstChild);
31487          }else if(s.nextSibling){
31488              return this.select(s.nextSibling);
31489          }else if(s.parentNode){
31490             var newS = null;
31491             s.parentNode.bubble(function(){
31492                 if(this.nextSibling){
31493                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31494                     return false;
31495                 }
31496             });
31497             return newS;
31498          }
31499         return null;
31500     },
31501
31502     onKeyDown : function(e){
31503         var s = this.selNode || this.lastSelNode;
31504         // undesirable, but required
31505         var sm = this;
31506         if(!s){
31507             return;
31508         }
31509         var k = e.getKey();
31510         switch(k){
31511              case e.DOWN:
31512                  e.stopEvent();
31513                  this.selectNext();
31514              break;
31515              case e.UP:
31516                  e.stopEvent();
31517                  this.selectPrevious();
31518              break;
31519              case e.RIGHT:
31520                  e.preventDefault();
31521                  if(s.hasChildNodes()){
31522                      if(!s.isExpanded()){
31523                          s.expand();
31524                      }else if(s.firstChild){
31525                          this.select(s.firstChild, e);
31526                      }
31527                  }
31528              break;
31529              case e.LEFT:
31530                  e.preventDefault();
31531                  if(s.hasChildNodes() && s.isExpanded()){
31532                      s.collapse();
31533                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31534                      this.select(s.parentNode, e);
31535                  }
31536              break;
31537         };
31538     }
31539 });
31540
31541 /**
31542  * @class Roo.tree.MultiSelectionModel
31543  * @extends Roo.util.Observable
31544  * Multi selection for a TreePanel.
31545  */
31546 Roo.tree.MultiSelectionModel = function(){
31547    this.selNodes = [];
31548    this.selMap = {};
31549    this.addEvents({
31550        /**
31551         * @event selectionchange
31552         * Fires when the selected nodes change
31553         * @param {MultiSelectionModel} this
31554         * @param {Array} nodes Array of the selected nodes
31555         */
31556        "selectionchange" : true
31557    });
31558 };
31559
31560 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31561     init : function(tree){
31562         this.tree = tree;
31563         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31564         tree.on("click", this.onNodeClick, this);
31565     },
31566     
31567     onNodeClick : function(node, e){
31568         this.select(node, e, e.ctrlKey);
31569     },
31570     
31571     /**
31572      * Select a node.
31573      * @param {TreeNode} node The node to select
31574      * @param {EventObject} e (optional) An event associated with the selection
31575      * @param {Boolean} keepExisting True to retain existing selections
31576      * @return {TreeNode} The selected node
31577      */
31578     select : function(node, e, keepExisting){
31579         if(keepExisting !== true){
31580             this.clearSelections(true);
31581         }
31582         if(this.isSelected(node)){
31583             this.lastSelNode = node;
31584             return node;
31585         }
31586         this.selNodes.push(node);
31587         this.selMap[node.id] = node;
31588         this.lastSelNode = node;
31589         node.ui.onSelectedChange(true);
31590         this.fireEvent("selectionchange", this, this.selNodes);
31591         return node;
31592     },
31593     
31594     /**
31595      * Deselect a node.
31596      * @param {TreeNode} node The node to unselect
31597      */
31598     unselect : function(node){
31599         if(this.selMap[node.id]){
31600             node.ui.onSelectedChange(false);
31601             var sn = this.selNodes;
31602             var index = -1;
31603             if(sn.indexOf){
31604                 index = sn.indexOf(node);
31605             }else{
31606                 for(var i = 0, len = sn.length; i < len; i++){
31607                     if(sn[i] == node){
31608                         index = i;
31609                         break;
31610                     }
31611                 }
31612             }
31613             if(index != -1){
31614                 this.selNodes.splice(index, 1);
31615             }
31616             delete this.selMap[node.id];
31617             this.fireEvent("selectionchange", this, this.selNodes);
31618         }
31619     },
31620     
31621     /**
31622      * Clear all selections
31623      */
31624     clearSelections : function(suppressEvent){
31625         var sn = this.selNodes;
31626         if(sn.length > 0){
31627             for(var i = 0, len = sn.length; i < len; i++){
31628                 sn[i].ui.onSelectedChange(false);
31629             }
31630             this.selNodes = [];
31631             this.selMap = {};
31632             if(suppressEvent !== true){
31633                 this.fireEvent("selectionchange", this, this.selNodes);
31634             }
31635         }
31636     },
31637     
31638     /**
31639      * Returns true if the node is selected
31640      * @param {TreeNode} node The node to check
31641      * @return {Boolean}
31642      */
31643     isSelected : function(node){
31644         return this.selMap[node.id] ? true : false;  
31645     },
31646     
31647     /**
31648      * Returns an array of the selected nodes
31649      * @return {Array}
31650      */
31651     getSelectedNodes : function(){
31652         return this.selNodes;    
31653     },
31654
31655     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31656
31657     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31658
31659     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31660 });/*
31661  * Based on:
31662  * Ext JS Library 1.1.1
31663  * Copyright(c) 2006-2007, Ext JS, LLC.
31664  *
31665  * Originally Released Under LGPL - original licence link has changed is not relivant.
31666  *
31667  * Fork - LGPL
31668  * <script type="text/javascript">
31669  */
31670  
31671 /**
31672  * @class Roo.tree.TreeNode
31673  * @extends Roo.data.Node
31674  * @cfg {String} text The text for this node
31675  * @cfg {Boolean} expanded true to start the node expanded
31676  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31677  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31678  * @cfg {Boolean} disabled true to start the node disabled
31679  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31680  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31681  * @cfg {String} cls A css class to be added to the node
31682  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31683  * @cfg {String} href URL of the link used for the node (defaults to #)
31684  * @cfg {String} hrefTarget target frame for the link
31685  * @cfg {String} qtip An Ext QuickTip for the node
31686  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31687  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31688  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31689  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31690  * (defaults to undefined with no checkbox rendered)
31691  * @constructor
31692  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31693  */
31694 Roo.tree.TreeNode = function(attributes){
31695     attributes = attributes || {};
31696     if(typeof attributes == "string"){
31697         attributes = {text: attributes};
31698     }
31699     this.childrenRendered = false;
31700     this.rendered = false;
31701     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31702     this.expanded = attributes.expanded === true;
31703     this.isTarget = attributes.isTarget !== false;
31704     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31705     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31706
31707     /**
31708      * Read-only. The text for this node. To change it use setText().
31709      * @type String
31710      */
31711     this.text = attributes.text;
31712     /**
31713      * True if this node is disabled.
31714      * @type Boolean
31715      */
31716     this.disabled = attributes.disabled === true;
31717
31718     this.addEvents({
31719         /**
31720         * @event textchange
31721         * Fires when the text for this node is changed
31722         * @param {Node} this This node
31723         * @param {String} text The new text
31724         * @param {String} oldText The old text
31725         */
31726         "textchange" : true,
31727         /**
31728         * @event beforeexpand
31729         * Fires before this node is expanded, return false to cancel.
31730         * @param {Node} this This node
31731         * @param {Boolean} deep
31732         * @param {Boolean} anim
31733         */
31734         "beforeexpand" : true,
31735         /**
31736         * @event beforecollapse
31737         * Fires before this node is collapsed, return false to cancel.
31738         * @param {Node} this This node
31739         * @param {Boolean} deep
31740         * @param {Boolean} anim
31741         */
31742         "beforecollapse" : true,
31743         /**
31744         * @event expand
31745         * Fires when this node is expanded
31746         * @param {Node} this This node
31747         */
31748         "expand" : true,
31749         /**
31750         * @event disabledchange
31751         * Fires when the disabled status of this node changes
31752         * @param {Node} this This node
31753         * @param {Boolean} disabled
31754         */
31755         "disabledchange" : true,
31756         /**
31757         * @event collapse
31758         * Fires when this node is collapsed
31759         * @param {Node} this This node
31760         */
31761         "collapse" : true,
31762         /**
31763         * @event beforeclick
31764         * Fires before click processing. Return false to cancel the default action.
31765         * @param {Node} this This node
31766         * @param {Roo.EventObject} e The event object
31767         */
31768         "beforeclick":true,
31769         /**
31770         * @event checkchange
31771         * Fires when a node with a checkbox's checked property changes
31772         * @param {Node} this This node
31773         * @param {Boolean} checked
31774         */
31775         "checkchange":true,
31776         /**
31777         * @event click
31778         * Fires when this node is clicked
31779         * @param {Node} this This node
31780         * @param {Roo.EventObject} e The event object
31781         */
31782         "click":true,
31783         /**
31784         * @event dblclick
31785         * Fires when this node is double clicked
31786         * @param {Node} this This node
31787         * @param {Roo.EventObject} e The event object
31788         */
31789         "dblclick":true,
31790         /**
31791         * @event contextmenu
31792         * Fires when this node is right clicked
31793         * @param {Node} this This node
31794         * @param {Roo.EventObject} e The event object
31795         */
31796         "contextmenu":true,
31797         /**
31798         * @event beforechildrenrendered
31799         * Fires right before the child nodes for this node are rendered
31800         * @param {Node} this This node
31801         */
31802         "beforechildrenrendered":true
31803     });
31804
31805     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31806
31807     /**
31808      * Read-only. The UI for this node
31809      * @type TreeNodeUI
31810      */
31811     this.ui = new uiClass(this);
31812 };
31813 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31814     preventHScroll: true,
31815     /**
31816      * Returns true if this node is expanded
31817      * @return {Boolean}
31818      */
31819     isExpanded : function(){
31820         return this.expanded;
31821     },
31822
31823     /**
31824      * Returns the UI object for this node
31825      * @return {TreeNodeUI}
31826      */
31827     getUI : function(){
31828         return this.ui;
31829     },
31830
31831     // private override
31832     setFirstChild : function(node){
31833         var of = this.firstChild;
31834         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31835         if(this.childrenRendered && of && node != of){
31836             of.renderIndent(true, true);
31837         }
31838         if(this.rendered){
31839             this.renderIndent(true, true);
31840         }
31841     },
31842
31843     // private override
31844     setLastChild : function(node){
31845         var ol = this.lastChild;
31846         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31847         if(this.childrenRendered && ol && node != ol){
31848             ol.renderIndent(true, true);
31849         }
31850         if(this.rendered){
31851             this.renderIndent(true, true);
31852         }
31853     },
31854
31855     // these methods are overridden to provide lazy rendering support
31856     // private override
31857     appendChild : function(){
31858         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31859         if(node && this.childrenRendered){
31860             node.render();
31861         }
31862         this.ui.updateExpandIcon();
31863         return node;
31864     },
31865
31866     // private override
31867     removeChild : function(node){
31868         this.ownerTree.getSelectionModel().unselect(node);
31869         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31870         // if it's been rendered remove dom node
31871         if(this.childrenRendered){
31872             node.ui.remove();
31873         }
31874         if(this.childNodes.length < 1){
31875             this.collapse(false, false);
31876         }else{
31877             this.ui.updateExpandIcon();
31878         }
31879         if(!this.firstChild) {
31880             this.childrenRendered = false;
31881         }
31882         return node;
31883     },
31884
31885     // private override
31886     insertBefore : function(node, refNode){
31887         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31888         if(newNode && refNode && this.childrenRendered){
31889             node.render();
31890         }
31891         this.ui.updateExpandIcon();
31892         return newNode;
31893     },
31894
31895     /**
31896      * Sets the text for this node
31897      * @param {String} text
31898      */
31899     setText : function(text){
31900         var oldText = this.text;
31901         this.text = text;
31902         this.attributes.text = text;
31903         if(this.rendered){ // event without subscribing
31904             this.ui.onTextChange(this, text, oldText);
31905         }
31906         this.fireEvent("textchange", this, text, oldText);
31907     },
31908
31909     /**
31910      * Triggers selection of this node
31911      */
31912     select : function(){
31913         this.getOwnerTree().getSelectionModel().select(this);
31914     },
31915
31916     /**
31917      * Triggers deselection of this node
31918      */
31919     unselect : function(){
31920         this.getOwnerTree().getSelectionModel().unselect(this);
31921     },
31922
31923     /**
31924      * Returns true if this node is selected
31925      * @return {Boolean}
31926      */
31927     isSelected : function(){
31928         return this.getOwnerTree().getSelectionModel().isSelected(this);
31929     },
31930
31931     /**
31932      * Expand this node.
31933      * @param {Boolean} deep (optional) True to expand all children as well
31934      * @param {Boolean} anim (optional) false to cancel the default animation
31935      * @param {Function} callback (optional) A callback to be called when
31936      * expanding this node completes (does not wait for deep expand to complete).
31937      * Called with 1 parameter, this node.
31938      */
31939     expand : function(deep, anim, callback){
31940         if(!this.expanded){
31941             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31942                 return;
31943             }
31944             if(!this.childrenRendered){
31945                 this.renderChildren();
31946             }
31947             this.expanded = true;
31948             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31949                 this.ui.animExpand(function(){
31950                     this.fireEvent("expand", this);
31951                     if(typeof callback == "function"){
31952                         callback(this);
31953                     }
31954                     if(deep === true){
31955                         this.expandChildNodes(true);
31956                     }
31957                 }.createDelegate(this));
31958                 return;
31959             }else{
31960                 this.ui.expand();
31961                 this.fireEvent("expand", this);
31962                 if(typeof callback == "function"){
31963                     callback(this);
31964                 }
31965             }
31966         }else{
31967            if(typeof callback == "function"){
31968                callback(this);
31969            }
31970         }
31971         if(deep === true){
31972             this.expandChildNodes(true);
31973         }
31974     },
31975
31976     isHiddenRoot : function(){
31977         return this.isRoot && !this.getOwnerTree().rootVisible;
31978     },
31979
31980     /**
31981      * Collapse this node.
31982      * @param {Boolean} deep (optional) True to collapse all children as well
31983      * @param {Boolean} anim (optional) false to cancel the default animation
31984      */
31985     collapse : function(deep, anim){
31986         if(this.expanded && !this.isHiddenRoot()){
31987             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31988                 return;
31989             }
31990             this.expanded = false;
31991             if((this.getOwnerTree().animate && anim !== false) || anim){
31992                 this.ui.animCollapse(function(){
31993                     this.fireEvent("collapse", this);
31994                     if(deep === true){
31995                         this.collapseChildNodes(true);
31996                     }
31997                 }.createDelegate(this));
31998                 return;
31999             }else{
32000                 this.ui.collapse();
32001                 this.fireEvent("collapse", this);
32002             }
32003         }
32004         if(deep === true){
32005             var cs = this.childNodes;
32006             for(var i = 0, len = cs.length; i < len; i++) {
32007                 cs[i].collapse(true, false);
32008             }
32009         }
32010     },
32011
32012     // private
32013     delayedExpand : function(delay){
32014         if(!this.expandProcId){
32015             this.expandProcId = this.expand.defer(delay, this);
32016         }
32017     },
32018
32019     // private
32020     cancelExpand : function(){
32021         if(this.expandProcId){
32022             clearTimeout(this.expandProcId);
32023         }
32024         this.expandProcId = false;
32025     },
32026
32027     /**
32028      * Toggles expanded/collapsed state of the node
32029      */
32030     toggle : function(){
32031         if(this.expanded){
32032             this.collapse();
32033         }else{
32034             this.expand();
32035         }
32036     },
32037
32038     /**
32039      * Ensures all parent nodes are expanded
32040      */
32041     ensureVisible : function(callback){
32042         var tree = this.getOwnerTree();
32043         tree.expandPath(this.parentNode.getPath(), false, function(){
32044             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32045             Roo.callback(callback);
32046         }.createDelegate(this));
32047     },
32048
32049     /**
32050      * Expand all child nodes
32051      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32052      */
32053     expandChildNodes : function(deep){
32054         var cs = this.childNodes;
32055         for(var i = 0, len = cs.length; i < len; i++) {
32056                 cs[i].expand(deep);
32057         }
32058     },
32059
32060     /**
32061      * Collapse all child nodes
32062      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32063      */
32064     collapseChildNodes : function(deep){
32065         var cs = this.childNodes;
32066         for(var i = 0, len = cs.length; i < len; i++) {
32067                 cs[i].collapse(deep);
32068         }
32069     },
32070
32071     /**
32072      * Disables this node
32073      */
32074     disable : function(){
32075         this.disabled = true;
32076         this.unselect();
32077         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32078             this.ui.onDisableChange(this, true);
32079         }
32080         this.fireEvent("disabledchange", this, true);
32081     },
32082
32083     /**
32084      * Enables this node
32085      */
32086     enable : function(){
32087         this.disabled = false;
32088         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32089             this.ui.onDisableChange(this, false);
32090         }
32091         this.fireEvent("disabledchange", this, false);
32092     },
32093
32094     // private
32095     renderChildren : function(suppressEvent){
32096         if(suppressEvent !== false){
32097             this.fireEvent("beforechildrenrendered", this);
32098         }
32099         var cs = this.childNodes;
32100         for(var i = 0, len = cs.length; i < len; i++){
32101             cs[i].render(true);
32102         }
32103         this.childrenRendered = true;
32104     },
32105
32106     // private
32107     sort : function(fn, scope){
32108         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32109         if(this.childrenRendered){
32110             var cs = this.childNodes;
32111             for(var i = 0, len = cs.length; i < len; i++){
32112                 cs[i].render(true);
32113             }
32114         }
32115     },
32116
32117     // private
32118     render : function(bulkRender){
32119         this.ui.render(bulkRender);
32120         if(!this.rendered){
32121             this.rendered = true;
32122             if(this.expanded){
32123                 this.expanded = false;
32124                 this.expand(false, false);
32125             }
32126         }
32127     },
32128
32129     // private
32130     renderIndent : function(deep, refresh){
32131         if(refresh){
32132             this.ui.childIndent = null;
32133         }
32134         this.ui.renderIndent();
32135         if(deep === true && this.childrenRendered){
32136             var cs = this.childNodes;
32137             for(var i = 0, len = cs.length; i < len; i++){
32138                 cs[i].renderIndent(true, refresh);
32139             }
32140         }
32141     }
32142 });/*
32143  * Based on:
32144  * Ext JS Library 1.1.1
32145  * Copyright(c) 2006-2007, Ext JS, LLC.
32146  *
32147  * Originally Released Under LGPL - original licence link has changed is not relivant.
32148  *
32149  * Fork - LGPL
32150  * <script type="text/javascript">
32151  */
32152  
32153 /**
32154  * @class Roo.tree.AsyncTreeNode
32155  * @extends Roo.tree.TreeNode
32156  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32157  * @constructor
32158  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32159  */
32160  Roo.tree.AsyncTreeNode = function(config){
32161     this.loaded = false;
32162     this.loading = false;
32163     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32164     /**
32165     * @event beforeload
32166     * Fires before this node is loaded, return false to cancel
32167     * @param {Node} this This node
32168     */
32169     this.addEvents({'beforeload':true, 'load': true});
32170     /**
32171     * @event load
32172     * Fires when this node is loaded
32173     * @param {Node} this This node
32174     */
32175     /**
32176      * The loader used by this node (defaults to using the tree's defined loader)
32177      * @type TreeLoader
32178      * @property loader
32179      */
32180 };
32181 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32182     expand : function(deep, anim, callback){
32183         if(this.loading){ // if an async load is already running, waiting til it's done
32184             var timer;
32185             var f = function(){
32186                 if(!this.loading){ // done loading
32187                     clearInterval(timer);
32188                     this.expand(deep, anim, callback);
32189                 }
32190             }.createDelegate(this);
32191             timer = setInterval(f, 200);
32192             return;
32193         }
32194         if(!this.loaded){
32195             if(this.fireEvent("beforeload", this) === false){
32196                 return;
32197             }
32198             this.loading = true;
32199             this.ui.beforeLoad(this);
32200             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32201             if(loader){
32202                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32203                 return;
32204             }
32205         }
32206         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32207     },
32208     
32209     /**
32210      * Returns true if this node is currently loading
32211      * @return {Boolean}
32212      */
32213     isLoading : function(){
32214         return this.loading;  
32215     },
32216     
32217     loadComplete : function(deep, anim, callback){
32218         this.loading = false;
32219         this.loaded = true;
32220         this.ui.afterLoad(this);
32221         this.fireEvent("load", this);
32222         this.expand(deep, anim, callback);
32223     },
32224     
32225     /**
32226      * Returns true if this node has been loaded
32227      * @return {Boolean}
32228      */
32229     isLoaded : function(){
32230         return this.loaded;
32231     },
32232     
32233     hasChildNodes : function(){
32234         if(!this.isLeaf() && !this.loaded){
32235             return true;
32236         }else{
32237             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32238         }
32239     },
32240
32241     /**
32242      * Trigger a reload for this node
32243      * @param {Function} callback
32244      */
32245     reload : function(callback){
32246         this.collapse(false, false);
32247         while(this.firstChild){
32248             this.removeChild(this.firstChild);
32249         }
32250         this.childrenRendered = false;
32251         this.loaded = false;
32252         if(this.isHiddenRoot()){
32253             this.expanded = false;
32254         }
32255         this.expand(false, false, callback);
32256     }
32257 });/*
32258  * Based on:
32259  * Ext JS Library 1.1.1
32260  * Copyright(c) 2006-2007, Ext JS, LLC.
32261  *
32262  * Originally Released Under LGPL - original licence link has changed is not relivant.
32263  *
32264  * Fork - LGPL
32265  * <script type="text/javascript">
32266  */
32267  
32268 /**
32269  * @class Roo.tree.TreeNodeUI
32270  * @constructor
32271  * @param {Object} node The node to render
32272  * The TreeNode UI implementation is separate from the
32273  * tree implementation. Unless you are customizing the tree UI,
32274  * you should never have to use this directly.
32275  */
32276 Roo.tree.TreeNodeUI = function(node){
32277     this.node = node;
32278     this.rendered = false;
32279     this.animating = false;
32280     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32281 };
32282
32283 Roo.tree.TreeNodeUI.prototype = {
32284     removeChild : function(node){
32285         if(this.rendered){
32286             this.ctNode.removeChild(node.ui.getEl());
32287         }
32288     },
32289
32290     beforeLoad : function(){
32291          this.addClass("x-tree-node-loading");
32292     },
32293
32294     afterLoad : function(){
32295          this.removeClass("x-tree-node-loading");
32296     },
32297
32298     onTextChange : function(node, text, oldText){
32299         if(this.rendered){
32300             this.textNode.innerHTML = text;
32301         }
32302     },
32303
32304     onDisableChange : function(node, state){
32305         this.disabled = state;
32306         if(state){
32307             this.addClass("x-tree-node-disabled");
32308         }else{
32309             this.removeClass("x-tree-node-disabled");
32310         }
32311     },
32312
32313     onSelectedChange : function(state){
32314         if(state){
32315             this.focus();
32316             this.addClass("x-tree-selected");
32317         }else{
32318             //this.blur();
32319             this.removeClass("x-tree-selected");
32320         }
32321     },
32322
32323     onMove : function(tree, node, oldParent, newParent, index, refNode){
32324         this.childIndent = null;
32325         if(this.rendered){
32326             var targetNode = newParent.ui.getContainer();
32327             if(!targetNode){//target not rendered
32328                 this.holder = document.createElement("div");
32329                 this.holder.appendChild(this.wrap);
32330                 return;
32331             }
32332             var insertBefore = refNode ? refNode.ui.getEl() : null;
32333             if(insertBefore){
32334                 targetNode.insertBefore(this.wrap, insertBefore);
32335             }else{
32336                 targetNode.appendChild(this.wrap);
32337             }
32338             this.node.renderIndent(true);
32339         }
32340     },
32341
32342     addClass : function(cls){
32343         if(this.elNode){
32344             Roo.fly(this.elNode).addClass(cls);
32345         }
32346     },
32347
32348     removeClass : function(cls){
32349         if(this.elNode){
32350             Roo.fly(this.elNode).removeClass(cls);
32351         }
32352     },
32353
32354     remove : function(){
32355         if(this.rendered){
32356             this.holder = document.createElement("div");
32357             this.holder.appendChild(this.wrap);
32358         }
32359     },
32360
32361     fireEvent : function(){
32362         return this.node.fireEvent.apply(this.node, arguments);
32363     },
32364
32365     initEvents : function(){
32366         this.node.on("move", this.onMove, this);
32367         var E = Roo.EventManager;
32368         var a = this.anchor;
32369
32370         var el = Roo.fly(a, '_treeui');
32371
32372         if(Roo.isOpera){ // opera render bug ignores the CSS
32373             el.setStyle("text-decoration", "none");
32374         }
32375
32376         el.on("click", this.onClick, this);
32377         el.on("dblclick", this.onDblClick, this);
32378
32379         if(this.checkbox){
32380             Roo.EventManager.on(this.checkbox,
32381                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32382         }
32383
32384         el.on("contextmenu", this.onContextMenu, this);
32385
32386         var icon = Roo.fly(this.iconNode);
32387         icon.on("click", this.onClick, this);
32388         icon.on("dblclick", this.onDblClick, this);
32389         icon.on("contextmenu", this.onContextMenu, this);
32390         E.on(this.ecNode, "click", this.ecClick, this, true);
32391
32392         if(this.node.disabled){
32393             this.addClass("x-tree-node-disabled");
32394         }
32395         if(this.node.hidden){
32396             this.addClass("x-tree-node-disabled");
32397         }
32398         var ot = this.node.getOwnerTree();
32399         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32400         if(dd && (!this.node.isRoot || ot.rootVisible)){
32401             Roo.dd.Registry.register(this.elNode, {
32402                 node: this.node,
32403                 handles: this.getDDHandles(),
32404                 isHandle: false
32405             });
32406         }
32407     },
32408
32409     getDDHandles : function(){
32410         return [this.iconNode, this.textNode];
32411     },
32412
32413     hide : function(){
32414         if(this.rendered){
32415             this.wrap.style.display = "none";
32416         }
32417     },
32418
32419     show : function(){
32420         if(this.rendered){
32421             this.wrap.style.display = "";
32422         }
32423     },
32424
32425     onContextMenu : function(e){
32426         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32427             e.preventDefault();
32428             this.focus();
32429             this.fireEvent("contextmenu", this.node, e);
32430         }
32431     },
32432
32433     onClick : function(e){
32434         if(this.dropping){
32435             e.stopEvent();
32436             return;
32437         }
32438         if(this.fireEvent("beforeclick", this.node, e) !== false){
32439             if(!this.disabled && this.node.attributes.href){
32440                 this.fireEvent("click", this.node, e);
32441                 return;
32442             }
32443             e.preventDefault();
32444             if(this.disabled){
32445                 return;
32446             }
32447
32448             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32449                 this.node.toggle();
32450             }
32451
32452             this.fireEvent("click", this.node, e);
32453         }else{
32454             e.stopEvent();
32455         }
32456     },
32457
32458     onDblClick : function(e){
32459         e.preventDefault();
32460         if(this.disabled){
32461             return;
32462         }
32463         if(this.checkbox){
32464             this.toggleCheck();
32465         }
32466         if(!this.animating && this.node.hasChildNodes()){
32467             this.node.toggle();
32468         }
32469         this.fireEvent("dblclick", this.node, e);
32470     },
32471
32472     onCheckChange : function(){
32473         var checked = this.checkbox.checked;
32474         this.node.attributes.checked = checked;
32475         this.fireEvent('checkchange', this.node, checked);
32476     },
32477
32478     ecClick : function(e){
32479         if(!this.animating && this.node.hasChildNodes()){
32480             this.node.toggle();
32481         }
32482     },
32483
32484     startDrop : function(){
32485         this.dropping = true;
32486     },
32487
32488     // delayed drop so the click event doesn't get fired on a drop
32489     endDrop : function(){
32490        setTimeout(function(){
32491            this.dropping = false;
32492        }.createDelegate(this), 50);
32493     },
32494
32495     expand : function(){
32496         this.updateExpandIcon();
32497         this.ctNode.style.display = "";
32498     },
32499
32500     focus : function(){
32501         if(!this.node.preventHScroll){
32502             try{this.anchor.focus();
32503             }catch(e){}
32504         }else if(!Roo.isIE){
32505             try{
32506                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32507                 var l = noscroll.scrollLeft;
32508                 this.anchor.focus();
32509                 noscroll.scrollLeft = l;
32510             }catch(e){}
32511         }
32512     },
32513
32514     toggleCheck : function(value){
32515         var cb = this.checkbox;
32516         if(cb){
32517             cb.checked = (value === undefined ? !cb.checked : value);
32518         }
32519     },
32520
32521     blur : function(){
32522         try{
32523             this.anchor.blur();
32524         }catch(e){}
32525     },
32526
32527     animExpand : function(callback){
32528         var ct = Roo.get(this.ctNode);
32529         ct.stopFx();
32530         if(!this.node.hasChildNodes()){
32531             this.updateExpandIcon();
32532             this.ctNode.style.display = "";
32533             Roo.callback(callback);
32534             return;
32535         }
32536         this.animating = true;
32537         this.updateExpandIcon();
32538
32539         ct.slideIn('t', {
32540            callback : function(){
32541                this.animating = false;
32542                Roo.callback(callback);
32543             },
32544             scope: this,
32545             duration: this.node.ownerTree.duration || .25
32546         });
32547     },
32548
32549     highlight : function(){
32550         var tree = this.node.getOwnerTree();
32551         Roo.fly(this.wrap).highlight(
32552             tree.hlColor || "C3DAF9",
32553             {endColor: tree.hlBaseColor}
32554         );
32555     },
32556
32557     collapse : function(){
32558         this.updateExpandIcon();
32559         this.ctNode.style.display = "none";
32560     },
32561
32562     animCollapse : function(callback){
32563         var ct = Roo.get(this.ctNode);
32564         ct.enableDisplayMode('block');
32565         ct.stopFx();
32566
32567         this.animating = true;
32568         this.updateExpandIcon();
32569
32570         ct.slideOut('t', {
32571             callback : function(){
32572                this.animating = false;
32573                Roo.callback(callback);
32574             },
32575             scope: this,
32576             duration: this.node.ownerTree.duration || .25
32577         });
32578     },
32579
32580     getContainer : function(){
32581         return this.ctNode;
32582     },
32583
32584     getEl : function(){
32585         return this.wrap;
32586     },
32587
32588     appendDDGhost : function(ghostNode){
32589         ghostNode.appendChild(this.elNode.cloneNode(true));
32590     },
32591
32592     getDDRepairXY : function(){
32593         return Roo.lib.Dom.getXY(this.iconNode);
32594     },
32595
32596     onRender : function(){
32597         this.render();
32598     },
32599
32600     render : function(bulkRender){
32601         var n = this.node, a = n.attributes;
32602         var targetNode = n.parentNode ?
32603               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32604
32605         if(!this.rendered){
32606             this.rendered = true;
32607
32608             this.renderElements(n, a, targetNode, bulkRender);
32609
32610             if(a.qtip){
32611                if(this.textNode.setAttributeNS){
32612                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32613                    if(a.qtipTitle){
32614                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32615                    }
32616                }else{
32617                    this.textNode.setAttribute("ext:qtip", a.qtip);
32618                    if(a.qtipTitle){
32619                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32620                    }
32621                }
32622             }else if(a.qtipCfg){
32623                 a.qtipCfg.target = Roo.id(this.textNode);
32624                 Roo.QuickTips.register(a.qtipCfg);
32625             }
32626             this.initEvents();
32627             if(!this.node.expanded){
32628                 this.updateExpandIcon();
32629             }
32630         }else{
32631             if(bulkRender === true) {
32632                 targetNode.appendChild(this.wrap);
32633             }
32634         }
32635     },
32636
32637     renderElements : function(n, a, targetNode, bulkRender){
32638         // add some indent caching, this helps performance when rendering a large tree
32639         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32640         var t = n.getOwnerTree();
32641         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32642         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32643         var cb = typeof a.checked == 'boolean';
32644         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32645         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32646             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32647             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32648             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32649             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32650             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32651              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32652                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32653             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32654             "</li>"];
32655
32656         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32657             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32658                                 n.nextSibling.ui.getEl(), buf.join(""));
32659         }else{
32660             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32661         }
32662
32663         this.elNode = this.wrap.childNodes[0];
32664         this.ctNode = this.wrap.childNodes[1];
32665         var cs = this.elNode.childNodes;
32666         this.indentNode = cs[0];
32667         this.ecNode = cs[1];
32668         this.iconNode = cs[2];
32669         var index = 3;
32670         if(cb){
32671             this.checkbox = cs[3];
32672             index++;
32673         }
32674         this.anchor = cs[index];
32675         this.textNode = cs[index].firstChild;
32676     },
32677
32678     getAnchor : function(){
32679         return this.anchor;
32680     },
32681
32682     getTextEl : function(){
32683         return this.textNode;
32684     },
32685
32686     getIconEl : function(){
32687         return this.iconNode;
32688     },
32689
32690     isChecked : function(){
32691         return this.checkbox ? this.checkbox.checked : false;
32692     },
32693
32694     updateExpandIcon : function(){
32695         if(this.rendered){
32696             var n = this.node, c1, c2;
32697             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32698             var hasChild = n.hasChildNodes();
32699             if(hasChild){
32700                 if(n.expanded){
32701                     cls += "-minus";
32702                     c1 = "x-tree-node-collapsed";
32703                     c2 = "x-tree-node-expanded";
32704                 }else{
32705                     cls += "-plus";
32706                     c1 = "x-tree-node-expanded";
32707                     c2 = "x-tree-node-collapsed";
32708                 }
32709                 if(this.wasLeaf){
32710                     this.removeClass("x-tree-node-leaf");
32711                     this.wasLeaf = false;
32712                 }
32713                 if(this.c1 != c1 || this.c2 != c2){
32714                     Roo.fly(this.elNode).replaceClass(c1, c2);
32715                     this.c1 = c1; this.c2 = c2;
32716                 }
32717             }else{
32718                 if(!this.wasLeaf){
32719                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32720                     delete this.c1;
32721                     delete this.c2;
32722                     this.wasLeaf = true;
32723                 }
32724             }
32725             var ecc = "x-tree-ec-icon "+cls;
32726             if(this.ecc != ecc){
32727                 this.ecNode.className = ecc;
32728                 this.ecc = ecc;
32729             }
32730         }
32731     },
32732
32733     getChildIndent : function(){
32734         if(!this.childIndent){
32735             var buf = [];
32736             var p = this.node;
32737             while(p){
32738                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32739                     if(!p.isLast()) {
32740                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32741                     } else {
32742                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32743                     }
32744                 }
32745                 p = p.parentNode;
32746             }
32747             this.childIndent = buf.join("");
32748         }
32749         return this.childIndent;
32750     },
32751
32752     renderIndent : function(){
32753         if(this.rendered){
32754             var indent = "";
32755             var p = this.node.parentNode;
32756             if(p){
32757                 indent = p.ui.getChildIndent();
32758             }
32759             if(this.indentMarkup != indent){ // don't rerender if not required
32760                 this.indentNode.innerHTML = indent;
32761                 this.indentMarkup = indent;
32762             }
32763             this.updateExpandIcon();
32764         }
32765     }
32766 };
32767
32768 Roo.tree.RootTreeNodeUI = function(){
32769     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32770 };
32771 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32772     render : function(){
32773         if(!this.rendered){
32774             var targetNode = this.node.ownerTree.innerCt.dom;
32775             this.node.expanded = true;
32776             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32777             this.wrap = this.ctNode = targetNode.firstChild;
32778         }
32779     },
32780     collapse : function(){
32781     },
32782     expand : function(){
32783     }
32784 });/*
32785  * Based on:
32786  * Ext JS Library 1.1.1
32787  * Copyright(c) 2006-2007, Ext JS, LLC.
32788  *
32789  * Originally Released Under LGPL - original licence link has changed is not relivant.
32790  *
32791  * Fork - LGPL
32792  * <script type="text/javascript">
32793  */
32794 /**
32795  * @class Roo.tree.TreeLoader
32796  * @extends Roo.util.Observable
32797  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32798  * nodes from a specified URL. The response must be a javascript Array definition
32799  * who's elements are node definition objects. eg:
32800  * <pre><code>
32801    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32802     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32803 </code></pre>
32804  * <br><br>
32805  * A server request is sent, and child nodes are loaded only when a node is expanded.
32806  * The loading node's id is passed to the server under the parameter name "node" to
32807  * enable the server to produce the correct child nodes.
32808  * <br><br>
32809  * To pass extra parameters, an event handler may be attached to the "beforeload"
32810  * event, and the parameters specified in the TreeLoader's baseParams property:
32811  * <pre><code>
32812     myTreeLoader.on("beforeload", function(treeLoader, node) {
32813         this.baseParams.category = node.attributes.category;
32814     }, this);
32815 </code></pre><
32816  * This would pass an HTTP parameter called "category" to the server containing
32817  * the value of the Node's "category" attribute.
32818  * @constructor
32819  * Creates a new Treeloader.
32820  * @param {Object} config A config object containing config properties.
32821  */
32822 Roo.tree.TreeLoader = function(config){
32823     this.baseParams = {};
32824     this.requestMethod = "POST";
32825     Roo.apply(this, config);
32826
32827     this.addEvents({
32828     
32829         /**
32830          * @event beforeload
32831          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32832          * @param {Object} This TreeLoader object.
32833          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32834          * @param {Object} callback The callback function specified in the {@link #load} call.
32835          */
32836         beforeload : true,
32837         /**
32838          * @event load
32839          * Fires when the node has been successfuly loaded.
32840          * @param {Object} This TreeLoader object.
32841          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32842          * @param {Object} response The response object containing the data from the server.
32843          */
32844         load : true,
32845         /**
32846          * @event loadexception
32847          * Fires if the network request failed.
32848          * @param {Object} This TreeLoader object.
32849          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32850          * @param {Object} response The response object containing the data from the server.
32851          */
32852         loadexception : true,
32853         /**
32854          * @event create
32855          * Fires before a node is created, enabling you to return custom Node types 
32856          * @param {Object} This TreeLoader object.
32857          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32858          */
32859         create : true
32860     });
32861
32862     Roo.tree.TreeLoader.superclass.constructor.call(this);
32863 };
32864
32865 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32866     /**
32867     * @cfg {String} dataUrl The URL from which to request a Json string which
32868     * specifies an array of node definition object representing the child nodes
32869     * to be loaded.
32870     */
32871     /**
32872     * @cfg {Object} baseParams (optional) An object containing properties which
32873     * specify HTTP parameters to be passed to each request for child nodes.
32874     */
32875     /**
32876     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32877     * created by this loader. If the attributes sent by the server have an attribute in this object,
32878     * they take priority.
32879     */
32880     /**
32881     * @cfg {Object} uiProviders (optional) An object containing properties which
32882     * 
32883     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32884     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32885     * <i>uiProvider</i> attribute of a returned child node is a string rather
32886     * than a reference to a TreeNodeUI implementation, this that string value
32887     * is used as a property name in the uiProviders object. You can define the provider named
32888     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32889     */
32890     uiProviders : {},
32891
32892     /**
32893     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32894     * child nodes before loading.
32895     */
32896     clearOnLoad : true,
32897
32898     /**
32899     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32900     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32901     * Grid query { data : [ .....] }
32902     */
32903     
32904     root : false,
32905      /**
32906     * @cfg {String} queryParam (optional) 
32907     * Name of the query as it will be passed on the querystring (defaults to 'node')
32908     * eg. the request will be ?node=[id]
32909     */
32910     
32911     
32912     queryParam: false,
32913     
32914     /**
32915      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32916      * This is called automatically when a node is expanded, but may be used to reload
32917      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32918      * @param {Roo.tree.TreeNode} node
32919      * @param {Function} callback
32920      */
32921     load : function(node, callback){
32922         if(this.clearOnLoad){
32923             while(node.firstChild){
32924                 node.removeChild(node.firstChild);
32925             }
32926         }
32927         if(node.attributes.children){ // preloaded json children
32928             var cs = node.attributes.children;
32929             for(var i = 0, len = cs.length; i < len; i++){
32930                 node.appendChild(this.createNode(cs[i]));
32931             }
32932             if(typeof callback == "function"){
32933                 callback();
32934             }
32935         }else if(this.dataUrl){
32936             this.requestData(node, callback);
32937         }
32938     },
32939
32940     getParams: function(node){
32941         var buf = [], bp = this.baseParams;
32942         for(var key in bp){
32943             if(typeof bp[key] != "function"){
32944                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32945             }
32946         }
32947         var n = this.queryParam === false ? 'node' : this.queryParam;
32948         buf.push(n + "=", encodeURIComponent(node.id));
32949         return buf.join("");
32950     },
32951
32952     requestData : function(node, callback){
32953         if(this.fireEvent("beforeload", this, node, callback) !== false){
32954             this.transId = Roo.Ajax.request({
32955                 method:this.requestMethod,
32956                 url: this.dataUrl||this.url,
32957                 success: this.handleResponse,
32958                 failure: this.handleFailure,
32959                 scope: this,
32960                 argument: {callback: callback, node: node},
32961                 params: this.getParams(node)
32962             });
32963         }else{
32964             // if the load is cancelled, make sure we notify
32965             // the node that we are done
32966             if(typeof callback == "function"){
32967                 callback();
32968             }
32969         }
32970     },
32971
32972     isLoading : function(){
32973         return this.transId ? true : false;
32974     },
32975
32976     abort : function(){
32977         if(this.isLoading()){
32978             Roo.Ajax.abort(this.transId);
32979         }
32980     },
32981
32982     // private
32983     createNode : function(attr)
32984     {
32985         // apply baseAttrs, nice idea Corey!
32986         if(this.baseAttrs){
32987             Roo.applyIf(attr, this.baseAttrs);
32988         }
32989         if(this.applyLoader !== false){
32990             attr.loader = this;
32991         }
32992         // uiProvider = depreciated..
32993         
32994         if(typeof(attr.uiProvider) == 'string'){
32995            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32996                 /**  eval:var:attr */ eval(attr.uiProvider);
32997         }
32998         if(typeof(this.uiProviders['default']) != 'undefined') {
32999             attr.uiProvider = this.uiProviders['default'];
33000         }
33001         
33002         this.fireEvent('create', this, attr);
33003         
33004         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33005         return(attr.leaf ?
33006                         new Roo.tree.TreeNode(attr) :
33007                         new Roo.tree.AsyncTreeNode(attr));
33008     },
33009
33010     processResponse : function(response, node, callback)
33011     {
33012         var json = response.responseText;
33013         try {
33014             
33015             var o = Roo.decode(json);
33016             
33017             if (!o.success) {
33018                 // it's a failure condition.
33019                 var a = response.argument;
33020                 this.fireEvent("loadexception", this, a.node, response);
33021                 Roo.log("Load failed - should have a handler really");
33022                 return;
33023             }
33024             
33025             if (this.root !== false) {
33026                 o = o[this.root];
33027             }
33028             
33029             for(var i = 0, len = o.length; i < len; i++){
33030                 var n = this.createNode(o[i]);
33031                 if(n){
33032                     node.appendChild(n);
33033                 }
33034             }
33035             if(typeof callback == "function"){
33036                 callback(this, node);
33037             }
33038         }catch(e){
33039             this.handleFailure(response);
33040         }
33041     },
33042
33043     handleResponse : function(response){
33044         this.transId = false;
33045         var a = response.argument;
33046         this.processResponse(response, a.node, a.callback);
33047         this.fireEvent("load", this, a.node, response);
33048     },
33049
33050     handleFailure : function(response)
33051     {
33052         // should handle failure better..
33053         this.transId = false;
33054         var a = response.argument;
33055         this.fireEvent("loadexception", this, a.node, response);
33056         if(typeof a.callback == "function"){
33057             a.callback(this, a.node);
33058         }
33059     }
33060 });/*
33061  * Based on:
33062  * Ext JS Library 1.1.1
33063  * Copyright(c) 2006-2007, Ext JS, LLC.
33064  *
33065  * Originally Released Under LGPL - original licence link has changed is not relivant.
33066  *
33067  * Fork - LGPL
33068  * <script type="text/javascript">
33069  */
33070
33071 /**
33072 * @class Roo.tree.TreeFilter
33073 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33074 * @param {TreePanel} tree
33075 * @param {Object} config (optional)
33076  */
33077 Roo.tree.TreeFilter = function(tree, config){
33078     this.tree = tree;
33079     this.filtered = {};
33080     Roo.apply(this, config);
33081 };
33082
33083 Roo.tree.TreeFilter.prototype = {
33084     clearBlank:false,
33085     reverse:false,
33086     autoClear:false,
33087     remove:false,
33088
33089      /**
33090      * Filter the data by a specific attribute.
33091      * @param {String/RegExp} value Either string that the attribute value
33092      * should start with or a RegExp to test against the attribute
33093      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33094      * @param {TreeNode} startNode (optional) The node to start the filter at.
33095      */
33096     filter : function(value, attr, startNode){
33097         attr = attr || "text";
33098         var f;
33099         if(typeof value == "string"){
33100             var vlen = value.length;
33101             // auto clear empty filter
33102             if(vlen == 0 && this.clearBlank){
33103                 this.clear();
33104                 return;
33105             }
33106             value = value.toLowerCase();
33107             f = function(n){
33108                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33109             };
33110         }else if(value.exec){ // regex?
33111             f = function(n){
33112                 return value.test(n.attributes[attr]);
33113             };
33114         }else{
33115             throw 'Illegal filter type, must be string or regex';
33116         }
33117         this.filterBy(f, null, startNode);
33118         },
33119
33120     /**
33121      * Filter by a function. The passed function will be called with each
33122      * node in the tree (or from the startNode). If the function returns true, the node is kept
33123      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33124      * @param {Function} fn The filter function
33125      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33126      */
33127     filterBy : function(fn, scope, startNode){
33128         startNode = startNode || this.tree.root;
33129         if(this.autoClear){
33130             this.clear();
33131         }
33132         var af = this.filtered, rv = this.reverse;
33133         var f = function(n){
33134             if(n == startNode){
33135                 return true;
33136             }
33137             if(af[n.id]){
33138                 return false;
33139             }
33140             var m = fn.call(scope || n, n);
33141             if(!m || rv){
33142                 af[n.id] = n;
33143                 n.ui.hide();
33144                 return false;
33145             }
33146             return true;
33147         };
33148         startNode.cascade(f);
33149         if(this.remove){
33150            for(var id in af){
33151                if(typeof id != "function"){
33152                    var n = af[id];
33153                    if(n && n.parentNode){
33154                        n.parentNode.removeChild(n);
33155                    }
33156                }
33157            }
33158         }
33159     },
33160
33161     /**
33162      * Clears the current filter. Note: with the "remove" option
33163      * set a filter cannot be cleared.
33164      */
33165     clear : function(){
33166         var t = this.tree;
33167         var af = this.filtered;
33168         for(var id in af){
33169             if(typeof id != "function"){
33170                 var n = af[id];
33171                 if(n){
33172                     n.ui.show();
33173                 }
33174             }
33175         }
33176         this.filtered = {};
33177     }
33178 };
33179 /*
33180  * Based on:
33181  * Ext JS Library 1.1.1
33182  * Copyright(c) 2006-2007, Ext JS, LLC.
33183  *
33184  * Originally Released Under LGPL - original licence link has changed is not relivant.
33185  *
33186  * Fork - LGPL
33187  * <script type="text/javascript">
33188  */
33189  
33190
33191 /**
33192  * @class Roo.tree.TreeSorter
33193  * Provides sorting of nodes in a TreePanel
33194  * 
33195  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33196  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33197  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33198  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33199  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33200  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33201  * @constructor
33202  * @param {TreePanel} tree
33203  * @param {Object} config
33204  */
33205 Roo.tree.TreeSorter = function(tree, config){
33206     Roo.apply(this, config);
33207     tree.on("beforechildrenrendered", this.doSort, this);
33208     tree.on("append", this.updateSort, this);
33209     tree.on("insert", this.updateSort, this);
33210     
33211     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33212     var p = this.property || "text";
33213     var sortType = this.sortType;
33214     var fs = this.folderSort;
33215     var cs = this.caseSensitive === true;
33216     var leafAttr = this.leafAttr || 'leaf';
33217
33218     this.sortFn = function(n1, n2){
33219         if(fs){
33220             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33221                 return 1;
33222             }
33223             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33224                 return -1;
33225             }
33226         }
33227         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33228         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33229         if(v1 < v2){
33230                         return dsc ? +1 : -1;
33231                 }else if(v1 > v2){
33232                         return dsc ? -1 : +1;
33233         }else{
33234                 return 0;
33235         }
33236     };
33237 };
33238
33239 Roo.tree.TreeSorter.prototype = {
33240     doSort : function(node){
33241         node.sort(this.sortFn);
33242     },
33243     
33244     compareNodes : function(n1, n2){
33245         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33246     },
33247     
33248     updateSort : function(tree, node){
33249         if(node.childrenRendered){
33250             this.doSort.defer(1, this, [node]);
33251         }
33252     }
33253 };/*
33254  * Based on:
33255  * Ext JS Library 1.1.1
33256  * Copyright(c) 2006-2007, Ext JS, LLC.
33257  *
33258  * Originally Released Under LGPL - original licence link has changed is not relivant.
33259  *
33260  * Fork - LGPL
33261  * <script type="text/javascript">
33262  */
33263
33264 if(Roo.dd.DropZone){
33265     
33266 Roo.tree.TreeDropZone = function(tree, config){
33267     this.allowParentInsert = false;
33268     this.allowContainerDrop = false;
33269     this.appendOnly = false;
33270     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33271     this.tree = tree;
33272     this.lastInsertClass = "x-tree-no-status";
33273     this.dragOverData = {};
33274 };
33275
33276 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33277     ddGroup : "TreeDD",
33278     
33279     expandDelay : 1000,
33280     
33281     expandNode : function(node){
33282         if(node.hasChildNodes() && !node.isExpanded()){
33283             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33284         }
33285     },
33286     
33287     queueExpand : function(node){
33288         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33289     },
33290     
33291     cancelExpand : function(){
33292         if(this.expandProcId){
33293             clearTimeout(this.expandProcId);
33294             this.expandProcId = false;
33295         }
33296     },
33297     
33298     isValidDropPoint : function(n, pt, dd, e, data){
33299         if(!n || !data){ return false; }
33300         var targetNode = n.node;
33301         var dropNode = data.node;
33302         // default drop rules
33303         if(!(targetNode && targetNode.isTarget && pt)){
33304             return false;
33305         }
33306         if(pt == "append" && targetNode.allowChildren === false){
33307             return false;
33308         }
33309         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33310             return false;
33311         }
33312         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33313             return false;
33314         }
33315         // reuse the object
33316         var overEvent = this.dragOverData;
33317         overEvent.tree = this.tree;
33318         overEvent.target = targetNode;
33319         overEvent.data = data;
33320         overEvent.point = pt;
33321         overEvent.source = dd;
33322         overEvent.rawEvent = e;
33323         overEvent.dropNode = dropNode;
33324         overEvent.cancel = false;  
33325         var result = this.tree.fireEvent("nodedragover", overEvent);
33326         return overEvent.cancel === false && result !== false;
33327     },
33328     
33329     getDropPoint : function(e, n, dd){
33330         var tn = n.node;
33331         if(tn.isRoot){
33332             return tn.allowChildren !== false ? "append" : false; // always append for root
33333         }
33334         var dragEl = n.ddel;
33335         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33336         var y = Roo.lib.Event.getPageY(e);
33337         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33338         
33339         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33340         var noAppend = tn.allowChildren === false;
33341         if(this.appendOnly || tn.parentNode.allowChildren === false){
33342             return noAppend ? false : "append";
33343         }
33344         var noBelow = false;
33345         if(!this.allowParentInsert){
33346             noBelow = tn.hasChildNodes() && tn.isExpanded();
33347         }
33348         var q = (b - t) / (noAppend ? 2 : 3);
33349         if(y >= t && y < (t + q)){
33350             return "above";
33351         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33352             return "below";
33353         }else{
33354             return "append";
33355         }
33356     },
33357     
33358     onNodeEnter : function(n, dd, e, data){
33359         this.cancelExpand();
33360     },
33361     
33362     onNodeOver : function(n, dd, e, data){
33363         var pt = this.getDropPoint(e, n, dd);
33364         var node = n.node;
33365         
33366         // auto node expand check
33367         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33368             this.queueExpand(node);
33369         }else if(pt != "append"){
33370             this.cancelExpand();
33371         }
33372         
33373         // set the insert point style on the target node
33374         var returnCls = this.dropNotAllowed;
33375         if(this.isValidDropPoint(n, pt, dd, e, data)){
33376            if(pt){
33377                var el = n.ddel;
33378                var cls;
33379                if(pt == "above"){
33380                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33381                    cls = "x-tree-drag-insert-above";
33382                }else if(pt == "below"){
33383                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33384                    cls = "x-tree-drag-insert-below";
33385                }else{
33386                    returnCls = "x-tree-drop-ok-append";
33387                    cls = "x-tree-drag-append";
33388                }
33389                if(this.lastInsertClass != cls){
33390                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33391                    this.lastInsertClass = cls;
33392                }
33393            }
33394        }
33395        return returnCls;
33396     },
33397     
33398     onNodeOut : function(n, dd, e, data){
33399         this.cancelExpand();
33400         this.removeDropIndicators(n);
33401     },
33402     
33403     onNodeDrop : function(n, dd, e, data){
33404         var point = this.getDropPoint(e, n, dd);
33405         var targetNode = n.node;
33406         targetNode.ui.startDrop();
33407         if(!this.isValidDropPoint(n, point, dd, e, data)){
33408             targetNode.ui.endDrop();
33409             return false;
33410         }
33411         // first try to find the drop node
33412         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33413         var dropEvent = {
33414             tree : this.tree,
33415             target: targetNode,
33416             data: data,
33417             point: point,
33418             source: dd,
33419             rawEvent: e,
33420             dropNode: dropNode,
33421             cancel: !dropNode   
33422         };
33423         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33424         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33425             targetNode.ui.endDrop();
33426             return false;
33427         }
33428         // allow target changing
33429         targetNode = dropEvent.target;
33430         if(point == "append" && !targetNode.isExpanded()){
33431             targetNode.expand(false, null, function(){
33432                 this.completeDrop(dropEvent);
33433             }.createDelegate(this));
33434         }else{
33435             this.completeDrop(dropEvent);
33436         }
33437         return true;
33438     },
33439     
33440     completeDrop : function(de){
33441         var ns = de.dropNode, p = de.point, t = de.target;
33442         if(!(ns instanceof Array)){
33443             ns = [ns];
33444         }
33445         var n;
33446         for(var i = 0, len = ns.length; i < len; i++){
33447             n = ns[i];
33448             if(p == "above"){
33449                 t.parentNode.insertBefore(n, t);
33450             }else if(p == "below"){
33451                 t.parentNode.insertBefore(n, t.nextSibling);
33452             }else{
33453                 t.appendChild(n);
33454             }
33455         }
33456         n.ui.focus();
33457         if(this.tree.hlDrop){
33458             n.ui.highlight();
33459         }
33460         t.ui.endDrop();
33461         this.tree.fireEvent("nodedrop", de);
33462     },
33463     
33464     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33465         if(this.tree.hlDrop){
33466             dropNode.ui.focus();
33467             dropNode.ui.highlight();
33468         }
33469         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33470     },
33471     
33472     getTree : function(){
33473         return this.tree;
33474     },
33475     
33476     removeDropIndicators : function(n){
33477         if(n && n.ddel){
33478             var el = n.ddel;
33479             Roo.fly(el).removeClass([
33480                     "x-tree-drag-insert-above",
33481                     "x-tree-drag-insert-below",
33482                     "x-tree-drag-append"]);
33483             this.lastInsertClass = "_noclass";
33484         }
33485     },
33486     
33487     beforeDragDrop : function(target, e, id){
33488         this.cancelExpand();
33489         return true;
33490     },
33491     
33492     afterRepair : function(data){
33493         if(data && Roo.enableFx){
33494             data.node.ui.highlight();
33495         }
33496         this.hideProxy();
33497     }    
33498 });
33499
33500 }
33501 /*
33502  * Based on:
33503  * Ext JS Library 1.1.1
33504  * Copyright(c) 2006-2007, Ext JS, LLC.
33505  *
33506  * Originally Released Under LGPL - original licence link has changed is not relivant.
33507  *
33508  * Fork - LGPL
33509  * <script type="text/javascript">
33510  */
33511  
33512
33513 if(Roo.dd.DragZone){
33514 Roo.tree.TreeDragZone = function(tree, config){
33515     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33516     this.tree = tree;
33517 };
33518
33519 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33520     ddGroup : "TreeDD",
33521     
33522     onBeforeDrag : function(data, e){
33523         var n = data.node;
33524         return n && n.draggable && !n.disabled;
33525     },
33526     
33527     onInitDrag : function(e){
33528         var data = this.dragData;
33529         this.tree.getSelectionModel().select(data.node);
33530         this.proxy.update("");
33531         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33532         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33533     },
33534     
33535     getRepairXY : function(e, data){
33536         return data.node.ui.getDDRepairXY();
33537     },
33538     
33539     onEndDrag : function(data, e){
33540         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33541     },
33542     
33543     onValidDrop : function(dd, e, id){
33544         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33545         this.hideProxy();
33546     },
33547     
33548     beforeInvalidDrop : function(e, id){
33549         // this scrolls the original position back into view
33550         var sm = this.tree.getSelectionModel();
33551         sm.clearSelections();
33552         sm.select(this.dragData.node);
33553     }
33554 });
33555 }/*
33556  * Based on:
33557  * Ext JS Library 1.1.1
33558  * Copyright(c) 2006-2007, Ext JS, LLC.
33559  *
33560  * Originally Released Under LGPL - original licence link has changed is not relivant.
33561  *
33562  * Fork - LGPL
33563  * <script type="text/javascript">
33564  */
33565 /**
33566  * @class Roo.tree.TreeEditor
33567  * @extends Roo.Editor
33568  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33569  * as the editor field.
33570  * @constructor
33571  * @param {TreePanel} tree
33572  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33573  */
33574 Roo.tree.TreeEditor = function(tree, config){
33575     config = config || {};
33576     var field = config.events ? config : new Roo.form.TextField(config);
33577     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33578
33579     this.tree = tree;
33580
33581     tree.on('beforeclick', this.beforeNodeClick, this);
33582     tree.getTreeEl().on('mousedown', this.hide, this);
33583     this.on('complete', this.updateNode, this);
33584     this.on('beforestartedit', this.fitToTree, this);
33585     this.on('startedit', this.bindScroll, this, {delay:10});
33586     this.on('specialkey', this.onSpecialKey, this);
33587 };
33588
33589 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33590     /**
33591      * @cfg {String} alignment
33592      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33593      */
33594     alignment: "l-l",
33595     // inherit
33596     autoSize: false,
33597     /**
33598      * @cfg {Boolean} hideEl
33599      * True to hide the bound element while the editor is displayed (defaults to false)
33600      */
33601     hideEl : false,
33602     /**
33603      * @cfg {String} cls
33604      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33605      */
33606     cls: "x-small-editor x-tree-editor",
33607     /**
33608      * @cfg {Boolean} shim
33609      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33610      */
33611     shim:false,
33612     // inherit
33613     shadow:"frame",
33614     /**
33615      * @cfg {Number} maxWidth
33616      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33617      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33618      * scroll and client offsets into account prior to each edit.
33619      */
33620     maxWidth: 250,
33621
33622     editDelay : 350,
33623
33624     // private
33625     fitToTree : function(ed, el){
33626         var td = this.tree.getTreeEl().dom, nd = el.dom;
33627         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33628             td.scrollLeft = nd.offsetLeft;
33629         }
33630         var w = Math.min(
33631                 this.maxWidth,
33632                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33633         this.setSize(w, '');
33634     },
33635
33636     // private
33637     triggerEdit : function(node){
33638         this.completeEdit();
33639         this.editNode = node;
33640         this.startEdit(node.ui.textNode, node.text);
33641     },
33642
33643     // private
33644     bindScroll : function(){
33645         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33646     },
33647
33648     // private
33649     beforeNodeClick : function(node, e){
33650         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33651         this.lastClick = new Date();
33652         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33653             e.stopEvent();
33654             this.triggerEdit(node);
33655             return false;
33656         }
33657     },
33658
33659     // private
33660     updateNode : function(ed, value){
33661         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33662         this.editNode.setText(value);
33663     },
33664
33665     // private
33666     onHide : function(){
33667         Roo.tree.TreeEditor.superclass.onHide.call(this);
33668         if(this.editNode){
33669             this.editNode.ui.focus();
33670         }
33671     },
33672
33673     // private
33674     onSpecialKey : function(field, e){
33675         var k = e.getKey();
33676         if(k == e.ESC){
33677             e.stopEvent();
33678             this.cancelEdit();
33679         }else if(k == e.ENTER && !e.hasModifier()){
33680             e.stopEvent();
33681             this.completeEdit();
33682         }
33683     }
33684 });//<Script type="text/javascript">
33685 /*
33686  * Based on:
33687  * Ext JS Library 1.1.1
33688  * Copyright(c) 2006-2007, Ext JS, LLC.
33689  *
33690  * Originally Released Under LGPL - original licence link has changed is not relivant.
33691  *
33692  * Fork - LGPL
33693  * <script type="text/javascript">
33694  */
33695  
33696 /**
33697  * Not documented??? - probably should be...
33698  */
33699
33700 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33701     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33702     
33703     renderElements : function(n, a, targetNode, bulkRender){
33704         //consel.log("renderElements?");
33705         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33706
33707         var t = n.getOwnerTree();
33708         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33709         
33710         var cols = t.columns;
33711         var bw = t.borderWidth;
33712         var c = cols[0];
33713         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33714          var cb = typeof a.checked == "boolean";
33715         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33716         var colcls = 'x-t-' + tid + '-c0';
33717         var buf = [
33718             '<li class="x-tree-node">',
33719             
33720                 
33721                 '<div class="x-tree-node-el ', a.cls,'">',
33722                     // extran...
33723                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33724                 
33725                 
33726                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33727                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33728                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33729                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33730                            (a.iconCls ? ' '+a.iconCls : ''),
33731                            '" unselectable="on" />',
33732                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33733                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33734                              
33735                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33736                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33737                             '<span unselectable="on" qtip="' + tx + '">',
33738                              tx,
33739                              '</span></a>' ,
33740                     '</div>',
33741                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33742                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33743                  ];
33744         for(var i = 1, len = cols.length; i < len; i++){
33745             c = cols[i];
33746             colcls = 'x-t-' + tid + '-c' +i;
33747             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33748             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33749                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33750                       "</div>");
33751          }
33752          
33753          buf.push(
33754             '</a>',
33755             '<div class="x-clear"></div></div>',
33756             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33757             "</li>");
33758         
33759         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33760             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33761                                 n.nextSibling.ui.getEl(), buf.join(""));
33762         }else{
33763             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33764         }
33765         var el = this.wrap.firstChild;
33766         this.elRow = el;
33767         this.elNode = el.firstChild;
33768         this.ranchor = el.childNodes[1];
33769         this.ctNode = this.wrap.childNodes[1];
33770         var cs = el.firstChild.childNodes;
33771         this.indentNode = cs[0];
33772         this.ecNode = cs[1];
33773         this.iconNode = cs[2];
33774         var index = 3;
33775         if(cb){
33776             this.checkbox = cs[3];
33777             index++;
33778         }
33779         this.anchor = cs[index];
33780         
33781         this.textNode = cs[index].firstChild;
33782         
33783         //el.on("click", this.onClick, this);
33784         //el.on("dblclick", this.onDblClick, this);
33785         
33786         
33787        // console.log(this);
33788     },
33789     initEvents : function(){
33790         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33791         
33792             
33793         var a = this.ranchor;
33794
33795         var el = Roo.get(a);
33796
33797         if(Roo.isOpera){ // opera render bug ignores the CSS
33798             el.setStyle("text-decoration", "none");
33799         }
33800
33801         el.on("click", this.onClick, this);
33802         el.on("dblclick", this.onDblClick, this);
33803         el.on("contextmenu", this.onContextMenu, this);
33804         
33805     },
33806     
33807     /*onSelectedChange : function(state){
33808         if(state){
33809             this.focus();
33810             this.addClass("x-tree-selected");
33811         }else{
33812             //this.blur();
33813             this.removeClass("x-tree-selected");
33814         }
33815     },*/
33816     addClass : function(cls){
33817         if(this.elRow){
33818             Roo.fly(this.elRow).addClass(cls);
33819         }
33820         
33821     },
33822     
33823     
33824     removeClass : function(cls){
33825         if(this.elRow){
33826             Roo.fly(this.elRow).removeClass(cls);
33827         }
33828     }
33829
33830     
33831     
33832 });//<Script type="text/javascript">
33833
33834 /*
33835  * Based on:
33836  * Ext JS Library 1.1.1
33837  * Copyright(c) 2006-2007, Ext JS, LLC.
33838  *
33839  * Originally Released Under LGPL - original licence link has changed is not relivant.
33840  *
33841  * Fork - LGPL
33842  * <script type="text/javascript">
33843  */
33844  
33845
33846 /**
33847  * @class Roo.tree.ColumnTree
33848  * @extends Roo.data.TreePanel
33849  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33850  * @cfg {int} borderWidth  compined right/left border allowance
33851  * @constructor
33852  * @param {String/HTMLElement/Element} el The container element
33853  * @param {Object} config
33854  */
33855 Roo.tree.ColumnTree =  function(el, config)
33856 {
33857    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33858    this.addEvents({
33859         /**
33860         * @event resize
33861         * Fire this event on a container when it resizes
33862         * @param {int} w Width
33863         * @param {int} h Height
33864         */
33865        "resize" : true
33866     });
33867     this.on('resize', this.onResize, this);
33868 };
33869
33870 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33871     //lines:false,
33872     
33873     
33874     borderWidth: Roo.isBorderBox ? 0 : 2, 
33875     headEls : false,
33876     
33877     render : function(){
33878         // add the header.....
33879        
33880         Roo.tree.ColumnTree.superclass.render.apply(this);
33881         
33882         this.el.addClass('x-column-tree');
33883         
33884         this.headers = this.el.createChild(
33885             {cls:'x-tree-headers'},this.innerCt.dom);
33886    
33887         var cols = this.columns, c;
33888         var totalWidth = 0;
33889         this.headEls = [];
33890         var  len = cols.length;
33891         for(var i = 0; i < len; i++){
33892              c = cols[i];
33893              totalWidth += c.width;
33894             this.headEls.push(this.headers.createChild({
33895                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33896                  cn: {
33897                      cls:'x-tree-hd-text',
33898                      html: c.header
33899                  },
33900                  style:'width:'+(c.width-this.borderWidth)+'px;'
33901              }));
33902         }
33903         this.headers.createChild({cls:'x-clear'});
33904         // prevent floats from wrapping when clipped
33905         this.headers.setWidth(totalWidth);
33906         //this.innerCt.setWidth(totalWidth);
33907         this.innerCt.setStyle({ overflow: 'auto' });
33908         this.onResize(this.width, this.height);
33909              
33910         
33911     },
33912     onResize : function(w,h)
33913     {
33914         this.height = h;
33915         this.width = w;
33916         // resize cols..
33917         this.innerCt.setWidth(this.width);
33918         this.innerCt.setHeight(this.height-20);
33919         
33920         // headers...
33921         var cols = this.columns, c;
33922         var totalWidth = 0;
33923         var expEl = false;
33924         var len = cols.length;
33925         for(var i = 0; i < len; i++){
33926             c = cols[i];
33927             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33928                 // it's the expander..
33929                 expEl  = this.headEls[i];
33930                 continue;
33931             }
33932             totalWidth += c.width;
33933             
33934         }
33935         if (expEl) {
33936             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33937         }
33938         this.headers.setWidth(w-20);
33939
33940         
33941         
33942         
33943     }
33944 });
33945 /*
33946  * Based on:
33947  * Ext JS Library 1.1.1
33948  * Copyright(c) 2006-2007, Ext JS, LLC.
33949  *
33950  * Originally Released Under LGPL - original licence link has changed is not relivant.
33951  *
33952  * Fork - LGPL
33953  * <script type="text/javascript">
33954  */
33955  
33956 /**
33957  * @class Roo.menu.Menu
33958  * @extends Roo.util.Observable
33959  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33960  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33961  * @constructor
33962  * Creates a new Menu
33963  * @param {Object} config Configuration options
33964  */
33965 Roo.menu.Menu = function(config){
33966     Roo.apply(this, config);
33967     this.id = this.id || Roo.id();
33968     this.addEvents({
33969         /**
33970          * @event beforeshow
33971          * Fires before this menu is displayed
33972          * @param {Roo.menu.Menu} this
33973          */
33974         beforeshow : true,
33975         /**
33976          * @event beforehide
33977          * Fires before this menu is hidden
33978          * @param {Roo.menu.Menu} this
33979          */
33980         beforehide : true,
33981         /**
33982          * @event show
33983          * Fires after this menu is displayed
33984          * @param {Roo.menu.Menu} this
33985          */
33986         show : true,
33987         /**
33988          * @event hide
33989          * Fires after this menu is hidden
33990          * @param {Roo.menu.Menu} this
33991          */
33992         hide : true,
33993         /**
33994          * @event click
33995          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33996          * @param {Roo.menu.Menu} this
33997          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33998          * @param {Roo.EventObject} e
33999          */
34000         click : true,
34001         /**
34002          * @event mouseover
34003          * Fires when the mouse is hovering over this menu
34004          * @param {Roo.menu.Menu} this
34005          * @param {Roo.EventObject} e
34006          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34007          */
34008         mouseover : true,
34009         /**
34010          * @event mouseout
34011          * Fires when the mouse exits this menu
34012          * @param {Roo.menu.Menu} this
34013          * @param {Roo.EventObject} e
34014          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34015          */
34016         mouseout : true,
34017         /**
34018          * @event itemclick
34019          * Fires when a menu item contained in this menu is clicked
34020          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34021          * @param {Roo.EventObject} e
34022          */
34023         itemclick: true
34024     });
34025     if (this.registerMenu) {
34026         Roo.menu.MenuMgr.register(this);
34027     }
34028     
34029     var mis = this.items;
34030     this.items = new Roo.util.MixedCollection();
34031     if(mis){
34032         this.add.apply(this, mis);
34033     }
34034 };
34035
34036 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34037     /**
34038      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34039      */
34040     minWidth : 120,
34041     /**
34042      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34043      * for bottom-right shadow (defaults to "sides")
34044      */
34045     shadow : "sides",
34046     /**
34047      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34048      * this menu (defaults to "tl-tr?")
34049      */
34050     subMenuAlign : "tl-tr?",
34051     /**
34052      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34053      * relative to its element of origin (defaults to "tl-bl?")
34054      */
34055     defaultAlign : "tl-bl?",
34056     /**
34057      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34058      */
34059     allowOtherMenus : false,
34060     /**
34061      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34062      */
34063     registerMenu : true,
34064
34065     hidden:true,
34066
34067     // private
34068     render : function(){
34069         if(this.el){
34070             return;
34071         }
34072         var el = this.el = new Roo.Layer({
34073             cls: "x-menu",
34074             shadow:this.shadow,
34075             constrain: false,
34076             parentEl: this.parentEl || document.body,
34077             zindex:15000
34078         });
34079
34080         this.keyNav = new Roo.menu.MenuNav(this);
34081
34082         if(this.plain){
34083             el.addClass("x-menu-plain");
34084         }
34085         if(this.cls){
34086             el.addClass(this.cls);
34087         }
34088         // generic focus element
34089         this.focusEl = el.createChild({
34090             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34091         });
34092         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34093         ul.on("click", this.onClick, this);
34094         ul.on("mouseover", this.onMouseOver, this);
34095         ul.on("mouseout", this.onMouseOut, this);
34096         this.items.each(function(item){
34097             var li = document.createElement("li");
34098             li.className = "x-menu-list-item";
34099             ul.dom.appendChild(li);
34100             item.render(li, this);
34101         }, this);
34102         this.ul = ul;
34103         this.autoWidth();
34104     },
34105
34106     // private
34107     autoWidth : function(){
34108         var el = this.el, ul = this.ul;
34109         if(!el){
34110             return;
34111         }
34112         var w = this.width;
34113         if(w){
34114             el.setWidth(w);
34115         }else if(Roo.isIE){
34116             el.setWidth(this.minWidth);
34117             var t = el.dom.offsetWidth; // force recalc
34118             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34119         }
34120     },
34121
34122     // private
34123     delayAutoWidth : function(){
34124         if(this.rendered){
34125             if(!this.awTask){
34126                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34127             }
34128             this.awTask.delay(20);
34129         }
34130     },
34131
34132     // private
34133     findTargetItem : function(e){
34134         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34135         if(t && t.menuItemId){
34136             return this.items.get(t.menuItemId);
34137         }
34138     },
34139
34140     // private
34141     onClick : function(e){
34142         var t;
34143         if(t = this.findTargetItem(e)){
34144             t.onClick(e);
34145             this.fireEvent("click", this, t, e);
34146         }
34147     },
34148
34149     // private
34150     setActiveItem : function(item, autoExpand){
34151         if(item != this.activeItem){
34152             if(this.activeItem){
34153                 this.activeItem.deactivate();
34154             }
34155             this.activeItem = item;
34156             item.activate(autoExpand);
34157         }else if(autoExpand){
34158             item.expandMenu();
34159         }
34160     },
34161
34162     // private
34163     tryActivate : function(start, step){
34164         var items = this.items;
34165         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34166             var item = items.get(i);
34167             if(!item.disabled && item.canActivate){
34168                 this.setActiveItem(item, false);
34169                 return item;
34170             }
34171         }
34172         return false;
34173     },
34174
34175     // private
34176     onMouseOver : function(e){
34177         var t;
34178         if(t = this.findTargetItem(e)){
34179             if(t.canActivate && !t.disabled){
34180                 this.setActiveItem(t, true);
34181             }
34182         }
34183         this.fireEvent("mouseover", this, e, t);
34184     },
34185
34186     // private
34187     onMouseOut : function(e){
34188         var t;
34189         if(t = this.findTargetItem(e)){
34190             if(t == this.activeItem && t.shouldDeactivate(e)){
34191                 this.activeItem.deactivate();
34192                 delete this.activeItem;
34193             }
34194         }
34195         this.fireEvent("mouseout", this, e, t);
34196     },
34197
34198     /**
34199      * Read-only.  Returns true if the menu is currently displayed, else false.
34200      * @type Boolean
34201      */
34202     isVisible : function(){
34203         return this.el && !this.hidden;
34204     },
34205
34206     /**
34207      * Displays this menu relative to another element
34208      * @param {String/HTMLElement/Roo.Element} element The element to align to
34209      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34210      * the element (defaults to this.defaultAlign)
34211      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34212      */
34213     show : function(el, pos, parentMenu){
34214         this.parentMenu = parentMenu;
34215         if(!this.el){
34216             this.render();
34217         }
34218         this.fireEvent("beforeshow", this);
34219         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34220     },
34221
34222     /**
34223      * Displays this menu at a specific xy position
34224      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34225      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34226      */
34227     showAt : function(xy, parentMenu, /* private: */_e){
34228         this.parentMenu = parentMenu;
34229         if(!this.el){
34230             this.render();
34231         }
34232         if(_e !== false){
34233             this.fireEvent("beforeshow", this);
34234             xy = this.el.adjustForConstraints(xy);
34235         }
34236         this.el.setXY(xy);
34237         this.el.show();
34238         this.hidden = false;
34239         this.focus();
34240         this.fireEvent("show", this);
34241     },
34242
34243     focus : function(){
34244         if(!this.hidden){
34245             this.doFocus.defer(50, this);
34246         }
34247     },
34248
34249     doFocus : function(){
34250         if(!this.hidden){
34251             this.focusEl.focus();
34252         }
34253     },
34254
34255     /**
34256      * Hides this menu and optionally all parent menus
34257      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34258      */
34259     hide : function(deep){
34260         if(this.el && this.isVisible()){
34261             this.fireEvent("beforehide", this);
34262             if(this.activeItem){
34263                 this.activeItem.deactivate();
34264                 this.activeItem = null;
34265             }
34266             this.el.hide();
34267             this.hidden = true;
34268             this.fireEvent("hide", this);
34269         }
34270         if(deep === true && this.parentMenu){
34271             this.parentMenu.hide(true);
34272         }
34273     },
34274
34275     /**
34276      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34277      * Any of the following are valid:
34278      * <ul>
34279      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34280      * <li>An HTMLElement object which will be converted to a menu item</li>
34281      * <li>A menu item config object that will be created as a new menu item</li>
34282      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34283      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34284      * </ul>
34285      * Usage:
34286      * <pre><code>
34287 // Create the menu
34288 var menu = new Roo.menu.Menu();
34289
34290 // Create a menu item to add by reference
34291 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34292
34293 // Add a bunch of items at once using different methods.
34294 // Only the last item added will be returned.
34295 var item = menu.add(
34296     menuItem,                // add existing item by ref
34297     'Dynamic Item',          // new TextItem
34298     '-',                     // new separator
34299     { text: 'Config Item' }  // new item by config
34300 );
34301 </code></pre>
34302      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34303      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34304      */
34305     add : function(){
34306         var a = arguments, l = a.length, item;
34307         for(var i = 0; i < l; i++){
34308             var el = a[i];
34309             if ((typeof(el) == "object") && el.xtype && el.xns) {
34310                 el = Roo.factory(el, Roo.menu);
34311             }
34312             
34313             if(el.render){ // some kind of Item
34314                 item = this.addItem(el);
34315             }else if(typeof el == "string"){ // string
34316                 if(el == "separator" || el == "-"){
34317                     item = this.addSeparator();
34318                 }else{
34319                     item = this.addText(el);
34320                 }
34321             }else if(el.tagName || el.el){ // element
34322                 item = this.addElement(el);
34323             }else if(typeof el == "object"){ // must be menu item config?
34324                 item = this.addMenuItem(el);
34325             }
34326         }
34327         return item;
34328     },
34329
34330     /**
34331      * Returns this menu's underlying {@link Roo.Element} object
34332      * @return {Roo.Element} The element
34333      */
34334     getEl : function(){
34335         if(!this.el){
34336             this.render();
34337         }
34338         return this.el;
34339     },
34340
34341     /**
34342      * Adds a separator bar to the menu
34343      * @return {Roo.menu.Item} The menu item that was added
34344      */
34345     addSeparator : function(){
34346         return this.addItem(new Roo.menu.Separator());
34347     },
34348
34349     /**
34350      * Adds an {@link Roo.Element} object to the menu
34351      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34352      * @return {Roo.menu.Item} The menu item that was added
34353      */
34354     addElement : function(el){
34355         return this.addItem(new Roo.menu.BaseItem(el));
34356     },
34357
34358     /**
34359      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34360      * @param {Roo.menu.Item} item The menu item to add
34361      * @return {Roo.menu.Item} The menu item that was added
34362      */
34363     addItem : function(item){
34364         this.items.add(item);
34365         if(this.ul){
34366             var li = document.createElement("li");
34367             li.className = "x-menu-list-item";
34368             this.ul.dom.appendChild(li);
34369             item.render(li, this);
34370             this.delayAutoWidth();
34371         }
34372         return item;
34373     },
34374
34375     /**
34376      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34377      * @param {Object} config A MenuItem config object
34378      * @return {Roo.menu.Item} The menu item that was added
34379      */
34380     addMenuItem : function(config){
34381         if(!(config instanceof Roo.menu.Item)){
34382             if(typeof config.checked == "boolean"){ // must be check menu item config?
34383                 config = new Roo.menu.CheckItem(config);
34384             }else{
34385                 config = new Roo.menu.Item(config);
34386             }
34387         }
34388         return this.addItem(config);
34389     },
34390
34391     /**
34392      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34393      * @param {String} text The text to display in the menu item
34394      * @return {Roo.menu.Item} The menu item that was added
34395      */
34396     addText : function(text){
34397         return this.addItem(new Roo.menu.TextItem({ text : text }));
34398     },
34399
34400     /**
34401      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34402      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34403      * @param {Roo.menu.Item} item The menu item to add
34404      * @return {Roo.menu.Item} The menu item that was added
34405      */
34406     insert : function(index, item){
34407         this.items.insert(index, item);
34408         if(this.ul){
34409             var li = document.createElement("li");
34410             li.className = "x-menu-list-item";
34411             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34412             item.render(li, this);
34413             this.delayAutoWidth();
34414         }
34415         return item;
34416     },
34417
34418     /**
34419      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34420      * @param {Roo.menu.Item} item The menu item to remove
34421      */
34422     remove : function(item){
34423         this.items.removeKey(item.id);
34424         item.destroy();
34425     },
34426
34427     /**
34428      * Removes and destroys all items in the menu
34429      */
34430     removeAll : function(){
34431         var f;
34432         while(f = this.items.first()){
34433             this.remove(f);
34434         }
34435     }
34436 });
34437
34438 // MenuNav is a private utility class used internally by the Menu
34439 Roo.menu.MenuNav = function(menu){
34440     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34441     this.scope = this.menu = menu;
34442 };
34443
34444 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34445     doRelay : function(e, h){
34446         var k = e.getKey();
34447         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34448             this.menu.tryActivate(0, 1);
34449             return false;
34450         }
34451         return h.call(this.scope || this, e, this.menu);
34452     },
34453
34454     up : function(e, m){
34455         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34456             m.tryActivate(m.items.length-1, -1);
34457         }
34458     },
34459
34460     down : function(e, m){
34461         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34462             m.tryActivate(0, 1);
34463         }
34464     },
34465
34466     right : function(e, m){
34467         if(m.activeItem){
34468             m.activeItem.expandMenu(true);
34469         }
34470     },
34471
34472     left : function(e, m){
34473         m.hide();
34474         if(m.parentMenu && m.parentMenu.activeItem){
34475             m.parentMenu.activeItem.activate();
34476         }
34477     },
34478
34479     enter : function(e, m){
34480         if(m.activeItem){
34481             e.stopPropagation();
34482             m.activeItem.onClick(e);
34483             m.fireEvent("click", this, m.activeItem);
34484             return true;
34485         }
34486     }
34487 });/*
34488  * Based on:
34489  * Ext JS Library 1.1.1
34490  * Copyright(c) 2006-2007, Ext JS, LLC.
34491  *
34492  * Originally Released Under LGPL - original licence link has changed is not relivant.
34493  *
34494  * Fork - LGPL
34495  * <script type="text/javascript">
34496  */
34497  
34498 /**
34499  * @class Roo.menu.MenuMgr
34500  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34501  * @singleton
34502  */
34503 Roo.menu.MenuMgr = function(){
34504    var menus, active, groups = {}, attached = false, lastShow = new Date();
34505
34506    // private - called when first menu is created
34507    function init(){
34508        menus = {};
34509        active = new Roo.util.MixedCollection();
34510        Roo.get(document).addKeyListener(27, function(){
34511            if(active.length > 0){
34512                hideAll();
34513            }
34514        });
34515    }
34516
34517    // private
34518    function hideAll(){
34519        if(active && active.length > 0){
34520            var c = active.clone();
34521            c.each(function(m){
34522                m.hide();
34523            });
34524        }
34525    }
34526
34527    // private
34528    function onHide(m){
34529        active.remove(m);
34530        if(active.length < 1){
34531            Roo.get(document).un("mousedown", onMouseDown);
34532            attached = false;
34533        }
34534    }
34535
34536    // private
34537    function onShow(m){
34538        var last = active.last();
34539        lastShow = new Date();
34540        active.add(m);
34541        if(!attached){
34542            Roo.get(document).on("mousedown", onMouseDown);
34543            attached = true;
34544        }
34545        if(m.parentMenu){
34546           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34547           m.parentMenu.activeChild = m;
34548        }else if(last && last.isVisible()){
34549           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34550        }
34551    }
34552
34553    // private
34554    function onBeforeHide(m){
34555        if(m.activeChild){
34556            m.activeChild.hide();
34557        }
34558        if(m.autoHideTimer){
34559            clearTimeout(m.autoHideTimer);
34560            delete m.autoHideTimer;
34561        }
34562    }
34563
34564    // private
34565    function onBeforeShow(m){
34566        var pm = m.parentMenu;
34567        if(!pm && !m.allowOtherMenus){
34568            hideAll();
34569        }else if(pm && pm.activeChild && active != m){
34570            pm.activeChild.hide();
34571        }
34572    }
34573
34574    // private
34575    function onMouseDown(e){
34576        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34577            hideAll();
34578        }
34579    }
34580
34581    // private
34582    function onBeforeCheck(mi, state){
34583        if(state){
34584            var g = groups[mi.group];
34585            for(var i = 0, l = g.length; i < l; i++){
34586                if(g[i] != mi){
34587                    g[i].setChecked(false);
34588                }
34589            }
34590        }
34591    }
34592
34593    return {
34594
34595        /**
34596         * Hides all menus that are currently visible
34597         */
34598        hideAll : function(){
34599             hideAll();  
34600        },
34601
34602        // private
34603        register : function(menu){
34604            if(!menus){
34605                init();
34606            }
34607            menus[menu.id] = menu;
34608            menu.on("beforehide", onBeforeHide);
34609            menu.on("hide", onHide);
34610            menu.on("beforeshow", onBeforeShow);
34611            menu.on("show", onShow);
34612            var g = menu.group;
34613            if(g && menu.events["checkchange"]){
34614                if(!groups[g]){
34615                    groups[g] = [];
34616                }
34617                groups[g].push(menu);
34618                menu.on("checkchange", onCheck);
34619            }
34620        },
34621
34622         /**
34623          * Returns a {@link Roo.menu.Menu} object
34624          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34625          * be used to generate and return a new Menu instance.
34626          */
34627        get : function(menu){
34628            if(typeof menu == "string"){ // menu id
34629                return menus[menu];
34630            }else if(menu.events){  // menu instance
34631                return menu;
34632            }else if(typeof menu.length == 'number'){ // array of menu items?
34633                return new Roo.menu.Menu({items:menu});
34634            }else{ // otherwise, must be a config
34635                return new Roo.menu.Menu(menu);
34636            }
34637        },
34638
34639        // private
34640        unregister : function(menu){
34641            delete menus[menu.id];
34642            menu.un("beforehide", onBeforeHide);
34643            menu.un("hide", onHide);
34644            menu.un("beforeshow", onBeforeShow);
34645            menu.un("show", onShow);
34646            var g = menu.group;
34647            if(g && menu.events["checkchange"]){
34648                groups[g].remove(menu);
34649                menu.un("checkchange", onCheck);
34650            }
34651        },
34652
34653        // private
34654        registerCheckable : function(menuItem){
34655            var g = menuItem.group;
34656            if(g){
34657                if(!groups[g]){
34658                    groups[g] = [];
34659                }
34660                groups[g].push(menuItem);
34661                menuItem.on("beforecheckchange", onBeforeCheck);
34662            }
34663        },
34664
34665        // private
34666        unregisterCheckable : function(menuItem){
34667            var g = menuItem.group;
34668            if(g){
34669                groups[g].remove(menuItem);
34670                menuItem.un("beforecheckchange", onBeforeCheck);
34671            }
34672        }
34673    };
34674 }();/*
34675  * Based on:
34676  * Ext JS Library 1.1.1
34677  * Copyright(c) 2006-2007, Ext JS, LLC.
34678  *
34679  * Originally Released Under LGPL - original licence link has changed is not relivant.
34680  *
34681  * Fork - LGPL
34682  * <script type="text/javascript">
34683  */
34684  
34685
34686 /**
34687  * @class Roo.menu.BaseItem
34688  * @extends Roo.Component
34689  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34690  * management and base configuration options shared by all menu components.
34691  * @constructor
34692  * Creates a new BaseItem
34693  * @param {Object} config Configuration options
34694  */
34695 Roo.menu.BaseItem = function(config){
34696     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34697
34698     this.addEvents({
34699         /**
34700          * @event click
34701          * Fires when this item is clicked
34702          * @param {Roo.menu.BaseItem} this
34703          * @param {Roo.EventObject} e
34704          */
34705         click: true,
34706         /**
34707          * @event activate
34708          * Fires when this item is activated
34709          * @param {Roo.menu.BaseItem} this
34710          */
34711         activate : true,
34712         /**
34713          * @event deactivate
34714          * Fires when this item is deactivated
34715          * @param {Roo.menu.BaseItem} this
34716          */
34717         deactivate : true
34718     });
34719
34720     if(this.handler){
34721         this.on("click", this.handler, this.scope, true);
34722     }
34723 };
34724
34725 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34726     /**
34727      * @cfg {Function} handler
34728      * A function that will handle the click event of this menu item (defaults to undefined)
34729      */
34730     /**
34731      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34732      */
34733     canActivate : false,
34734     /**
34735      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34736      */
34737     activeClass : "x-menu-item-active",
34738     /**
34739      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34740      */
34741     hideOnClick : true,
34742     /**
34743      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34744      */
34745     hideDelay : 100,
34746
34747     // private
34748     ctype: "Roo.menu.BaseItem",
34749
34750     // private
34751     actionMode : "container",
34752
34753     // private
34754     render : function(container, parentMenu){
34755         this.parentMenu = parentMenu;
34756         Roo.menu.BaseItem.superclass.render.call(this, container);
34757         this.container.menuItemId = this.id;
34758     },
34759
34760     // private
34761     onRender : function(container, position){
34762         this.el = Roo.get(this.el);
34763         container.dom.appendChild(this.el.dom);
34764     },
34765
34766     // private
34767     onClick : function(e){
34768         if(!this.disabled && this.fireEvent("click", this, e) !== false
34769                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34770             this.handleClick(e);
34771         }else{
34772             e.stopEvent();
34773         }
34774     },
34775
34776     // private
34777     activate : function(){
34778         if(this.disabled){
34779             return false;
34780         }
34781         var li = this.container;
34782         li.addClass(this.activeClass);
34783         this.region = li.getRegion().adjust(2, 2, -2, -2);
34784         this.fireEvent("activate", this);
34785         return true;
34786     },
34787
34788     // private
34789     deactivate : function(){
34790         this.container.removeClass(this.activeClass);
34791         this.fireEvent("deactivate", this);
34792     },
34793
34794     // private
34795     shouldDeactivate : function(e){
34796         return !this.region || !this.region.contains(e.getPoint());
34797     },
34798
34799     // private
34800     handleClick : function(e){
34801         if(this.hideOnClick){
34802             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34803         }
34804     },
34805
34806     // private
34807     expandMenu : function(autoActivate){
34808         // do nothing
34809     },
34810
34811     // private
34812     hideMenu : function(){
34813         // do nothing
34814     }
34815 });/*
34816  * Based on:
34817  * Ext JS Library 1.1.1
34818  * Copyright(c) 2006-2007, Ext JS, LLC.
34819  *
34820  * Originally Released Under LGPL - original licence link has changed is not relivant.
34821  *
34822  * Fork - LGPL
34823  * <script type="text/javascript">
34824  */
34825  
34826 /**
34827  * @class Roo.menu.Adapter
34828  * @extends Roo.menu.BaseItem
34829  * 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.
34830  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34831  * @constructor
34832  * Creates a new Adapter
34833  * @param {Object} config Configuration options
34834  */
34835 Roo.menu.Adapter = function(component, config){
34836     Roo.menu.Adapter.superclass.constructor.call(this, config);
34837     this.component = component;
34838 };
34839 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34840     // private
34841     canActivate : true,
34842
34843     // private
34844     onRender : function(container, position){
34845         this.component.render(container);
34846         this.el = this.component.getEl();
34847     },
34848
34849     // private
34850     activate : function(){
34851         if(this.disabled){
34852             return false;
34853         }
34854         this.component.focus();
34855         this.fireEvent("activate", this);
34856         return true;
34857     },
34858
34859     // private
34860     deactivate : function(){
34861         this.fireEvent("deactivate", this);
34862     },
34863
34864     // private
34865     disable : function(){
34866         this.component.disable();
34867         Roo.menu.Adapter.superclass.disable.call(this);
34868     },
34869
34870     // private
34871     enable : function(){
34872         this.component.enable();
34873         Roo.menu.Adapter.superclass.enable.call(this);
34874     }
34875 });/*
34876  * Based on:
34877  * Ext JS Library 1.1.1
34878  * Copyright(c) 2006-2007, Ext JS, LLC.
34879  *
34880  * Originally Released Under LGPL - original licence link has changed is not relivant.
34881  *
34882  * Fork - LGPL
34883  * <script type="text/javascript">
34884  */
34885
34886 /**
34887  * @class Roo.menu.TextItem
34888  * @extends Roo.menu.BaseItem
34889  * Adds a static text string to a menu, usually used as either a heading or group separator.
34890  * Note: old style constructor with text is still supported.
34891  * 
34892  * @constructor
34893  * Creates a new TextItem
34894  * @param {Object} cfg Configuration
34895  */
34896 Roo.menu.TextItem = function(cfg){
34897     if (typeof(cfg) == 'string') {
34898         this.text = cfg;
34899     } else {
34900         Roo.apply(this,cfg);
34901     }
34902     
34903     Roo.menu.TextItem.superclass.constructor.call(this);
34904 };
34905
34906 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34907     /**
34908      * @cfg {Boolean} text Text to show on item.
34909      */
34910     text : '',
34911     
34912     /**
34913      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34914      */
34915     hideOnClick : false,
34916     /**
34917      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34918      */
34919     itemCls : "x-menu-text",
34920
34921     // private
34922     onRender : function(){
34923         var s = document.createElement("span");
34924         s.className = this.itemCls;
34925         s.innerHTML = this.text;
34926         this.el = s;
34927         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34928     }
34929 });/*
34930  * Based on:
34931  * Ext JS Library 1.1.1
34932  * Copyright(c) 2006-2007, Ext JS, LLC.
34933  *
34934  * Originally Released Under LGPL - original licence link has changed is not relivant.
34935  *
34936  * Fork - LGPL
34937  * <script type="text/javascript">
34938  */
34939
34940 /**
34941  * @class Roo.menu.Separator
34942  * @extends Roo.menu.BaseItem
34943  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34944  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34945  * @constructor
34946  * @param {Object} config Configuration options
34947  */
34948 Roo.menu.Separator = function(config){
34949     Roo.menu.Separator.superclass.constructor.call(this, config);
34950 };
34951
34952 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34953     /**
34954      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34955      */
34956     itemCls : "x-menu-sep",
34957     /**
34958      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34959      */
34960     hideOnClick : false,
34961
34962     // private
34963     onRender : function(li){
34964         var s = document.createElement("span");
34965         s.className = this.itemCls;
34966         s.innerHTML = "&#160;";
34967         this.el = s;
34968         li.addClass("x-menu-sep-li");
34969         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34970     }
34971 });/*
34972  * Based on:
34973  * Ext JS Library 1.1.1
34974  * Copyright(c) 2006-2007, Ext JS, LLC.
34975  *
34976  * Originally Released Under LGPL - original licence link has changed is not relivant.
34977  *
34978  * Fork - LGPL
34979  * <script type="text/javascript">
34980  */
34981 /**
34982  * @class Roo.menu.Item
34983  * @extends Roo.menu.BaseItem
34984  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34985  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34986  * activation and click handling.
34987  * @constructor
34988  * Creates a new Item
34989  * @param {Object} config Configuration options
34990  */
34991 Roo.menu.Item = function(config){
34992     Roo.menu.Item.superclass.constructor.call(this, config);
34993     if(this.menu){
34994         this.menu = Roo.menu.MenuMgr.get(this.menu);
34995     }
34996 };
34997 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34998     
34999     /**
35000      * @cfg {String} text
35001      * The text to show on the menu item.
35002      */
35003     text: '',
35004      /**
35005      * @cfg {String} HTML to render in menu
35006      * The text to show on the menu item (HTML version).
35007      */
35008     html: '',
35009     /**
35010      * @cfg {String} icon
35011      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35012      */
35013     icon: undefined,
35014     /**
35015      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35016      */
35017     itemCls : "x-menu-item",
35018     /**
35019      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35020      */
35021     canActivate : true,
35022     /**
35023      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35024      */
35025     showDelay: 200,
35026     // doc'd in BaseItem
35027     hideDelay: 200,
35028
35029     // private
35030     ctype: "Roo.menu.Item",
35031     
35032     // private
35033     onRender : function(container, position){
35034         var el = document.createElement("a");
35035         el.hideFocus = true;
35036         el.unselectable = "on";
35037         el.href = this.href || "#";
35038         if(this.hrefTarget){
35039             el.target = this.hrefTarget;
35040         }
35041         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35042         
35043         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35044         
35045         el.innerHTML = String.format(
35046                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35047                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35048         this.el = el;
35049         Roo.menu.Item.superclass.onRender.call(this, container, position);
35050     },
35051
35052     /**
35053      * Sets the text to display in this menu item
35054      * @param {String} text The text to display
35055      * @param {Boolean} isHTML true to indicate text is pure html.
35056      */
35057     setText : function(text, isHTML){
35058         if (isHTML) {
35059             this.html = text;
35060         } else {
35061             this.text = text;
35062             this.html = '';
35063         }
35064         if(this.rendered){
35065             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35066      
35067             this.el.update(String.format(
35068                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35069                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35070             this.parentMenu.autoWidth();
35071         }
35072     },
35073
35074     // private
35075     handleClick : function(e){
35076         if(!this.href){ // if no link defined, stop the event automatically
35077             e.stopEvent();
35078         }
35079         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35080     },
35081
35082     // private
35083     activate : function(autoExpand){
35084         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35085             this.focus();
35086             if(autoExpand){
35087                 this.expandMenu();
35088             }
35089         }
35090         return true;
35091     },
35092
35093     // private
35094     shouldDeactivate : function(e){
35095         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35096             if(this.menu && this.menu.isVisible()){
35097                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35098             }
35099             return true;
35100         }
35101         return false;
35102     },
35103
35104     // private
35105     deactivate : function(){
35106         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35107         this.hideMenu();
35108     },
35109
35110     // private
35111     expandMenu : function(autoActivate){
35112         if(!this.disabled && this.menu){
35113             clearTimeout(this.hideTimer);
35114             delete this.hideTimer;
35115             if(!this.menu.isVisible() && !this.showTimer){
35116                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35117             }else if (this.menu.isVisible() && autoActivate){
35118                 this.menu.tryActivate(0, 1);
35119             }
35120         }
35121     },
35122
35123     // private
35124     deferExpand : function(autoActivate){
35125         delete this.showTimer;
35126         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35127         if(autoActivate){
35128             this.menu.tryActivate(0, 1);
35129         }
35130     },
35131
35132     // private
35133     hideMenu : function(){
35134         clearTimeout(this.showTimer);
35135         delete this.showTimer;
35136         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35137             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35138         }
35139     },
35140
35141     // private
35142     deferHide : function(){
35143         delete this.hideTimer;
35144         this.menu.hide();
35145     }
35146 });/*
35147  * Based on:
35148  * Ext JS Library 1.1.1
35149  * Copyright(c) 2006-2007, Ext JS, LLC.
35150  *
35151  * Originally Released Under LGPL - original licence link has changed is not relivant.
35152  *
35153  * Fork - LGPL
35154  * <script type="text/javascript">
35155  */
35156  
35157 /**
35158  * @class Roo.menu.CheckItem
35159  * @extends Roo.menu.Item
35160  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35161  * @constructor
35162  * Creates a new CheckItem
35163  * @param {Object} config Configuration options
35164  */
35165 Roo.menu.CheckItem = function(config){
35166     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35167     this.addEvents({
35168         /**
35169          * @event beforecheckchange
35170          * Fires before the checked value is set, providing an opportunity to cancel if needed
35171          * @param {Roo.menu.CheckItem} this
35172          * @param {Boolean} checked The new checked value that will be set
35173          */
35174         "beforecheckchange" : true,
35175         /**
35176          * @event checkchange
35177          * Fires after the checked value has been set
35178          * @param {Roo.menu.CheckItem} this
35179          * @param {Boolean} checked The checked value that was set
35180          */
35181         "checkchange" : true
35182     });
35183     if(this.checkHandler){
35184         this.on('checkchange', this.checkHandler, this.scope);
35185     }
35186 };
35187 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35188     /**
35189      * @cfg {String} group
35190      * All check items with the same group name will automatically be grouped into a single-select
35191      * radio button group (defaults to '')
35192      */
35193     /**
35194      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35195      */
35196     itemCls : "x-menu-item x-menu-check-item",
35197     /**
35198      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35199      */
35200     groupClass : "x-menu-group-item",
35201
35202     /**
35203      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35204      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35205      * initialized with checked = true will be rendered as checked.
35206      */
35207     checked: false,
35208
35209     // private
35210     ctype: "Roo.menu.CheckItem",
35211
35212     // private
35213     onRender : function(c){
35214         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35215         if(this.group){
35216             this.el.addClass(this.groupClass);
35217         }
35218         Roo.menu.MenuMgr.registerCheckable(this);
35219         if(this.checked){
35220             this.checked = false;
35221             this.setChecked(true, true);
35222         }
35223     },
35224
35225     // private
35226     destroy : function(){
35227         if(this.rendered){
35228             Roo.menu.MenuMgr.unregisterCheckable(this);
35229         }
35230         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35231     },
35232
35233     /**
35234      * Set the checked state of this item
35235      * @param {Boolean} checked The new checked value
35236      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35237      */
35238     setChecked : function(state, suppressEvent){
35239         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35240             if(this.container){
35241                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35242             }
35243             this.checked = state;
35244             if(suppressEvent !== true){
35245                 this.fireEvent("checkchange", this, state);
35246             }
35247         }
35248     },
35249
35250     // private
35251     handleClick : function(e){
35252        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35253            this.setChecked(!this.checked);
35254        }
35255        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35256     }
35257 });/*
35258  * Based on:
35259  * Ext JS Library 1.1.1
35260  * Copyright(c) 2006-2007, Ext JS, LLC.
35261  *
35262  * Originally Released Under LGPL - original licence link has changed is not relivant.
35263  *
35264  * Fork - LGPL
35265  * <script type="text/javascript">
35266  */
35267  
35268 /**
35269  * @class Roo.menu.DateItem
35270  * @extends Roo.menu.Adapter
35271  * A menu item that wraps the {@link Roo.DatPicker} component.
35272  * @constructor
35273  * Creates a new DateItem
35274  * @param {Object} config Configuration options
35275  */
35276 Roo.menu.DateItem = function(config){
35277     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35278     /** The Roo.DatePicker object @type Roo.DatePicker */
35279     this.picker = this.component;
35280     this.addEvents({select: true});
35281     
35282     this.picker.on("render", function(picker){
35283         picker.getEl().swallowEvent("click");
35284         picker.container.addClass("x-menu-date-item");
35285     });
35286
35287     this.picker.on("select", this.onSelect, this);
35288 };
35289
35290 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35291     // private
35292     onSelect : function(picker, date){
35293         this.fireEvent("select", this, date, picker);
35294         Roo.menu.DateItem.superclass.handleClick.call(this);
35295     }
35296 });/*
35297  * Based on:
35298  * Ext JS Library 1.1.1
35299  * Copyright(c) 2006-2007, Ext JS, LLC.
35300  *
35301  * Originally Released Under LGPL - original licence link has changed is not relivant.
35302  *
35303  * Fork - LGPL
35304  * <script type="text/javascript">
35305  */
35306  
35307 /**
35308  * @class Roo.menu.ColorItem
35309  * @extends Roo.menu.Adapter
35310  * A menu item that wraps the {@link Roo.ColorPalette} component.
35311  * @constructor
35312  * Creates a new ColorItem
35313  * @param {Object} config Configuration options
35314  */
35315 Roo.menu.ColorItem = function(config){
35316     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35317     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35318     this.palette = this.component;
35319     this.relayEvents(this.palette, ["select"]);
35320     if(this.selectHandler){
35321         this.on('select', this.selectHandler, this.scope);
35322     }
35323 };
35324 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335
35336 /**
35337  * @class Roo.menu.DateMenu
35338  * @extends Roo.menu.Menu
35339  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35340  * @constructor
35341  * Creates a new DateMenu
35342  * @param {Object} config Configuration options
35343  */
35344 Roo.menu.DateMenu = function(config){
35345     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35346     this.plain = true;
35347     var di = new Roo.menu.DateItem(config);
35348     this.add(di);
35349     /**
35350      * The {@link Roo.DatePicker} instance for this DateMenu
35351      * @type DatePicker
35352      */
35353     this.picker = di.picker;
35354     /**
35355      * @event select
35356      * @param {DatePicker} picker
35357      * @param {Date} date
35358      */
35359     this.relayEvents(di, ["select"]);
35360
35361     this.on('beforeshow', function(){
35362         if(this.picker){
35363             this.picker.hideMonthPicker(true);
35364         }
35365     }, this);
35366 };
35367 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35368     cls:'x-date-menu'
35369 });/*
35370  * Based on:
35371  * Ext JS Library 1.1.1
35372  * Copyright(c) 2006-2007, Ext JS, LLC.
35373  *
35374  * Originally Released Under LGPL - original licence link has changed is not relivant.
35375  *
35376  * Fork - LGPL
35377  * <script type="text/javascript">
35378  */
35379  
35380
35381 /**
35382  * @class Roo.menu.ColorMenu
35383  * @extends Roo.menu.Menu
35384  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35385  * @constructor
35386  * Creates a new ColorMenu
35387  * @param {Object} config Configuration options
35388  */
35389 Roo.menu.ColorMenu = function(config){
35390     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35391     this.plain = true;
35392     var ci = new Roo.menu.ColorItem(config);
35393     this.add(ci);
35394     /**
35395      * The {@link Roo.ColorPalette} instance for this ColorMenu
35396      * @type ColorPalette
35397      */
35398     this.palette = ci.palette;
35399     /**
35400      * @event select
35401      * @param {ColorPalette} palette
35402      * @param {String} color
35403      */
35404     this.relayEvents(ci, ["select"]);
35405 };
35406 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35407  * Based on:
35408  * Ext JS Library 1.1.1
35409  * Copyright(c) 2006-2007, Ext JS, LLC.
35410  *
35411  * Originally Released Under LGPL - original licence link has changed is not relivant.
35412  *
35413  * Fork - LGPL
35414  * <script type="text/javascript">
35415  */
35416  
35417 /**
35418  * @class Roo.form.Field
35419  * @extends Roo.BoxComponent
35420  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35421  * @constructor
35422  * Creates a new Field
35423  * @param {Object} config Configuration options
35424  */
35425 Roo.form.Field = function(config){
35426     Roo.form.Field.superclass.constructor.call(this, config);
35427 };
35428
35429 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35430     /**
35431      * @cfg {String} fieldLabel Label to use when rendering a form.
35432      */
35433        /**
35434      * @cfg {String} qtip Mouse over tip
35435      */
35436      
35437     /**
35438      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35439      */
35440     invalidClass : "x-form-invalid",
35441     /**
35442      * @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")
35443      */
35444     invalidText : "The value in this field is invalid",
35445     /**
35446      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35447      */
35448     focusClass : "x-form-focus",
35449     /**
35450      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35451       automatic validation (defaults to "keyup").
35452      */
35453     validationEvent : "keyup",
35454     /**
35455      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35456      */
35457     validateOnBlur : true,
35458     /**
35459      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35460      */
35461     validationDelay : 250,
35462     /**
35463      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35464      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35465      */
35466     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35467     /**
35468      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35469      */
35470     fieldClass : "x-form-field",
35471     /**
35472      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35473      *<pre>
35474 Value         Description
35475 -----------   ----------------------------------------------------------------------
35476 qtip          Display a quick tip when the user hovers over the field
35477 title         Display a default browser title attribute popup
35478 under         Add a block div beneath the field containing the error text
35479 side          Add an error icon to the right of the field with a popup on hover
35480 [element id]  Add the error text directly to the innerHTML of the specified element
35481 </pre>
35482      */
35483     msgTarget : 'qtip',
35484     /**
35485      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35486      */
35487     msgFx : 'normal',
35488
35489     /**
35490      * @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.
35491      */
35492     readOnly : false,
35493
35494     /**
35495      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35496      */
35497     disabled : false,
35498
35499     /**
35500      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35501      */
35502     inputType : undefined,
35503     
35504     /**
35505      * @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).
35506          */
35507         tabIndex : undefined,
35508         
35509     // private
35510     isFormField : true,
35511
35512     // private
35513     hasFocus : false,
35514     /**
35515      * @property {Roo.Element} fieldEl
35516      * Element Containing the rendered Field (with label etc.)
35517      */
35518     /**
35519      * @cfg {Mixed} value A value to initialize this field with.
35520      */
35521     value : undefined,
35522
35523     /**
35524      * @cfg {String} name The field's HTML name attribute.
35525      */
35526     /**
35527      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35528      */
35529
35530         // private ??
35531         initComponent : function(){
35532         Roo.form.Field.superclass.initComponent.call(this);
35533         this.addEvents({
35534             /**
35535              * @event focus
35536              * Fires when this field receives input focus.
35537              * @param {Roo.form.Field} this
35538              */
35539             focus : true,
35540             /**
35541              * @event blur
35542              * Fires when this field loses input focus.
35543              * @param {Roo.form.Field} this
35544              */
35545             blur : true,
35546             /**
35547              * @event specialkey
35548              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35549              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35550              * @param {Roo.form.Field} this
35551              * @param {Roo.EventObject} e The event object
35552              */
35553             specialkey : true,
35554             /**
35555              * @event change
35556              * Fires just before the field blurs if the field value has changed.
35557              * @param {Roo.form.Field} this
35558              * @param {Mixed} newValue The new value
35559              * @param {Mixed} oldValue The original value
35560              */
35561             change : true,
35562             /**
35563              * @event invalid
35564              * Fires after the field has been marked as invalid.
35565              * @param {Roo.form.Field} this
35566              * @param {String} msg The validation message
35567              */
35568             invalid : true,
35569             /**
35570              * @event valid
35571              * Fires after the field has been validated with no errors.
35572              * @param {Roo.form.Field} this
35573              */
35574             valid : true,
35575              /**
35576              * @event keyup
35577              * Fires after the key up
35578              * @param {Roo.form.Field} this
35579              * @param {Roo.EventObject}  e The event Object
35580              */
35581             keyup : true
35582         });
35583     },
35584
35585     /**
35586      * Returns the name attribute of the field if available
35587      * @return {String} name The field name
35588      */
35589     getName: function(){
35590          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35591     },
35592
35593     // private
35594     onRender : function(ct, position){
35595         Roo.form.Field.superclass.onRender.call(this, ct, position);
35596         if(!this.el){
35597             var cfg = this.getAutoCreate();
35598             if(!cfg.name){
35599                 cfg.name = this.name || this.id;
35600             }
35601             if(this.inputType){
35602                 cfg.type = this.inputType;
35603             }
35604             this.el = ct.createChild(cfg, position);
35605         }
35606         var type = this.el.dom.type;
35607         if(type){
35608             if(type == 'password'){
35609                 type = 'text';
35610             }
35611             this.el.addClass('x-form-'+type);
35612         }
35613         if(this.readOnly){
35614             this.el.dom.readOnly = true;
35615         }
35616         if(this.tabIndex !== undefined){
35617             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35618         }
35619
35620         this.el.addClass([this.fieldClass, this.cls]);
35621         this.initValue();
35622     },
35623
35624     /**
35625      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35626      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35627      * @return {Roo.form.Field} this
35628      */
35629     applyTo : function(target){
35630         this.allowDomMove = false;
35631         this.el = Roo.get(target);
35632         this.render(this.el.dom.parentNode);
35633         return this;
35634     },
35635
35636     // private
35637     initValue : function(){
35638         if(this.value !== undefined){
35639             this.setValue(this.value);
35640         }else if(this.el.dom.value.length > 0){
35641             this.setValue(this.el.dom.value);
35642         }
35643     },
35644
35645     /**
35646      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35647      */
35648     isDirty : function() {
35649         if(this.disabled) {
35650             return false;
35651         }
35652         return String(this.getValue()) !== String(this.originalValue);
35653     },
35654
35655     // private
35656     afterRender : function(){
35657         Roo.form.Field.superclass.afterRender.call(this);
35658         this.initEvents();
35659     },
35660
35661     // private
35662     fireKey : function(e){
35663         //Roo.log('field ' + e.getKey());
35664         if(e.isNavKeyPress()){
35665             this.fireEvent("specialkey", this, e);
35666         }
35667     },
35668
35669     /**
35670      * Resets the current field value to the originally loaded value and clears any validation messages
35671      */
35672     reset : function(){
35673         this.setValue(this.originalValue);
35674         this.clearInvalid();
35675     },
35676
35677     // private
35678     initEvents : function(){
35679         // safari killled keypress - so keydown is now used..
35680         this.el.on("keydown" , this.fireKey,  this);
35681         this.el.on("focus", this.onFocus,  this);
35682         this.el.on("blur", this.onBlur,  this);
35683         this.el.relayEvent('keyup', this);
35684
35685         // reference to original value for reset
35686         this.originalValue = this.getValue();
35687     },
35688
35689     // private
35690     onFocus : function(){
35691         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35692             this.el.addClass(this.focusClass);
35693         }
35694         if(!this.hasFocus){
35695             this.hasFocus = true;
35696             this.startValue = this.getValue();
35697             this.fireEvent("focus", this);
35698         }
35699     },
35700
35701     beforeBlur : Roo.emptyFn,
35702
35703     // private
35704     onBlur : function(){
35705         this.beforeBlur();
35706         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35707             this.el.removeClass(this.focusClass);
35708         }
35709         this.hasFocus = false;
35710         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35711             this.validate();
35712         }
35713         var v = this.getValue();
35714         if(String(v) !== String(this.startValue)){
35715             this.fireEvent('change', this, v, this.startValue);
35716         }
35717         this.fireEvent("blur", this);
35718     },
35719
35720     /**
35721      * Returns whether or not the field value is currently valid
35722      * @param {Boolean} preventMark True to disable marking the field invalid
35723      * @return {Boolean} True if the value is valid, else false
35724      */
35725     isValid : function(preventMark){
35726         if(this.disabled){
35727             return true;
35728         }
35729         var restore = this.preventMark;
35730         this.preventMark = preventMark === true;
35731         var v = this.validateValue(this.processValue(this.getRawValue()));
35732         this.preventMark = restore;
35733         return v;
35734     },
35735
35736     /**
35737      * Validates the field value
35738      * @return {Boolean} True if the value is valid, else false
35739      */
35740     validate : function(){
35741         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35742             this.clearInvalid();
35743             return true;
35744         }
35745         return false;
35746     },
35747
35748     processValue : function(value){
35749         return value;
35750     },
35751
35752     // private
35753     // Subclasses should provide the validation implementation by overriding this
35754     validateValue : function(value){
35755         return true;
35756     },
35757
35758     /**
35759      * Mark this field as invalid
35760      * @param {String} msg The validation message
35761      */
35762     markInvalid : function(msg){
35763         if(!this.rendered || this.preventMark){ // not rendered
35764             return;
35765         }
35766         this.el.addClass(this.invalidClass);
35767         msg = msg || this.invalidText;
35768         switch(this.msgTarget){
35769             case 'qtip':
35770                 this.el.dom.qtip = msg;
35771                 this.el.dom.qclass = 'x-form-invalid-tip';
35772                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35773                     Roo.QuickTips.enable();
35774                 }
35775                 break;
35776             case 'title':
35777                 this.el.dom.title = msg;
35778                 break;
35779             case 'under':
35780                 if(!this.errorEl){
35781                     var elp = this.el.findParent('.x-form-element', 5, true);
35782                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35783                     this.errorEl.setWidth(elp.getWidth(true)-20);
35784                 }
35785                 this.errorEl.update(msg);
35786                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35787                 break;
35788             case 'side':
35789                 if(!this.errorIcon){
35790                     var elp = this.el.findParent('.x-form-element', 5, true);
35791                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35792                 }
35793                 this.alignErrorIcon();
35794                 this.errorIcon.dom.qtip = msg;
35795                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35796                 this.errorIcon.show();
35797                 this.on('resize', this.alignErrorIcon, this);
35798                 break;
35799             default:
35800                 var t = Roo.getDom(this.msgTarget);
35801                 t.innerHTML = msg;
35802                 t.style.display = this.msgDisplay;
35803                 break;
35804         }
35805         this.fireEvent('invalid', this, msg);
35806     },
35807
35808     // private
35809     alignErrorIcon : function(){
35810         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35811     },
35812
35813     /**
35814      * Clear any invalid styles/messages for this field
35815      */
35816     clearInvalid : function(){
35817         if(!this.rendered || this.preventMark){ // not rendered
35818             return;
35819         }
35820         this.el.removeClass(this.invalidClass);
35821         switch(this.msgTarget){
35822             case 'qtip':
35823                 this.el.dom.qtip = '';
35824                 break;
35825             case 'title':
35826                 this.el.dom.title = '';
35827                 break;
35828             case 'under':
35829                 if(this.errorEl){
35830                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35831                 }
35832                 break;
35833             case 'side':
35834                 if(this.errorIcon){
35835                     this.errorIcon.dom.qtip = '';
35836                     this.errorIcon.hide();
35837                     this.un('resize', this.alignErrorIcon, this);
35838                 }
35839                 break;
35840             default:
35841                 var t = Roo.getDom(this.msgTarget);
35842                 t.innerHTML = '';
35843                 t.style.display = 'none';
35844                 break;
35845         }
35846         this.fireEvent('valid', this);
35847     },
35848
35849     /**
35850      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35851      * @return {Mixed} value The field value
35852      */
35853     getRawValue : function(){
35854         var v = this.el.getValue();
35855         if(v === this.emptyText){
35856             v = '';
35857         }
35858         return v;
35859     },
35860
35861     /**
35862      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35863      * @return {Mixed} value The field value
35864      */
35865     getValue : function(){
35866         var v = this.el.getValue();
35867         if(v === this.emptyText || v === undefined){
35868             v = '';
35869         }
35870         return v;
35871     },
35872
35873     /**
35874      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35875      * @param {Mixed} value The value to set
35876      */
35877     setRawValue : function(v){
35878         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35879     },
35880
35881     /**
35882      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35883      * @param {Mixed} value The value to set
35884      */
35885     setValue : function(v){
35886         this.value = v;
35887         if(this.rendered){
35888             this.el.dom.value = (v === null || v === undefined ? '' : v);
35889             this.validate();
35890         }
35891     },
35892
35893     adjustSize : function(w, h){
35894         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35895         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35896         return s;
35897     },
35898
35899     adjustWidth : function(tag, w){
35900         tag = tag.toLowerCase();
35901         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35902             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35903                 if(tag == 'input'){
35904                     return w + 2;
35905                 }
35906                 if(tag = 'textarea'){
35907                     return w-2;
35908                 }
35909             }else if(Roo.isOpera){
35910                 if(tag == 'input'){
35911                     return w + 2;
35912                 }
35913                 if(tag = 'textarea'){
35914                     return w-2;
35915                 }
35916             }
35917         }
35918         return w;
35919     }
35920 });
35921
35922
35923 // anything other than normal should be considered experimental
35924 Roo.form.Field.msgFx = {
35925     normal : {
35926         show: function(msgEl, f){
35927             msgEl.setDisplayed('block');
35928         },
35929
35930         hide : function(msgEl, f){
35931             msgEl.setDisplayed(false).update('');
35932         }
35933     },
35934
35935     slide : {
35936         show: function(msgEl, f){
35937             msgEl.slideIn('t', {stopFx:true});
35938         },
35939
35940         hide : function(msgEl, f){
35941             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35942         }
35943     },
35944
35945     slideRight : {
35946         show: function(msgEl, f){
35947             msgEl.fixDisplay();
35948             msgEl.alignTo(f.el, 'tl-tr');
35949             msgEl.slideIn('l', {stopFx:true});
35950         },
35951
35952         hide : function(msgEl, f){
35953             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35954         }
35955     }
35956 };/*
35957  * Based on:
35958  * Ext JS Library 1.1.1
35959  * Copyright(c) 2006-2007, Ext JS, LLC.
35960  *
35961  * Originally Released Under LGPL - original licence link has changed is not relivant.
35962  *
35963  * Fork - LGPL
35964  * <script type="text/javascript">
35965  */
35966  
35967
35968 /**
35969  * @class Roo.form.TextField
35970  * @extends Roo.form.Field
35971  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35972  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35973  * @constructor
35974  * Creates a new TextField
35975  * @param {Object} config Configuration options
35976  */
35977 Roo.form.TextField = function(config){
35978     Roo.form.TextField.superclass.constructor.call(this, config);
35979     this.addEvents({
35980         /**
35981          * @event autosize
35982          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35983          * according to the default logic, but this event provides a hook for the developer to apply additional
35984          * logic at runtime to resize the field if needed.
35985              * @param {Roo.form.Field} this This text field
35986              * @param {Number} width The new field width
35987              */
35988         autosize : true
35989     });
35990 };
35991
35992 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35993     /**
35994      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35995      */
35996     grow : false,
35997     /**
35998      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35999      */
36000     growMin : 30,
36001     /**
36002      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36003      */
36004     growMax : 800,
36005     /**
36006      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36007      */
36008     vtype : null,
36009     /**
36010      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36011      */
36012     maskRe : null,
36013     /**
36014      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36015      */
36016     disableKeyFilter : false,
36017     /**
36018      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36019      */
36020     allowBlank : true,
36021     /**
36022      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36023      */
36024     minLength : 0,
36025     /**
36026      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36027      */
36028     maxLength : Number.MAX_VALUE,
36029     /**
36030      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36031      */
36032     minLengthText : "The minimum length for this field is {0}",
36033     /**
36034      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36035      */
36036     maxLengthText : "The maximum length for this field is {0}",
36037     /**
36038      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36039      */
36040     selectOnFocus : false,
36041     /**
36042      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36043      */
36044     blankText : "This field is required",
36045     /**
36046      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36047      * If available, this function will be called only after the basic validators all return true, and will be passed the
36048      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36049      */
36050     validator : null,
36051     /**
36052      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36053      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36054      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36055      */
36056     regex : null,
36057     /**
36058      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36059      */
36060     regexText : "",
36061     /**
36062      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36063      */
36064     emptyText : null,
36065     /**
36066      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36067      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36068      */
36069     emptyClass : 'x-form-empty-field',
36070
36071     // private
36072     initEvents : function(){
36073         Roo.form.TextField.superclass.initEvents.call(this);
36074         if(this.validationEvent == 'keyup'){
36075             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36076             this.el.on('keyup', this.filterValidation, this);
36077         }
36078         else if(this.validationEvent !== false){
36079             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36080         }
36081         if(this.selectOnFocus || this.emptyText){
36082             this.on("focus", this.preFocus, this);
36083             if(this.emptyText){
36084                 this.on('blur', this.postBlur, this);
36085                 this.applyEmptyText();
36086             }
36087         }
36088         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36089             this.el.on("keypress", this.filterKeys, this);
36090         }
36091         if(this.grow){
36092             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36093             this.el.on("click", this.autoSize,  this);
36094         }
36095     },
36096
36097     processValue : function(value){
36098         if(this.stripCharsRe){
36099             var newValue = value.replace(this.stripCharsRe, '');
36100             if(newValue !== value){
36101                 this.setRawValue(newValue);
36102                 return newValue;
36103             }
36104         }
36105         return value;
36106     },
36107
36108     filterValidation : function(e){
36109         if(!e.isNavKeyPress()){
36110             this.validationTask.delay(this.validationDelay);
36111         }
36112     },
36113
36114     // private
36115     onKeyUp : function(e){
36116         if(!e.isNavKeyPress()){
36117             this.autoSize();
36118         }
36119     },
36120
36121     /**
36122      * Resets the current field value to the originally-loaded value and clears any validation messages.
36123      * Also adds emptyText and emptyClass if the original value was blank.
36124      */
36125     reset : function(){
36126         Roo.form.TextField.superclass.reset.call(this);
36127         this.applyEmptyText();
36128     },
36129
36130     applyEmptyText : function(){
36131         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36132             this.setRawValue(this.emptyText);
36133             this.el.addClass(this.emptyClass);
36134         }
36135     },
36136
36137     // private
36138     preFocus : function(){
36139         if(this.emptyText){
36140             if(this.el.dom.value == this.emptyText){
36141                 this.setRawValue('');
36142             }
36143             this.el.removeClass(this.emptyClass);
36144         }
36145         if(this.selectOnFocus){
36146             this.el.dom.select();
36147         }
36148     },
36149
36150     // private
36151     postBlur : function(){
36152         this.applyEmptyText();
36153     },
36154
36155     // private
36156     filterKeys : function(e){
36157         var k = e.getKey();
36158         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36159             return;
36160         }
36161         var c = e.getCharCode(), cc = String.fromCharCode(c);
36162         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36163             return;
36164         }
36165         if(!this.maskRe.test(cc)){
36166             e.stopEvent();
36167         }
36168     },
36169
36170     setValue : function(v){
36171         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36172             this.el.removeClass(this.emptyClass);
36173         }
36174         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36175         this.applyEmptyText();
36176         this.autoSize();
36177     },
36178
36179     /**
36180      * Validates a value according to the field's validation rules and marks the field as invalid
36181      * if the validation fails
36182      * @param {Mixed} value The value to validate
36183      * @return {Boolean} True if the value is valid, else false
36184      */
36185     validateValue : function(value){
36186         if(value.length < 1 || value === this.emptyText){ // if it's blank
36187              if(this.allowBlank){
36188                 this.clearInvalid();
36189                 return true;
36190              }else{
36191                 this.markInvalid(this.blankText);
36192                 return false;
36193              }
36194         }
36195         if(value.length < this.minLength){
36196             this.markInvalid(String.format(this.minLengthText, this.minLength));
36197             return false;
36198         }
36199         if(value.length > this.maxLength){
36200             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36201             return false;
36202         }
36203         if(this.vtype){
36204             var vt = Roo.form.VTypes;
36205             if(!vt[this.vtype](value, this)){
36206                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36207                 return false;
36208             }
36209         }
36210         if(typeof this.validator == "function"){
36211             var msg = this.validator(value);
36212             if(msg !== true){
36213                 this.markInvalid(msg);
36214                 return false;
36215             }
36216         }
36217         if(this.regex && !this.regex.test(value)){
36218             this.markInvalid(this.regexText);
36219             return false;
36220         }
36221         return true;
36222     },
36223
36224     /**
36225      * Selects text in this field
36226      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36227      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36228      */
36229     selectText : function(start, end){
36230         var v = this.getRawValue();
36231         if(v.length > 0){
36232             start = start === undefined ? 0 : start;
36233             end = end === undefined ? v.length : end;
36234             var d = this.el.dom;
36235             if(d.setSelectionRange){
36236                 d.setSelectionRange(start, end);
36237             }else if(d.createTextRange){
36238                 var range = d.createTextRange();
36239                 range.moveStart("character", start);
36240                 range.moveEnd("character", v.length-end);
36241                 range.select();
36242             }
36243         }
36244     },
36245
36246     /**
36247      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36248      * This only takes effect if grow = true, and fires the autosize event.
36249      */
36250     autoSize : function(){
36251         if(!this.grow || !this.rendered){
36252             return;
36253         }
36254         if(!this.metrics){
36255             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36256         }
36257         var el = this.el;
36258         var v = el.dom.value;
36259         var d = document.createElement('div');
36260         d.appendChild(document.createTextNode(v));
36261         v = d.innerHTML;
36262         d = null;
36263         v += "&#160;";
36264         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36265         this.el.setWidth(w);
36266         this.fireEvent("autosize", this, w);
36267     }
36268 });/*
36269  * Based on:
36270  * Ext JS Library 1.1.1
36271  * Copyright(c) 2006-2007, Ext JS, LLC.
36272  *
36273  * Originally Released Under LGPL - original licence link has changed is not relivant.
36274  *
36275  * Fork - LGPL
36276  * <script type="text/javascript">
36277  */
36278  
36279 /**
36280  * @class Roo.form.Hidden
36281  * @extends Roo.form.TextField
36282  * Simple Hidden element used on forms 
36283  * 
36284  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36285  * 
36286  * @constructor
36287  * Creates a new Hidden form element.
36288  * @param {Object} config Configuration options
36289  */
36290
36291
36292
36293 // easy hidden field...
36294 Roo.form.Hidden = function(config){
36295     Roo.form.Hidden.superclass.constructor.call(this, config);
36296 };
36297   
36298 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36299     fieldLabel:      '',
36300     inputType:      'hidden',
36301     width:          50,
36302     allowBlank:     true,
36303     labelSeparator: '',
36304     hidden:         true,
36305     itemCls :       'x-form-item-display-none'
36306
36307
36308 });
36309
36310
36311 /*
36312  * Based on:
36313  * Ext JS Library 1.1.1
36314  * Copyright(c) 2006-2007, Ext JS, LLC.
36315  *
36316  * Originally Released Under LGPL - original licence link has changed is not relivant.
36317  *
36318  * Fork - LGPL
36319  * <script type="text/javascript">
36320  */
36321  
36322 /**
36323  * @class Roo.form.TriggerField
36324  * @extends Roo.form.TextField
36325  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36326  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36327  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36328  * for which you can provide a custom implementation.  For example:
36329  * <pre><code>
36330 var trigger = new Roo.form.TriggerField();
36331 trigger.onTriggerClick = myTriggerFn;
36332 trigger.applyTo('my-field');
36333 </code></pre>
36334  *
36335  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36336  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36337  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36338  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36339  * @constructor
36340  * Create a new TriggerField.
36341  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36342  * to the base TextField)
36343  */
36344 Roo.form.TriggerField = function(config){
36345     this.mimicing = false;
36346     Roo.form.TriggerField.superclass.constructor.call(this, config);
36347 };
36348
36349 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36350     /**
36351      * @cfg {String} triggerClass A CSS class to apply to the trigger
36352      */
36353     /**
36354      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36355      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36356      */
36357     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36358     /**
36359      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36360      */
36361     hideTrigger:false,
36362
36363     /** @cfg {Boolean} grow @hide */
36364     /** @cfg {Number} growMin @hide */
36365     /** @cfg {Number} growMax @hide */
36366
36367     /**
36368      * @hide 
36369      * @method
36370      */
36371     autoSize: Roo.emptyFn,
36372     // private
36373     monitorTab : true,
36374     // private
36375     deferHeight : true,
36376
36377     
36378     actionMode : 'wrap',
36379     // private
36380     onResize : function(w, h){
36381         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36382         if(typeof w == 'number'){
36383             var x = w - this.trigger.getWidth();
36384             this.el.setWidth(this.adjustWidth('input', x));
36385             this.trigger.setStyle('left', x+'px');
36386         }
36387     },
36388
36389     // private
36390     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36391
36392     // private
36393     getResizeEl : function(){
36394         return this.wrap;
36395     },
36396
36397     // private
36398     getPositionEl : function(){
36399         return this.wrap;
36400     },
36401
36402     // private
36403     alignErrorIcon : function(){
36404         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36405     },
36406
36407     // private
36408     onRender : function(ct, position){
36409         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36410         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36411         this.trigger = this.wrap.createChild(this.triggerConfig ||
36412                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36413         if(this.hideTrigger){
36414             this.trigger.setDisplayed(false);
36415         }
36416         this.initTrigger();
36417         if(!this.width){
36418             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36419         }
36420     },
36421
36422     // private
36423     initTrigger : function(){
36424         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36425         this.trigger.addClassOnOver('x-form-trigger-over');
36426         this.trigger.addClassOnClick('x-form-trigger-click');
36427     },
36428
36429     // private
36430     onDestroy : function(){
36431         if(this.trigger){
36432             this.trigger.removeAllListeners();
36433             this.trigger.remove();
36434         }
36435         if(this.wrap){
36436             this.wrap.remove();
36437         }
36438         Roo.form.TriggerField.superclass.onDestroy.call(this);
36439     },
36440
36441     // private
36442     onFocus : function(){
36443         Roo.form.TriggerField.superclass.onFocus.call(this);
36444         if(!this.mimicing){
36445             this.wrap.addClass('x-trigger-wrap-focus');
36446             this.mimicing = true;
36447             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36448             if(this.monitorTab){
36449                 this.el.on("keydown", this.checkTab, this);
36450             }
36451         }
36452     },
36453
36454     // private
36455     checkTab : function(e){
36456         if(e.getKey() == e.TAB){
36457             this.triggerBlur();
36458         }
36459     },
36460
36461     // private
36462     onBlur : function(){
36463         // do nothing
36464     },
36465
36466     // private
36467     mimicBlur : function(e, t){
36468         if(!this.wrap.contains(t) && this.validateBlur()){
36469             this.triggerBlur();
36470         }
36471     },
36472
36473     // private
36474     triggerBlur : function(){
36475         this.mimicing = false;
36476         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36477         if(this.monitorTab){
36478             this.el.un("keydown", this.checkTab, this);
36479         }
36480         this.wrap.removeClass('x-trigger-wrap-focus');
36481         Roo.form.TriggerField.superclass.onBlur.call(this);
36482     },
36483
36484     // private
36485     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36486     validateBlur : function(e, t){
36487         return true;
36488     },
36489
36490     // private
36491     onDisable : function(){
36492         Roo.form.TriggerField.superclass.onDisable.call(this);
36493         if(this.wrap){
36494             this.wrap.addClass('x-item-disabled');
36495         }
36496     },
36497
36498     // private
36499     onEnable : function(){
36500         Roo.form.TriggerField.superclass.onEnable.call(this);
36501         if(this.wrap){
36502             this.wrap.removeClass('x-item-disabled');
36503         }
36504     },
36505
36506     // private
36507     onShow : function(){
36508         var ae = this.getActionEl();
36509         
36510         if(ae){
36511             ae.dom.style.display = '';
36512             ae.dom.style.visibility = 'visible';
36513         }
36514     },
36515
36516     // private
36517     
36518     onHide : function(){
36519         var ae = this.getActionEl();
36520         ae.dom.style.display = 'none';
36521     },
36522
36523     /**
36524      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36525      * by an implementing function.
36526      * @method
36527      * @param {EventObject} e
36528      */
36529     onTriggerClick : Roo.emptyFn
36530 });
36531
36532 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36533 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36534 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36535 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36536     initComponent : function(){
36537         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36538
36539         this.triggerConfig = {
36540             tag:'span', cls:'x-form-twin-triggers', cn:[
36541             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36542             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36543         ]};
36544     },
36545
36546     getTrigger : function(index){
36547         return this.triggers[index];
36548     },
36549
36550     initTrigger : function(){
36551         var ts = this.trigger.select('.x-form-trigger', true);
36552         this.wrap.setStyle('overflow', 'hidden');
36553         var triggerField = this;
36554         ts.each(function(t, all, index){
36555             t.hide = function(){
36556                 var w = triggerField.wrap.getWidth();
36557                 this.dom.style.display = 'none';
36558                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36559             };
36560             t.show = function(){
36561                 var w = triggerField.wrap.getWidth();
36562                 this.dom.style.display = '';
36563                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36564             };
36565             var triggerIndex = 'Trigger'+(index+1);
36566
36567             if(this['hide'+triggerIndex]){
36568                 t.dom.style.display = 'none';
36569             }
36570             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36571             t.addClassOnOver('x-form-trigger-over');
36572             t.addClassOnClick('x-form-trigger-click');
36573         }, this);
36574         this.triggers = ts.elements;
36575     },
36576
36577     onTrigger1Click : Roo.emptyFn,
36578     onTrigger2Click : Roo.emptyFn
36579 });/*
36580  * Based on:
36581  * Ext JS Library 1.1.1
36582  * Copyright(c) 2006-2007, Ext JS, LLC.
36583  *
36584  * Originally Released Under LGPL - original licence link has changed is not relivant.
36585  *
36586  * Fork - LGPL
36587  * <script type="text/javascript">
36588  */
36589  
36590 /**
36591  * @class Roo.form.TextArea
36592  * @extends Roo.form.TextField
36593  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36594  * support for auto-sizing.
36595  * @constructor
36596  * Creates a new TextArea
36597  * @param {Object} config Configuration options
36598  */
36599 Roo.form.TextArea = function(config){
36600     Roo.form.TextArea.superclass.constructor.call(this, config);
36601     // these are provided exchanges for backwards compat
36602     // minHeight/maxHeight were replaced by growMin/growMax to be
36603     // compatible with TextField growing config values
36604     if(this.minHeight !== undefined){
36605         this.growMin = this.minHeight;
36606     }
36607     if(this.maxHeight !== undefined){
36608         this.growMax = this.maxHeight;
36609     }
36610 };
36611
36612 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36613     /**
36614      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36615      */
36616     growMin : 60,
36617     /**
36618      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36619      */
36620     growMax: 1000,
36621     /**
36622      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36623      * in the field (equivalent to setting overflow: hidden, defaults to false)
36624      */
36625     preventScrollbars: false,
36626     /**
36627      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36628      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36629      */
36630
36631     // private
36632     onRender : function(ct, position){
36633         if(!this.el){
36634             this.defaultAutoCreate = {
36635                 tag: "textarea",
36636                 style:"width:300px;height:60px;",
36637                 autocomplete: "off"
36638             };
36639         }
36640         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36641         if(this.grow){
36642             this.textSizeEl = Roo.DomHelper.append(document.body, {
36643                 tag: "pre", cls: "x-form-grow-sizer"
36644             });
36645             if(this.preventScrollbars){
36646                 this.el.setStyle("overflow", "hidden");
36647             }
36648             this.el.setHeight(this.growMin);
36649         }
36650     },
36651
36652     onDestroy : function(){
36653         if(this.textSizeEl){
36654             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36655         }
36656         Roo.form.TextArea.superclass.onDestroy.call(this);
36657     },
36658
36659     // private
36660     onKeyUp : function(e){
36661         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36662             this.autoSize();
36663         }
36664     },
36665
36666     /**
36667      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36668      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36669      */
36670     autoSize : function(){
36671         if(!this.grow || !this.textSizeEl){
36672             return;
36673         }
36674         var el = this.el;
36675         var v = el.dom.value;
36676         var ts = this.textSizeEl;
36677
36678         ts.innerHTML = '';
36679         ts.appendChild(document.createTextNode(v));
36680         v = ts.innerHTML;
36681
36682         Roo.fly(ts).setWidth(this.el.getWidth());
36683         if(v.length < 1){
36684             v = "&#160;&#160;";
36685         }else{
36686             if(Roo.isIE){
36687                 v = v.replace(/\n/g, '<p>&#160;</p>');
36688             }
36689             v += "&#160;\n&#160;";
36690         }
36691         ts.innerHTML = v;
36692         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36693         if(h != this.lastHeight){
36694             this.lastHeight = h;
36695             this.el.setHeight(h);
36696             this.fireEvent("autosize", this, h);
36697         }
36698     }
36699 });/*
36700  * Based on:
36701  * Ext JS Library 1.1.1
36702  * Copyright(c) 2006-2007, Ext JS, LLC.
36703  *
36704  * Originally Released Under LGPL - original licence link has changed is not relivant.
36705  *
36706  * Fork - LGPL
36707  * <script type="text/javascript">
36708  */
36709  
36710
36711 /**
36712  * @class Roo.form.NumberField
36713  * @extends Roo.form.TextField
36714  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36715  * @constructor
36716  * Creates a new NumberField
36717  * @param {Object} config Configuration options
36718  */
36719 Roo.form.NumberField = function(config){
36720     Roo.form.NumberField.superclass.constructor.call(this, config);
36721 };
36722
36723 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36724     /**
36725      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36726      */
36727     fieldClass: "x-form-field x-form-num-field",
36728     /**
36729      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36730      */
36731     allowDecimals : true,
36732     /**
36733      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36734      */
36735     decimalSeparator : ".",
36736     /**
36737      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36738      */
36739     decimalPrecision : 2,
36740     /**
36741      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36742      */
36743     allowNegative : true,
36744     /**
36745      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36746      */
36747     minValue : Number.NEGATIVE_INFINITY,
36748     /**
36749      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36750      */
36751     maxValue : Number.MAX_VALUE,
36752     /**
36753      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36754      */
36755     minText : "The minimum value for this field is {0}",
36756     /**
36757      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36758      */
36759     maxText : "The maximum value for this field is {0}",
36760     /**
36761      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36762      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36763      */
36764     nanText : "{0} is not a valid number",
36765
36766     // private
36767     initEvents : function(){
36768         Roo.form.NumberField.superclass.initEvents.call(this);
36769         var allowed = "0123456789";
36770         if(this.allowDecimals){
36771             allowed += this.decimalSeparator;
36772         }
36773         if(this.allowNegative){
36774             allowed += "-";
36775         }
36776         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36777         var keyPress = function(e){
36778             var k = e.getKey();
36779             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36780                 return;
36781             }
36782             var c = e.getCharCode();
36783             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36784                 e.stopEvent();
36785             }
36786         };
36787         this.el.on("keypress", keyPress, this);
36788     },
36789
36790     // private
36791     validateValue : function(value){
36792         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36793             return false;
36794         }
36795         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36796              return true;
36797         }
36798         var num = this.parseValue(value);
36799         if(isNaN(num)){
36800             this.markInvalid(String.format(this.nanText, value));
36801             return false;
36802         }
36803         if(num < this.minValue){
36804             this.markInvalid(String.format(this.minText, this.minValue));
36805             return false;
36806         }
36807         if(num > this.maxValue){
36808             this.markInvalid(String.format(this.maxText, this.maxValue));
36809             return false;
36810         }
36811         return true;
36812     },
36813
36814     getValue : function(){
36815         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36816     },
36817
36818     // private
36819     parseValue : function(value){
36820         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36821         return isNaN(value) ? '' : value;
36822     },
36823
36824     // private
36825     fixPrecision : function(value){
36826         var nan = isNaN(value);
36827         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36828             return nan ? '' : value;
36829         }
36830         return parseFloat(value).toFixed(this.decimalPrecision);
36831     },
36832
36833     setValue : function(v){
36834         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36835     },
36836
36837     // private
36838     decimalPrecisionFcn : function(v){
36839         return Math.floor(v);
36840     },
36841
36842     beforeBlur : function(){
36843         var v = this.parseValue(this.getRawValue());
36844         if(v){
36845             this.setValue(this.fixPrecision(v));
36846         }
36847     }
36848 });/*
36849  * Based on:
36850  * Ext JS Library 1.1.1
36851  * Copyright(c) 2006-2007, Ext JS, LLC.
36852  *
36853  * Originally Released Under LGPL - original licence link has changed is not relivant.
36854  *
36855  * Fork - LGPL
36856  * <script type="text/javascript">
36857  */
36858  
36859 /**
36860  * @class Roo.form.DateField
36861  * @extends Roo.form.TriggerField
36862  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36863 * @constructor
36864 * Create a new DateField
36865 * @param {Object} config
36866  */
36867 Roo.form.DateField = function(config){
36868     Roo.form.DateField.superclass.constructor.call(this, config);
36869     
36870       this.addEvents({
36871          
36872         /**
36873          * @event select
36874          * Fires when a date is selected
36875              * @param {Roo.form.DateField} combo This combo box
36876              * @param {Date} date The date selected
36877              */
36878         'select' : true
36879          
36880     });
36881     
36882     
36883     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36884     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36885     this.ddMatch = null;
36886     if(this.disabledDates){
36887         var dd = this.disabledDates;
36888         var re = "(?:";
36889         for(var i = 0; i < dd.length; i++){
36890             re += dd[i];
36891             if(i != dd.length-1) re += "|";
36892         }
36893         this.ddMatch = new RegExp(re + ")");
36894     }
36895 };
36896
36897 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36898     /**
36899      * @cfg {String} format
36900      * The default date format string which can be overriden for localization support.  The format must be
36901      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36902      */
36903     format : "m/d/y",
36904     /**
36905      * @cfg {String} altFormats
36906      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36907      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36908      */
36909     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36910     /**
36911      * @cfg {Array} disabledDays
36912      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36913      */
36914     disabledDays : null,
36915     /**
36916      * @cfg {String} disabledDaysText
36917      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36918      */
36919     disabledDaysText : "Disabled",
36920     /**
36921      * @cfg {Array} disabledDates
36922      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36923      * expression so they are very powerful. Some examples:
36924      * <ul>
36925      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36926      * <li>["03/08", "09/16"] would disable those days for every year</li>
36927      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36928      * <li>["03/../2006"] would disable every day in March 2006</li>
36929      * <li>["^03"] would disable every day in every March</li>
36930      * </ul>
36931      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36932      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36933      */
36934     disabledDates : null,
36935     /**
36936      * @cfg {String} disabledDatesText
36937      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36938      */
36939     disabledDatesText : "Disabled",
36940     /**
36941      * @cfg {Date/String} minValue
36942      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36943      * valid format (defaults to null).
36944      */
36945     minValue : null,
36946     /**
36947      * @cfg {Date/String} maxValue
36948      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36949      * valid format (defaults to null).
36950      */
36951     maxValue : null,
36952     /**
36953      * @cfg {String} minText
36954      * The error text to display when the date in the cell is before minValue (defaults to
36955      * 'The date in this field must be after {minValue}').
36956      */
36957     minText : "The date in this field must be equal to or after {0}",
36958     /**
36959      * @cfg {String} maxText
36960      * The error text to display when the date in the cell is after maxValue (defaults to
36961      * 'The date in this field must be before {maxValue}').
36962      */
36963     maxText : "The date in this field must be equal to or before {0}",
36964     /**
36965      * @cfg {String} invalidText
36966      * The error text to display when the date in the field is invalid (defaults to
36967      * '{value} is not a valid date - it must be in the format {format}').
36968      */
36969     invalidText : "{0} is not a valid date - it must be in the format {1}",
36970     /**
36971      * @cfg {String} triggerClass
36972      * An additional CSS class used to style the trigger button.  The trigger will always get the
36973      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36974      * which displays a calendar icon).
36975      */
36976     triggerClass : 'x-form-date-trigger',
36977     
36978
36979     /**
36980      * @cfg {bool} useIso
36981      * if enabled, then the date field will use a hidden field to store the 
36982      * real value as iso formated date. default (false)
36983      */ 
36984     useIso : false,
36985     /**
36986      * @cfg {String/Object} autoCreate
36987      * A DomHelper element spec, or true for a default element spec (defaults to
36988      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36989      */ 
36990     // private
36991     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36992     
36993     // private
36994     hiddenField: false,
36995     
36996     onRender : function(ct, position)
36997     {
36998         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36999         if (this.useIso) {
37000             this.el.dom.removeAttribute('name'); 
37001             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37002                     'before', true);
37003             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37004             // prevent input submission
37005             this.hiddenName = this.name;
37006         }
37007             
37008             
37009     },
37010     
37011     // private
37012     validateValue : function(value)
37013     {
37014         value = this.formatDate(value);
37015         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37016             return false;
37017         }
37018         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37019              return true;
37020         }
37021         var svalue = value;
37022         value = this.parseDate(value);
37023         if(!value){
37024             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37025             return false;
37026         }
37027         var time = value.getTime();
37028         if(this.minValue && time < this.minValue.getTime()){
37029             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37030             return false;
37031         }
37032         if(this.maxValue && time > this.maxValue.getTime()){
37033             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37034             return false;
37035         }
37036         if(this.disabledDays){
37037             var day = value.getDay();
37038             for(var i = 0; i < this.disabledDays.length; i++) {
37039                 if(day === this.disabledDays[i]){
37040                     this.markInvalid(this.disabledDaysText);
37041                     return false;
37042                 }
37043             }
37044         }
37045         var fvalue = this.formatDate(value);
37046         if(this.ddMatch && this.ddMatch.test(fvalue)){
37047             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37048             return false;
37049         }
37050         return true;
37051     },
37052
37053     // private
37054     // Provides logic to override the default TriggerField.validateBlur which just returns true
37055     validateBlur : function(){
37056         return !this.menu || !this.menu.isVisible();
37057     },
37058
37059     /**
37060      * Returns the current date value of the date field.
37061      * @return {Date} The date value
37062      */
37063     getValue : function(){
37064         
37065         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37066     },
37067
37068     /**
37069      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37070      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37071      * (the default format used is "m/d/y").
37072      * <br />Usage:
37073      * <pre><code>
37074 //All of these calls set the same date value (May 4, 2006)
37075
37076 //Pass a date object:
37077 var dt = new Date('5/4/06');
37078 dateField.setValue(dt);
37079
37080 //Pass a date string (default format):
37081 dateField.setValue('5/4/06');
37082
37083 //Pass a date string (custom format):
37084 dateField.format = 'Y-m-d';
37085 dateField.setValue('2006-5-4');
37086 </code></pre>
37087      * @param {String/Date} date The date or valid date string
37088      */
37089     setValue : function(date){
37090         if (this.hiddenField) {
37091             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37092         }
37093         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37094     },
37095
37096     // private
37097     parseDate : function(value){
37098         if(!value || value instanceof Date){
37099             return value;
37100         }
37101         var v = Date.parseDate(value, this.format);
37102         if(!v && this.altFormats){
37103             if(!this.altFormatsArray){
37104                 this.altFormatsArray = this.altFormats.split("|");
37105             }
37106             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37107                 v = Date.parseDate(value, this.altFormatsArray[i]);
37108             }
37109         }
37110         return v;
37111     },
37112
37113     // private
37114     formatDate : function(date, fmt){
37115         return (!date || !(date instanceof Date)) ?
37116                date : date.dateFormat(fmt || this.format);
37117     },
37118
37119     // private
37120     menuListeners : {
37121         select: function(m, d){
37122             this.setValue(d);
37123             this.fireEvent('select', this, d);
37124         },
37125         show : function(){ // retain focus styling
37126             this.onFocus();
37127         },
37128         hide : function(){
37129             this.focus.defer(10, this);
37130             var ml = this.menuListeners;
37131             this.menu.un("select", ml.select,  this);
37132             this.menu.un("show", ml.show,  this);
37133             this.menu.un("hide", ml.hide,  this);
37134         }
37135     },
37136
37137     // private
37138     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37139     onTriggerClick : function(){
37140         if(this.disabled){
37141             return;
37142         }
37143         if(this.menu == null){
37144             this.menu = new Roo.menu.DateMenu();
37145         }
37146         Roo.apply(this.menu.picker,  {
37147             showClear: this.allowBlank,
37148             minDate : this.minValue,
37149             maxDate : this.maxValue,
37150             disabledDatesRE : this.ddMatch,
37151             disabledDatesText : this.disabledDatesText,
37152             disabledDays : this.disabledDays,
37153             disabledDaysText : this.disabledDaysText,
37154             format : this.format,
37155             minText : String.format(this.minText, this.formatDate(this.minValue)),
37156             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37157         });
37158         this.menu.on(Roo.apply({}, this.menuListeners, {
37159             scope:this
37160         }));
37161         this.menu.picker.setValue(this.getValue() || new Date());
37162         this.menu.show(this.el, "tl-bl?");
37163     },
37164
37165     beforeBlur : function(){
37166         var v = this.parseDate(this.getRawValue());
37167         if(v){
37168             this.setValue(v);
37169         }
37170     }
37171
37172     /** @cfg {Boolean} grow @hide */
37173     /** @cfg {Number} growMin @hide */
37174     /** @cfg {Number} growMax @hide */
37175     /**
37176      * @hide
37177      * @method autoSize
37178      */
37179 });/*
37180  * Based on:
37181  * Ext JS Library 1.1.1
37182  * Copyright(c) 2006-2007, Ext JS, LLC.
37183  *
37184  * Originally Released Under LGPL - original licence link has changed is not relivant.
37185  *
37186  * Fork - LGPL
37187  * <script type="text/javascript">
37188  */
37189  
37190
37191 /**
37192  * @class Roo.form.ComboBox
37193  * @extends Roo.form.TriggerField
37194  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37195  * @constructor
37196  * Create a new ComboBox.
37197  * @param {Object} config Configuration options
37198  */
37199 Roo.form.ComboBox = function(config){
37200     Roo.form.ComboBox.superclass.constructor.call(this, config);
37201     this.addEvents({
37202         /**
37203          * @event expand
37204          * Fires when the dropdown list is expanded
37205              * @param {Roo.form.ComboBox} combo This combo box
37206              */
37207         'expand' : true,
37208         /**
37209          * @event collapse
37210          * Fires when the dropdown list is collapsed
37211              * @param {Roo.form.ComboBox} combo This combo box
37212              */
37213         'collapse' : true,
37214         /**
37215          * @event beforeselect
37216          * Fires before a list item is selected. Return false to cancel the selection.
37217              * @param {Roo.form.ComboBox} combo This combo box
37218              * @param {Roo.data.Record} record The data record returned from the underlying store
37219              * @param {Number} index The index of the selected item in the dropdown list
37220              */
37221         'beforeselect' : true,
37222         /**
37223          * @event select
37224          * Fires when a list item is selected
37225              * @param {Roo.form.ComboBox} combo This combo box
37226              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37227              * @param {Number} index The index of the selected item in the dropdown list
37228              */
37229         'select' : true,
37230         /**
37231          * @event beforequery
37232          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37233          * The event object passed has these properties:
37234              * @param {Roo.form.ComboBox} combo This combo box
37235              * @param {String} query The query
37236              * @param {Boolean} forceAll true to force "all" query
37237              * @param {Boolean} cancel true to cancel the query
37238              * @param {Object} e The query event object
37239              */
37240         'beforequery': true,
37241          /**
37242          * @event add
37243          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37244              * @param {Roo.form.ComboBox} combo This combo box
37245              */
37246         'add' : true,
37247         /**
37248          * @event edit
37249          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37250              * @param {Roo.form.ComboBox} combo This combo box
37251              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37252              */
37253         'edit' : true
37254         
37255         
37256     });
37257     if(this.transform){
37258         this.allowDomMove = false;
37259         var s = Roo.getDom(this.transform);
37260         if(!this.hiddenName){
37261             this.hiddenName = s.name;
37262         }
37263         if(!this.store){
37264             this.mode = 'local';
37265             var d = [], opts = s.options;
37266             for(var i = 0, len = opts.length;i < len; i++){
37267                 var o = opts[i];
37268                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37269                 if(o.selected) {
37270                     this.value = value;
37271                 }
37272                 d.push([value, o.text]);
37273             }
37274             this.store = new Roo.data.SimpleStore({
37275                 'id': 0,
37276                 fields: ['value', 'text'],
37277                 data : d
37278             });
37279             this.valueField = 'value';
37280             this.displayField = 'text';
37281         }
37282         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37283         if(!this.lazyRender){
37284             this.target = true;
37285             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37286             s.parentNode.removeChild(s); // remove it
37287             this.render(this.el.parentNode);
37288         }else{
37289             s.parentNode.removeChild(s); // remove it
37290         }
37291
37292     }
37293     if (this.store) {
37294         this.store = Roo.factory(this.store, Roo.data);
37295     }
37296     
37297     this.selectedIndex = -1;
37298     if(this.mode == 'local'){
37299         if(config.queryDelay === undefined){
37300             this.queryDelay = 10;
37301         }
37302         if(config.minChars === undefined){
37303             this.minChars = 0;
37304         }
37305     }
37306 };
37307
37308 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37309     /**
37310      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37311      */
37312     /**
37313      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37314      * rendering into an Roo.Editor, defaults to false)
37315      */
37316     /**
37317      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37318      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37319      */
37320     /**
37321      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37322      */
37323     /**
37324      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37325      * the dropdown list (defaults to undefined, with no header element)
37326      */
37327
37328      /**
37329      * @cfg {String/Roo.Template} tpl The template to use to render the output
37330      */
37331      
37332     // private
37333     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37334     /**
37335      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37336      */
37337     listWidth: undefined,
37338     /**
37339      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37340      * mode = 'remote' or 'text' if mode = 'local')
37341      */
37342     displayField: undefined,
37343     /**
37344      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37345      * mode = 'remote' or 'value' if mode = 'local'). 
37346      * Note: use of a valueField requires the user make a selection
37347      * in order for a value to be mapped.
37348      */
37349     valueField: undefined,
37350     /**
37351      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37352      * field's data value (defaults to the underlying DOM element's name)
37353      */
37354     hiddenName: undefined,
37355     /**
37356      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37357      */
37358     listClass: '',
37359     /**
37360      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37361      */
37362     selectedClass: 'x-combo-selected',
37363     /**
37364      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37365      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37366      * which displays a downward arrow icon).
37367      */
37368     triggerClass : 'x-form-arrow-trigger',
37369     /**
37370      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37371      */
37372     shadow:'sides',
37373     /**
37374      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37375      * anchor positions (defaults to 'tl-bl')
37376      */
37377     listAlign: 'tl-bl?',
37378     /**
37379      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37380      */
37381     maxHeight: 300,
37382     /**
37383      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37384      * query specified by the allQuery config option (defaults to 'query')
37385      */
37386     triggerAction: 'query',
37387     /**
37388      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37389      * (defaults to 4, does not apply if editable = false)
37390      */
37391     minChars : 4,
37392     /**
37393      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37394      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37395      */
37396     typeAhead: false,
37397     /**
37398      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37399      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37400      */
37401     queryDelay: 500,
37402     /**
37403      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37404      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37405      */
37406     pageSize: 0,
37407     /**
37408      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37409      * when editable = true (defaults to false)
37410      */
37411     selectOnFocus:false,
37412     /**
37413      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37414      */
37415     queryParam: 'query',
37416     /**
37417      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37418      * when mode = 'remote' (defaults to 'Loading...')
37419      */
37420     loadingText: 'Loading...',
37421     /**
37422      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37423      */
37424     resizable: false,
37425     /**
37426      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37427      */
37428     handleHeight : 8,
37429     /**
37430      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37431      * traditional select (defaults to true)
37432      */
37433     editable: true,
37434     /**
37435      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37436      */
37437     allQuery: '',
37438     /**
37439      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37440      */
37441     mode: 'remote',
37442     /**
37443      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37444      * listWidth has a higher value)
37445      */
37446     minListWidth : 70,
37447     /**
37448      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37449      * allow the user to set arbitrary text into the field (defaults to false)
37450      */
37451     forceSelection:false,
37452     /**
37453      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37454      * if typeAhead = true (defaults to 250)
37455      */
37456     typeAheadDelay : 250,
37457     /**
37458      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37459      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37460      */
37461     valueNotFoundText : undefined,
37462     /**
37463      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37464      */
37465     blockFocus : false,
37466     
37467     /**
37468      * @cfg {Boolean} disableClear Disable showing of clear button.
37469      */
37470     disableClear : false,
37471     /**
37472      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37473      */
37474     alwaysQuery : false,
37475     
37476     //private
37477     addicon : false,
37478     editicon: false,
37479     
37480     
37481     // private
37482     onRender : function(ct, position){
37483         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37484         if(this.hiddenName){
37485             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37486                     'before', true);
37487             this.hiddenField.value =
37488                 this.hiddenValue !== undefined ? this.hiddenValue :
37489                 this.value !== undefined ? this.value : '';
37490
37491             // prevent input submission
37492             this.el.dom.removeAttribute('name');
37493         }
37494         if(Roo.isGecko){
37495             this.el.dom.setAttribute('autocomplete', 'off');
37496         }
37497
37498         var cls = 'x-combo-list';
37499
37500         this.list = new Roo.Layer({
37501             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37502         });
37503
37504         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37505         this.list.setWidth(lw);
37506         this.list.swallowEvent('mousewheel');
37507         this.assetHeight = 0;
37508
37509         if(this.title){
37510             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37511             this.assetHeight += this.header.getHeight();
37512         }
37513
37514         this.innerList = this.list.createChild({cls:cls+'-inner'});
37515         this.innerList.on('mouseover', this.onViewOver, this);
37516         this.innerList.on('mousemove', this.onViewMove, this);
37517         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37518         
37519         if(this.allowBlank && !this.pageSize && !this.disableClear){
37520             this.footer = this.list.createChild({cls:cls+'-ft'});
37521             this.pageTb = new Roo.Toolbar(this.footer);
37522            
37523         }
37524         if(this.pageSize){
37525             this.footer = this.list.createChild({cls:cls+'-ft'});
37526             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37527                     {pageSize: this.pageSize});
37528             
37529         }
37530         
37531         if (this.pageTb && this.allowBlank && !this.disableClear) {
37532             var _this = this;
37533             this.pageTb.add(new Roo.Toolbar.Fill(), {
37534                 cls: 'x-btn-icon x-btn-clear',
37535                 text: '&#160;',
37536                 handler: function()
37537                 {
37538                     _this.collapse();
37539                     _this.clearValue();
37540                     _this.onSelect(false, -1);
37541                 }
37542             });
37543         }
37544         if (this.footer) {
37545             this.assetHeight += this.footer.getHeight();
37546         }
37547         
37548
37549         if(!this.tpl){
37550             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37551         }
37552
37553         this.view = new Roo.View(this.innerList, this.tpl, {
37554             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37555         });
37556
37557         this.view.on('click', this.onViewClick, this);
37558
37559         this.store.on('beforeload', this.onBeforeLoad, this);
37560         this.store.on('load', this.onLoad, this);
37561         this.store.on('loadexception', this.collapse, this);
37562
37563         if(this.resizable){
37564             this.resizer = new Roo.Resizable(this.list,  {
37565                pinned:true, handles:'se'
37566             });
37567             this.resizer.on('resize', function(r, w, h){
37568                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37569                 this.listWidth = w;
37570                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37571                 this.restrictHeight();
37572             }, this);
37573             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37574         }
37575         if(!this.editable){
37576             this.editable = true;
37577             this.setEditable(false);
37578         }  
37579         
37580         
37581         if (typeof(this.events.add.listeners) != 'undefined') {
37582             
37583             this.addicon = this.wrap.createChild(
37584                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37585        
37586             this.addicon.on('click', function(e) {
37587                 this.fireEvent('add', this);
37588             }, this);
37589         }
37590         if (typeof(this.events.edit.listeners) != 'undefined') {
37591             
37592             this.editicon = this.wrap.createChild(
37593                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37594             if (this.addicon) {
37595                 this.editicon.setStyle('margin-left', '40px');
37596             }
37597             this.editicon.on('click', function(e) {
37598                 
37599                 // we fire even  if inothing is selected..
37600                 this.fireEvent('edit', this, this.lastData );
37601                 
37602             }, this);
37603         }
37604         
37605         
37606         
37607     },
37608
37609     // private
37610     initEvents : function(){
37611         Roo.form.ComboBox.superclass.initEvents.call(this);
37612
37613         this.keyNav = new Roo.KeyNav(this.el, {
37614             "up" : function(e){
37615                 this.inKeyMode = true;
37616                 this.selectPrev();
37617             },
37618
37619             "down" : function(e){
37620                 if(!this.isExpanded()){
37621                     this.onTriggerClick();
37622                 }else{
37623                     this.inKeyMode = true;
37624                     this.selectNext();
37625                 }
37626             },
37627
37628             "enter" : function(e){
37629                 this.onViewClick();
37630                 //return true;
37631             },
37632
37633             "esc" : function(e){
37634                 this.collapse();
37635             },
37636
37637             "tab" : function(e){
37638                 this.onViewClick(false);
37639                 this.fireEvent("specialkey", this, e);
37640                 return true;
37641             },
37642
37643             scope : this,
37644
37645             doRelay : function(foo, bar, hname){
37646                 if(hname == 'down' || this.scope.isExpanded()){
37647                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37648                 }
37649                 return true;
37650             },
37651
37652             forceKeyDown: true
37653         });
37654         this.queryDelay = Math.max(this.queryDelay || 10,
37655                 this.mode == 'local' ? 10 : 250);
37656         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37657         if(this.typeAhead){
37658             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37659         }
37660         if(this.editable !== false){
37661             this.el.on("keyup", this.onKeyUp, this);
37662         }
37663         if(this.forceSelection){
37664             this.on('blur', this.doForce, this);
37665         }
37666     },
37667
37668     onDestroy : function(){
37669         if(this.view){
37670             this.view.setStore(null);
37671             this.view.el.removeAllListeners();
37672             this.view.el.remove();
37673             this.view.purgeListeners();
37674         }
37675         if(this.list){
37676             this.list.destroy();
37677         }
37678         if(this.store){
37679             this.store.un('beforeload', this.onBeforeLoad, this);
37680             this.store.un('load', this.onLoad, this);
37681             this.store.un('loadexception', this.collapse, this);
37682         }
37683         Roo.form.ComboBox.superclass.onDestroy.call(this);
37684     },
37685
37686     // private
37687     fireKey : function(e){
37688         if(e.isNavKeyPress() && !this.list.isVisible()){
37689             this.fireEvent("specialkey", this, e);
37690         }
37691     },
37692
37693     // private
37694     onResize: function(w, h){
37695         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37696         
37697         if(typeof w != 'number'){
37698             // we do not handle it!?!?
37699             return;
37700         }
37701         var tw = this.trigger.getWidth();
37702         tw += this.addicon ? this.addicon.getWidth() : 0;
37703         tw += this.editicon ? this.editicon.getWidth() : 0;
37704         var x = w - tw;
37705         this.el.setWidth( this.adjustWidth('input', x));
37706             
37707         this.trigger.setStyle('left', x+'px');
37708         
37709         if(this.list && this.listWidth === undefined){
37710             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37711             this.list.setWidth(lw);
37712             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37713         }
37714         
37715     
37716         
37717     },
37718
37719     /**
37720      * Allow or prevent the user from directly editing the field text.  If false is passed,
37721      * the user will only be able to select from the items defined in the dropdown list.  This method
37722      * is the runtime equivalent of setting the 'editable' config option at config time.
37723      * @param {Boolean} value True to allow the user to directly edit the field text
37724      */
37725     setEditable : function(value){
37726         if(value == this.editable){
37727             return;
37728         }
37729         this.editable = value;
37730         if(!value){
37731             this.el.dom.setAttribute('readOnly', true);
37732             this.el.on('mousedown', this.onTriggerClick,  this);
37733             this.el.addClass('x-combo-noedit');
37734         }else{
37735             this.el.dom.setAttribute('readOnly', false);
37736             this.el.un('mousedown', this.onTriggerClick,  this);
37737             this.el.removeClass('x-combo-noedit');
37738         }
37739     },
37740
37741     // private
37742     onBeforeLoad : function(){
37743         if(!this.hasFocus){
37744             return;
37745         }
37746         this.innerList.update(this.loadingText ?
37747                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37748         this.restrictHeight();
37749         this.selectedIndex = -1;
37750     },
37751
37752     // private
37753     onLoad : function(){
37754         if(!this.hasFocus){
37755             return;
37756         }
37757         if(this.store.getCount() > 0){
37758             this.expand();
37759             this.restrictHeight();
37760             if(this.lastQuery == this.allQuery){
37761                 if(this.editable){
37762                     this.el.dom.select();
37763                 }
37764                 if(!this.selectByValue(this.value, true)){
37765                     this.select(0, true);
37766                 }
37767             }else{
37768                 this.selectNext();
37769                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37770                     this.taTask.delay(this.typeAheadDelay);
37771                 }
37772             }
37773         }else{
37774             this.onEmptyResults();
37775         }
37776         //this.el.focus();
37777     },
37778
37779     // private
37780     onTypeAhead : function(){
37781         if(this.store.getCount() > 0){
37782             var r = this.store.getAt(0);
37783             var newValue = r.data[this.displayField];
37784             var len = newValue.length;
37785             var selStart = this.getRawValue().length;
37786             if(selStart != len){
37787                 this.setRawValue(newValue);
37788                 this.selectText(selStart, newValue.length);
37789             }
37790         }
37791     },
37792
37793     // private
37794     onSelect : function(record, index){
37795         if(this.fireEvent('beforeselect', this, record, index) !== false){
37796             this.setFromData(index > -1 ? record.data : false);
37797             this.collapse();
37798             this.fireEvent('select', this, record, index);
37799         }
37800     },
37801
37802     /**
37803      * Returns the currently selected field value or empty string if no value is set.
37804      * @return {String} value The selected value
37805      */
37806     getValue : function(){
37807         if(this.valueField){
37808             return typeof this.value != 'undefined' ? this.value : '';
37809         }else{
37810             return Roo.form.ComboBox.superclass.getValue.call(this);
37811         }
37812     },
37813
37814     /**
37815      * Clears any text/value currently set in the field
37816      */
37817     clearValue : function(){
37818         if(this.hiddenField){
37819             this.hiddenField.value = '';
37820         }
37821         this.value = '';
37822         this.setRawValue('');
37823         this.lastSelectionText = '';
37824         this.applyEmptyText();
37825     },
37826
37827     /**
37828      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37829      * will be displayed in the field.  If the value does not match the data value of an existing item,
37830      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37831      * Otherwise the field will be blank (although the value will still be set).
37832      * @param {String} value The value to match
37833      */
37834     setValue : function(v){
37835         var text = v;
37836         if(this.valueField){
37837             var r = this.findRecord(this.valueField, v);
37838             if(r){
37839                 text = r.data[this.displayField];
37840             }else if(this.valueNotFoundText !== undefined){
37841                 text = this.valueNotFoundText;
37842             }
37843         }
37844         this.lastSelectionText = text;
37845         if(this.hiddenField){
37846             this.hiddenField.value = v;
37847         }
37848         Roo.form.ComboBox.superclass.setValue.call(this, text);
37849         this.value = v;
37850     },
37851     /**
37852      * @property {Object} the last set data for the element
37853      */
37854     
37855     lastData : false,
37856     /**
37857      * Sets the value of the field based on a object which is related to the record format for the store.
37858      * @param {Object} value the value to set as. or false on reset?
37859      */
37860     setFromData : function(o){
37861         var dv = ''; // display value
37862         var vv = ''; // value value..
37863         this.lastData = o;
37864         if (this.displayField) {
37865             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37866         } else {
37867             // this is an error condition!!!
37868             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37869         }
37870         
37871         if(this.valueField){
37872             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37873         }
37874         if(this.hiddenField){
37875             this.hiddenField.value = vv;
37876             
37877             this.lastSelectionText = dv;
37878             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37879             this.value = vv;
37880             return;
37881         }
37882         // no hidden field.. - we store the value in 'value', but still display
37883         // display field!!!!
37884         this.lastSelectionText = dv;
37885         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37886         this.value = vv;
37887         
37888         
37889     },
37890     // private
37891     reset : function(){
37892         // overridden so that last data is reset..
37893         this.setValue(this.originalValue);
37894         this.clearInvalid();
37895         this.lastData = false;
37896     },
37897     // private
37898     findRecord : function(prop, value){
37899         var record;
37900         if(this.store.getCount() > 0){
37901             this.store.each(function(r){
37902                 if(r.data[prop] == value){
37903                     record = r;
37904                     return false;
37905                 }
37906             });
37907         }
37908         return record;
37909     },
37910
37911     // private
37912     onViewMove : function(e, t){
37913         this.inKeyMode = false;
37914     },
37915
37916     // private
37917     onViewOver : function(e, t){
37918         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37919             return;
37920         }
37921         var item = this.view.findItemFromChild(t);
37922         if(item){
37923             var index = this.view.indexOf(item);
37924             this.select(index, false);
37925         }
37926     },
37927
37928     // private
37929     onViewClick : function(doFocus)
37930     {
37931         var index = this.view.getSelectedIndexes()[0];
37932         var r = this.store.getAt(index);
37933         if(r){
37934             this.onSelect(r, index);
37935         }
37936         if(doFocus !== false && !this.blockFocus){
37937             this.el.focus();
37938         }
37939     },
37940
37941     // private
37942     restrictHeight : function(){
37943         this.innerList.dom.style.height = '';
37944         var inner = this.innerList.dom;
37945         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37946         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37947         this.list.beginUpdate();
37948         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37949         this.list.alignTo(this.el, this.listAlign);
37950         this.list.endUpdate();
37951     },
37952
37953     // private
37954     onEmptyResults : function(){
37955         this.collapse();
37956     },
37957
37958     /**
37959      * Returns true if the dropdown list is expanded, else false.
37960      */
37961     isExpanded : function(){
37962         return this.list.isVisible();
37963     },
37964
37965     /**
37966      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37967      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37968      * @param {String} value The data value of the item to select
37969      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37970      * selected item if it is not currently in view (defaults to true)
37971      * @return {Boolean} True if the value matched an item in the list, else false
37972      */
37973     selectByValue : function(v, scrollIntoView){
37974         if(v !== undefined && v !== null){
37975             var r = this.findRecord(this.valueField || this.displayField, v);
37976             if(r){
37977                 this.select(this.store.indexOf(r), scrollIntoView);
37978                 return true;
37979             }
37980         }
37981         return false;
37982     },
37983
37984     /**
37985      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37986      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37987      * @param {Number} index The zero-based index of the list item to select
37988      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37989      * selected item if it is not currently in view (defaults to true)
37990      */
37991     select : function(index, scrollIntoView){
37992         this.selectedIndex = index;
37993         this.view.select(index);
37994         if(scrollIntoView !== false){
37995             var el = this.view.getNode(index);
37996             if(el){
37997                 this.innerList.scrollChildIntoView(el, false);
37998             }
37999         }
38000     },
38001
38002     // private
38003     selectNext : function(){
38004         var ct = this.store.getCount();
38005         if(ct > 0){
38006             if(this.selectedIndex == -1){
38007                 this.select(0);
38008             }else if(this.selectedIndex < ct-1){
38009                 this.select(this.selectedIndex+1);
38010             }
38011         }
38012     },
38013
38014     // private
38015     selectPrev : function(){
38016         var ct = this.store.getCount();
38017         if(ct > 0){
38018             if(this.selectedIndex == -1){
38019                 this.select(0);
38020             }else if(this.selectedIndex != 0){
38021                 this.select(this.selectedIndex-1);
38022             }
38023         }
38024     },
38025
38026     // private
38027     onKeyUp : function(e){
38028         if(this.editable !== false && !e.isSpecialKey()){
38029             this.lastKey = e.getKey();
38030             this.dqTask.delay(this.queryDelay);
38031         }
38032     },
38033
38034     // private
38035     validateBlur : function(){
38036         return !this.list || !this.list.isVisible();   
38037     },
38038
38039     // private
38040     initQuery : function(){
38041         this.doQuery(this.getRawValue());
38042     },
38043
38044     // private
38045     doForce : function(){
38046         if(this.el.dom.value.length > 0){
38047             this.el.dom.value =
38048                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38049             this.applyEmptyText();
38050         }
38051     },
38052
38053     /**
38054      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38055      * query allowing the query action to be canceled if needed.
38056      * @param {String} query The SQL query to execute
38057      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38058      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38059      * saved in the current store (defaults to false)
38060      */
38061     doQuery : function(q, forceAll){
38062         if(q === undefined || q === null){
38063             q = '';
38064         }
38065         var qe = {
38066             query: q,
38067             forceAll: forceAll,
38068             combo: this,
38069             cancel:false
38070         };
38071         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38072             return false;
38073         }
38074         q = qe.query;
38075         forceAll = qe.forceAll;
38076         if(forceAll === true || (q.length >= this.minChars)){
38077             if(this.lastQuery != q || this.alwaysQuery){
38078                 this.lastQuery = q;
38079                 if(this.mode == 'local'){
38080                     this.selectedIndex = -1;
38081                     if(forceAll){
38082                         this.store.clearFilter();
38083                     }else{
38084                         this.store.filter(this.displayField, q);
38085                     }
38086                     this.onLoad();
38087                 }else{
38088                     this.store.baseParams[this.queryParam] = q;
38089                     this.store.load({
38090                         params: this.getParams(q)
38091                     });
38092                     this.expand();
38093                 }
38094             }else{
38095                 this.selectedIndex = -1;
38096                 this.onLoad();   
38097             }
38098         }
38099     },
38100
38101     // private
38102     getParams : function(q){
38103         var p = {};
38104         //p[this.queryParam] = q;
38105         if(this.pageSize){
38106             p.start = 0;
38107             p.limit = this.pageSize;
38108         }
38109         return p;
38110     },
38111
38112     /**
38113      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38114      */
38115     collapse : function(){
38116         if(!this.isExpanded()){
38117             return;
38118         }
38119         this.list.hide();
38120         Roo.get(document).un('mousedown', this.collapseIf, this);
38121         Roo.get(document).un('mousewheel', this.collapseIf, this);
38122         if (!this.editable) {
38123             Roo.get(document).un('keydown', this.listKeyPress, this);
38124         }
38125         this.fireEvent('collapse', this);
38126     },
38127
38128     // private
38129     collapseIf : function(e){
38130         if(!e.within(this.wrap) && !e.within(this.list)){
38131             this.collapse();
38132         }
38133     },
38134
38135     /**
38136      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38137      */
38138     expand : function(){
38139         if(this.isExpanded() || !this.hasFocus){
38140             return;
38141         }
38142         this.list.alignTo(this.el, this.listAlign);
38143         this.list.show();
38144         Roo.get(document).on('mousedown', this.collapseIf, this);
38145         Roo.get(document).on('mousewheel', this.collapseIf, this);
38146         if (!this.editable) {
38147             Roo.get(document).on('keydown', this.listKeyPress, this);
38148         }
38149         
38150         this.fireEvent('expand', this);
38151     },
38152
38153     // private
38154     // Implements the default empty TriggerField.onTriggerClick function
38155     onTriggerClick : function(){
38156         if(this.disabled){
38157             return;
38158         }
38159         if(this.isExpanded()){
38160             this.collapse();
38161             if (!this.blockFocus) {
38162                 this.el.focus();
38163             }
38164             
38165         }else {
38166             this.hasFocus = true;
38167             if(this.triggerAction == 'all') {
38168                 this.doQuery(this.allQuery, true);
38169             } else {
38170                 this.doQuery(this.getRawValue());
38171             }
38172             if (!this.blockFocus) {
38173                 this.el.focus();
38174             }
38175         }
38176     },
38177     listKeyPress : function(e)
38178     {
38179         //Roo.log('listkeypress');
38180         // scroll to first matching element based on key pres..
38181         if (e.isSpecialKey()) {
38182             return false;
38183         }
38184         var k = String.fromCharCode(e.getKey()).toUpperCase();
38185         //Roo.log(k);
38186         var match  = false;
38187         var csel = this.view.getSelectedNodes();
38188         var cselitem = false;
38189         if (csel.length) {
38190             var ix = this.view.indexOf(csel[0]);
38191             cselitem  = this.store.getAt(ix);
38192             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38193                 cselitem = false;
38194             }
38195             
38196         }
38197         
38198         this.store.each(function(v) { 
38199             if (cselitem) {
38200                 // start at existing selection.
38201                 if (cselitem.id == v.id) {
38202                     cselitem = false;
38203                 }
38204                 return;
38205             }
38206                 
38207             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38208                 match = this.store.indexOf(v);
38209                 return false;
38210             }
38211         }, this);
38212         
38213         if (match === false) {
38214             return true; // no more action?
38215         }
38216         // scroll to?
38217         this.view.select(match);
38218         var sn = Roo.get(this.view.getSelectedNodes()[0])
38219         sn.scrollIntoView(sn.dom.parentNode, false);
38220     }
38221
38222     /** 
38223     * @cfg {Boolean} grow 
38224     * @hide 
38225     */
38226     /** 
38227     * @cfg {Number} growMin 
38228     * @hide 
38229     */
38230     /** 
38231     * @cfg {Number} growMax 
38232     * @hide 
38233     */
38234     /**
38235      * @hide
38236      * @method autoSize
38237      */
38238 });/*
38239  * Based on:
38240  * Ext JS Library 1.1.1
38241  * Copyright(c) 2006-2007, Ext JS, LLC.
38242  *
38243  * Originally Released Under LGPL - original licence link has changed is not relivant.
38244  *
38245  * Fork - LGPL
38246  * <script type="text/javascript">
38247  */
38248 /**
38249  * @class Roo.form.Checkbox
38250  * @extends Roo.form.Field
38251  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38252  * @constructor
38253  * Creates a new Checkbox
38254  * @param {Object} config Configuration options
38255  */
38256 Roo.form.Checkbox = function(config){
38257     Roo.form.Checkbox.superclass.constructor.call(this, config);
38258     this.addEvents({
38259         /**
38260          * @event check
38261          * Fires when the checkbox is checked or unchecked.
38262              * @param {Roo.form.Checkbox} this This checkbox
38263              * @param {Boolean} checked The new checked value
38264              */
38265         check : true
38266     });
38267 };
38268
38269 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38270     /**
38271      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38272      */
38273     focusClass : undefined,
38274     /**
38275      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38276      */
38277     fieldClass: "x-form-field",
38278     /**
38279      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38280      */
38281     checked: false,
38282     /**
38283      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38284      * {tag: "input", type: "checkbox", autocomplete: "off"})
38285      */
38286     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38287     /**
38288      * @cfg {String} boxLabel The text that appears beside the checkbox
38289      */
38290     boxLabel : "",
38291     /**
38292      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38293      */  
38294     inputValue : '1',
38295     /**
38296      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38297      */
38298      valueOff: '0', // value when not checked..
38299
38300     actionMode : 'viewEl', 
38301     //
38302     // private
38303     itemCls : 'x-menu-check-item x-form-item',
38304     groupClass : 'x-menu-group-item',
38305     inputType : 'hidden',
38306     
38307     
38308     inSetChecked: false, // check that we are not calling self...
38309     
38310     inputElement: false, // real input element?
38311     basedOn: false, // ????
38312     
38313     isFormField: true, // not sure where this is needed!!!!
38314
38315     onResize : function(){
38316         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38317         if(!this.boxLabel){
38318             this.el.alignTo(this.wrap, 'c-c');
38319         }
38320     },
38321
38322     initEvents : function(){
38323         Roo.form.Checkbox.superclass.initEvents.call(this);
38324         this.el.on("click", this.onClick,  this);
38325         this.el.on("change", this.onClick,  this);
38326     },
38327
38328
38329     getResizeEl : function(){
38330         return this.wrap;
38331     },
38332
38333     getPositionEl : function(){
38334         return this.wrap;
38335     },
38336
38337     // private
38338     onRender : function(ct, position){
38339         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38340         /*
38341         if(this.inputValue !== undefined){
38342             this.el.dom.value = this.inputValue;
38343         }
38344         */
38345         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38346         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38347         var viewEl = this.wrap.createChild({ 
38348             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38349         this.viewEl = viewEl;   
38350         this.wrap.on('click', this.onClick,  this); 
38351         
38352         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38353         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38354         
38355         
38356         
38357         if(this.boxLabel){
38358             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38359         //    viewEl.on('click', this.onClick,  this); 
38360         }
38361         //if(this.checked){
38362             this.setChecked(this.checked);
38363         //}else{
38364             //this.checked = this.el.dom;
38365         //}
38366
38367     },
38368
38369     // private
38370     initValue : Roo.emptyFn,
38371
38372     /**
38373      * Returns the checked state of the checkbox.
38374      * @return {Boolean} True if checked, else false
38375      */
38376     getValue : function(){
38377         if(this.el){
38378             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38379         }
38380         return this.valueOff;
38381         
38382     },
38383
38384         // private
38385     onClick : function(){ 
38386         this.setChecked(!this.checked);
38387
38388         //if(this.el.dom.checked != this.checked){
38389         //    this.setValue(this.el.dom.checked);
38390        // }
38391     },
38392
38393     /**
38394      * Sets the checked state of the checkbox.
38395      * On is always based on a string comparison between inputValue and the param.
38396      * @param {Boolean/String} value - the value to set 
38397      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38398      */
38399     setValue : function(v,suppressEvent){
38400         
38401         
38402         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38403         //if(this.el && this.el.dom){
38404         //    this.el.dom.checked = this.checked;
38405         //    this.el.dom.defaultChecked = this.checked;
38406         //}
38407         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38408         //this.fireEvent("check", this, this.checked);
38409     },
38410     // private..
38411     setChecked : function(state,suppressEvent)
38412     {
38413         if (this.inSetChecked) {
38414             this.checked = state;
38415             return;
38416         }
38417         
38418     
38419         if(this.wrap){
38420             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38421         }
38422         this.checked = state;
38423         if(suppressEvent !== true){
38424             this.fireEvent('check', this, state);
38425         }
38426         this.inSetChecked = true;
38427         this.el.dom.value = state ? this.inputValue : this.valueOff;
38428         this.inSetChecked = false;
38429         
38430     },
38431     // handle setting of hidden value by some other method!!?!?
38432     setFromHidden: function()
38433     {
38434         if(!this.el){
38435             return;
38436         }
38437         //console.log("SET FROM HIDDEN");
38438         //alert('setFrom hidden');
38439         this.setValue(this.el.dom.value);
38440     },
38441     
38442     onDestroy : function()
38443     {
38444         if(this.viewEl){
38445             Roo.get(this.viewEl).remove();
38446         }
38447          
38448         Roo.form.Checkbox.superclass.onDestroy.call(this);
38449     }
38450
38451 });/*
38452  * Based on:
38453  * Ext JS Library 1.1.1
38454  * Copyright(c) 2006-2007, Ext JS, LLC.
38455  *
38456  * Originally Released Under LGPL - original licence link has changed is not relivant.
38457  *
38458  * Fork - LGPL
38459  * <script type="text/javascript">
38460  */
38461  
38462 /**
38463  * @class Roo.form.Radio
38464  * @extends Roo.form.Checkbox
38465  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38466  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38467  * @constructor
38468  * Creates a new Radio
38469  * @param {Object} config Configuration options
38470  */
38471 Roo.form.Radio = function(){
38472     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38473 };
38474 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38475     inputType: 'radio',
38476
38477     /**
38478      * If this radio is part of a group, it will return the selected value
38479      * @return {String}
38480      */
38481     getGroupValue : function(){
38482         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38483     }
38484 });//<script type="text/javascript">
38485
38486 /*
38487  * Ext JS Library 1.1.1
38488  * Copyright(c) 2006-2007, Ext JS, LLC.
38489  * licensing@extjs.com
38490  * 
38491  * http://www.extjs.com/license
38492  */
38493  
38494  /*
38495   * 
38496   * Known bugs:
38497   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38498   * - IE ? - no idea how much works there.
38499   * 
38500   * 
38501   * 
38502   */
38503  
38504
38505 /**
38506  * @class Ext.form.HtmlEditor
38507  * @extends Ext.form.Field
38508  * Provides a lightweight HTML Editor component.
38509  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38510  * 
38511  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38512  * supported by this editor.</b><br/><br/>
38513  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38514  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38515  */
38516 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38517       /**
38518      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38519      */
38520     toolbars : false,
38521     /**
38522      * @cfg {String} createLinkText The default text for the create link prompt
38523      */
38524     createLinkText : 'Please enter the URL for the link:',
38525     /**
38526      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38527      */
38528     defaultLinkValue : 'http:/'+'/',
38529    
38530      /**
38531      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38532      *                        Roo.resizable.
38533      */
38534     resizable : false,
38535      /**
38536      * @cfg {Number} height (in pixels)
38537      */   
38538     height: 300,
38539    /**
38540      * @cfg {Number} width (in pixels)
38541      */   
38542     width: 500,
38543     
38544     /**
38545      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38546      * 
38547      */
38548     stylesheets: false,
38549     
38550     // id of frame..
38551     frameId: false,
38552     
38553     // private properties
38554     validationEvent : false,
38555     deferHeight: true,
38556     initialized : false,
38557     activated : false,
38558     sourceEditMode : false,
38559     onFocus : Roo.emptyFn,
38560     iframePad:3,
38561     hideMode:'offsets',
38562     
38563     defaultAutoCreate : { // modified by initCompnoent..
38564         tag: "textarea",
38565         style:"width:500px;height:300px;",
38566         autocomplete: "off"
38567     },
38568
38569     // private
38570     initComponent : function(){
38571         this.addEvents({
38572             /**
38573              * @event initialize
38574              * Fires when the editor is fully initialized (including the iframe)
38575              * @param {HtmlEditor} this
38576              */
38577             initialize: true,
38578             /**
38579              * @event activate
38580              * Fires when the editor is first receives the focus. Any insertion must wait
38581              * until after this event.
38582              * @param {HtmlEditor} this
38583              */
38584             activate: true,
38585              /**
38586              * @event beforesync
38587              * Fires before the textarea is updated with content from the editor iframe. Return false
38588              * to cancel the sync.
38589              * @param {HtmlEditor} this
38590              * @param {String} html
38591              */
38592             beforesync: true,
38593              /**
38594              * @event beforepush
38595              * Fires before the iframe editor is updated with content from the textarea. Return false
38596              * to cancel the push.
38597              * @param {HtmlEditor} this
38598              * @param {String} html
38599              */
38600             beforepush: true,
38601              /**
38602              * @event sync
38603              * Fires when the textarea is updated with content from the editor iframe.
38604              * @param {HtmlEditor} this
38605              * @param {String} html
38606              */
38607             sync: true,
38608              /**
38609              * @event push
38610              * Fires when the iframe editor is updated with content from the textarea.
38611              * @param {HtmlEditor} this
38612              * @param {String} html
38613              */
38614             push: true,
38615              /**
38616              * @event editmodechange
38617              * Fires when the editor switches edit modes
38618              * @param {HtmlEditor} this
38619              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38620              */
38621             editmodechange: true,
38622             /**
38623              * @event editorevent
38624              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38625              * @param {HtmlEditor} this
38626              */
38627             editorevent: true
38628         });
38629         this.defaultAutoCreate =  {
38630             tag: "textarea",
38631             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38632             autocomplete: "off"
38633         };
38634     },
38635
38636     /**
38637      * Protected method that will not generally be called directly. It
38638      * is called when the editor creates its toolbar. Override this method if you need to
38639      * add custom toolbar buttons.
38640      * @param {HtmlEditor} editor
38641      */
38642     createToolbar : function(editor){
38643         if (!editor.toolbars || !editor.toolbars.length) {
38644             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38645         }
38646         
38647         for (var i =0 ; i < editor.toolbars.length;i++) {
38648             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38649             editor.toolbars[i].init(editor);
38650         }
38651          
38652         
38653     },
38654
38655     /**
38656      * Protected method that will not generally be called directly. It
38657      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38658      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38659      */
38660     getDocMarkup : function(){
38661         // body styles..
38662         var st = '';
38663         if (this.stylesheets === false) {
38664             
38665             Roo.get(document.head).select('style').each(function(node) {
38666                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38667             });
38668             
38669             Roo.get(document.head).select('link').each(function(node) { 
38670                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38671             });
38672             
38673         } else if (!this.stylesheets.length) {
38674                 // simple..
38675                 st = '<style type="text/css">' +
38676                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38677                    '</style>';
38678         } else {
38679             Roo.each(this.stylesheets, function(s) {
38680                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38681             });
38682             
38683         }
38684         
38685         return '<html><head>' + st  +
38686             //<style type="text/css">' +
38687             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38688             //'</style>' +
38689             ' </head><body></body></html>';
38690     },
38691
38692     // private
38693     onRender : function(ct, position)
38694     {
38695         var _t = this;
38696         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38697         this.el.dom.style.border = '0 none';
38698         this.el.dom.setAttribute('tabIndex', -1);
38699         this.el.addClass('x-hidden');
38700         if(Roo.isIE){ // fix IE 1px bogus margin
38701             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38702         }
38703         this.wrap = this.el.wrap({
38704             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38705         });
38706         
38707         if (this.resizable) {
38708             this.resizeEl = new Roo.Resizable(this.wrap, {
38709                 pinned : true,
38710                 wrap: true,
38711                 dynamic : true,
38712                 minHeight : this.height,
38713                 height: this.height,
38714                 handles : this.resizable,
38715                 width: this.width,
38716                 listeners : {
38717                     resize : function(r, w, h) {
38718                         _t.onResize(w,h); // -something
38719                     }
38720                 }
38721             });
38722             
38723         }
38724
38725         this.frameId = Roo.id();
38726         
38727         this.createToolbar(this);
38728         
38729       
38730         
38731         var iframe = this.wrap.createChild({
38732             tag: 'iframe',
38733             id: this.frameId,
38734             name: this.frameId,
38735             frameBorder : 'no',
38736             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38737         }, this.el
38738         );
38739         
38740        // console.log(iframe);
38741         //this.wrap.dom.appendChild(iframe);
38742
38743         this.iframe = iframe.dom;
38744
38745          this.assignDocWin();
38746         
38747         this.doc.designMode = 'on';
38748        
38749         this.doc.open();
38750         this.doc.write(this.getDocMarkup());
38751         this.doc.close();
38752
38753         
38754         var task = { // must defer to wait for browser to be ready
38755             run : function(){
38756                 //console.log("run task?" + this.doc.readyState);
38757                 this.assignDocWin();
38758                 if(this.doc.body || this.doc.readyState == 'complete'){
38759                     try {
38760                         this.doc.designMode="on";
38761                     } catch (e) {
38762                         return;
38763                     }
38764                     Roo.TaskMgr.stop(task);
38765                     this.initEditor.defer(10, this);
38766                 }
38767             },
38768             interval : 10,
38769             duration:10000,
38770             scope: this
38771         };
38772         Roo.TaskMgr.start(task);
38773
38774         if(!this.width){
38775             this.setSize(this.wrap.getSize());
38776         }
38777         if (this.resizeEl) {
38778             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38779             // should trigger onReize..
38780         }
38781     },
38782
38783     // private
38784     onResize : function(w, h)
38785     {
38786         //Roo.log('resize: ' +w + ',' + h );
38787         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38788         if(this.el && this.iframe){
38789             if(typeof w == 'number'){
38790                 var aw = w - this.wrap.getFrameWidth('lr');
38791                 this.el.setWidth(this.adjustWidth('textarea', aw));
38792                 this.iframe.style.width = aw + 'px';
38793             }
38794             if(typeof h == 'number'){
38795                 var tbh = 0;
38796                 for (var i =0; i < this.toolbars.length;i++) {
38797                     // fixme - ask toolbars for heights?
38798                     tbh += this.toolbars[i].tb.el.getHeight();
38799                     if (this.toolbars[i].footer) {
38800                         tbh += this.toolbars[i].footer.el.getHeight();
38801                     }
38802                 }
38803                 
38804                 
38805                 
38806                 
38807                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38808                 ah -= 5; // knock a few pixes off for look..
38809                 this.el.setHeight(this.adjustWidth('textarea', ah));
38810                 this.iframe.style.height = ah + 'px';
38811                 if(this.doc){
38812                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38813                 }
38814             }
38815         }
38816     },
38817
38818     /**
38819      * Toggles the editor between standard and source edit mode.
38820      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38821      */
38822     toggleSourceEdit : function(sourceEditMode){
38823         
38824         this.sourceEditMode = sourceEditMode === true;
38825         
38826         if(this.sourceEditMode){
38827           
38828             this.syncValue();
38829             this.iframe.className = 'x-hidden';
38830             this.el.removeClass('x-hidden');
38831             this.el.dom.removeAttribute('tabIndex');
38832             this.el.focus();
38833         }else{
38834              
38835             this.pushValue();
38836             this.iframe.className = '';
38837             this.el.addClass('x-hidden');
38838             this.el.dom.setAttribute('tabIndex', -1);
38839             this.deferFocus();
38840         }
38841         this.setSize(this.wrap.getSize());
38842         this.fireEvent('editmodechange', this, this.sourceEditMode);
38843     },
38844
38845     // private used internally
38846     createLink : function(){
38847         var url = prompt(this.createLinkText, this.defaultLinkValue);
38848         if(url && url != 'http:/'+'/'){
38849             this.relayCmd('createlink', url);
38850         }
38851     },
38852
38853     // private (for BoxComponent)
38854     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38855
38856     // private (for BoxComponent)
38857     getResizeEl : function(){
38858         return this.wrap;
38859     },
38860
38861     // private (for BoxComponent)
38862     getPositionEl : function(){
38863         return this.wrap;
38864     },
38865
38866     // private
38867     initEvents : function(){
38868         this.originalValue = this.getValue();
38869     },
38870
38871     /**
38872      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38873      * @method
38874      */
38875     markInvalid : Roo.emptyFn,
38876     /**
38877      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38878      * @method
38879      */
38880     clearInvalid : Roo.emptyFn,
38881
38882     setValue : function(v){
38883         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38884         this.pushValue();
38885     },
38886
38887     /**
38888      * Protected method that will not generally be called directly. If you need/want
38889      * custom HTML cleanup, this is the method you should override.
38890      * @param {String} html The HTML to be cleaned
38891      * return {String} The cleaned HTML
38892      */
38893     cleanHtml : function(html){
38894         html = String(html);
38895         if(html.length > 5){
38896             if(Roo.isSafari){ // strip safari nonsense
38897                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38898             }
38899         }
38900         if(html == '&nbsp;'){
38901             html = '';
38902         }
38903         return html;
38904     },
38905
38906     /**
38907      * Protected method that will not generally be called directly. Syncs the contents
38908      * of the editor iframe with the textarea.
38909      */
38910     syncValue : function(){
38911         if(this.initialized){
38912             var bd = (this.doc.body || this.doc.documentElement);
38913             this.cleanUpPaste();
38914             var html = bd.innerHTML;
38915             if(Roo.isSafari){
38916                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38917                 var m = bs.match(/text-align:(.*?);/i);
38918                 if(m && m[1]){
38919                     html = '<div style="'+m[0]+'">' + html + '</div>';
38920                 }
38921             }
38922             html = this.cleanHtml(html);
38923             if(this.fireEvent('beforesync', this, html) !== false){
38924                 this.el.dom.value = html;
38925                 this.fireEvent('sync', this, html);
38926             }
38927         }
38928     },
38929
38930     /**
38931      * Protected method that will not generally be called directly. Pushes the value of the textarea
38932      * into the iframe editor.
38933      */
38934     pushValue : function(){
38935         if(this.initialized){
38936             var v = this.el.dom.value;
38937             if(v.length < 1){
38938                 v = '&#160;';
38939             }
38940             
38941             if(this.fireEvent('beforepush', this, v) !== false){
38942                 var d = (this.doc.body || this.doc.documentElement);
38943                 d.innerHTML = v;
38944                 this.cleanUpPaste();
38945                 this.el.dom.value = d.innerHTML;
38946                 this.fireEvent('push', this, v);
38947             }
38948         }
38949     },
38950
38951     // private
38952     deferFocus : function(){
38953         this.focus.defer(10, this);
38954     },
38955
38956     // doc'ed in Field
38957     focus : function(){
38958         if(this.win && !this.sourceEditMode){
38959             this.win.focus();
38960         }else{
38961             this.el.focus();
38962         }
38963     },
38964     
38965     assignDocWin: function()
38966     {
38967         var iframe = this.iframe;
38968         
38969          if(Roo.isIE){
38970             this.doc = iframe.contentWindow.document;
38971             this.win = iframe.contentWindow;
38972         } else {
38973             if (!Roo.get(this.frameId)) {
38974                 return;
38975             }
38976             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38977             this.win = Roo.get(this.frameId).dom.contentWindow;
38978         }
38979     },
38980     
38981     // private
38982     initEditor : function(){
38983         //console.log("INIT EDITOR");
38984         this.assignDocWin();
38985         
38986         
38987         
38988         this.doc.designMode="on";
38989         this.doc.open();
38990         this.doc.write(this.getDocMarkup());
38991         this.doc.close();
38992         
38993         var dbody = (this.doc.body || this.doc.documentElement);
38994         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38995         // this copies styles from the containing element into thsi one..
38996         // not sure why we need all of this..
38997         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38998         ss['background-attachment'] = 'fixed'; // w3c
38999         dbody.bgProperties = 'fixed'; // ie
39000         Roo.DomHelper.applyStyles(dbody, ss);
39001         Roo.EventManager.on(this.doc, {
39002             //'mousedown': this.onEditorEvent,
39003             'mouseup': this.onEditorEvent,
39004             'dblclick': this.onEditorEvent,
39005             'click': this.onEditorEvent,
39006             'keyup': this.onEditorEvent,
39007             buffer:100,
39008             scope: this
39009         });
39010         if(Roo.isGecko){
39011             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39012         }
39013         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39014             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39015         }
39016         this.initialized = true;
39017
39018         this.fireEvent('initialize', this);
39019         this.pushValue();
39020     },
39021
39022     // private
39023     onDestroy : function(){
39024         
39025         
39026         
39027         if(this.rendered){
39028             
39029             for (var i =0; i < this.toolbars.length;i++) {
39030                 // fixme - ask toolbars for heights?
39031                 this.toolbars[i].onDestroy();
39032             }
39033             
39034             this.wrap.dom.innerHTML = '';
39035             this.wrap.remove();
39036         }
39037     },
39038
39039     // private
39040     onFirstFocus : function(){
39041         
39042         this.assignDocWin();
39043         
39044         
39045         this.activated = true;
39046         for (var i =0; i < this.toolbars.length;i++) {
39047             this.toolbars[i].onFirstFocus();
39048         }
39049        
39050         if(Roo.isGecko){ // prevent silly gecko errors
39051             this.win.focus();
39052             var s = this.win.getSelection();
39053             if(!s.focusNode || s.focusNode.nodeType != 3){
39054                 var r = s.getRangeAt(0);
39055                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39056                 r.collapse(true);
39057                 this.deferFocus();
39058             }
39059             try{
39060                 this.execCmd('useCSS', true);
39061                 this.execCmd('styleWithCSS', false);
39062             }catch(e){}
39063         }
39064         this.fireEvent('activate', this);
39065     },
39066
39067     // private
39068     adjustFont: function(btn){
39069         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39070         //if(Roo.isSafari){ // safari
39071         //    adjust *= 2;
39072        // }
39073         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39074         if(Roo.isSafari){ // safari
39075             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39076             v =  (v < 10) ? 10 : v;
39077             v =  (v > 48) ? 48 : v;
39078             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39079             
39080         }
39081         
39082         
39083         v = Math.max(1, v+adjust);
39084         
39085         this.execCmd('FontSize', v  );
39086     },
39087
39088     onEditorEvent : function(e){
39089         this.fireEvent('editorevent', this, e);
39090       //  this.updateToolbar();
39091         this.syncValue();
39092     },
39093
39094     insertTag : function(tg)
39095     {
39096         // could be a bit smarter... -> wrap the current selected tRoo..
39097         
39098         this.execCmd("formatblock",   tg);
39099         
39100     },
39101     
39102     insertText : function(txt)
39103     {
39104         
39105         
39106         range = this.createRange();
39107         range.deleteContents();
39108                //alert(Sender.getAttribute('label'));
39109                
39110         range.insertNode(this.doc.createTextNode(txt));
39111     } ,
39112     
39113     // private
39114     relayBtnCmd : function(btn){
39115         this.relayCmd(btn.cmd);
39116     },
39117
39118     /**
39119      * Executes a Midas editor command on the editor document and performs necessary focus and
39120      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39121      * @param {String} cmd The Midas command
39122      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39123      */
39124     relayCmd : function(cmd, value){
39125         this.win.focus();
39126         this.execCmd(cmd, value);
39127         this.fireEvent('editorevent', this);
39128         //this.updateToolbar();
39129         this.deferFocus();
39130     },
39131
39132     /**
39133      * Executes a Midas editor command directly on the editor document.
39134      * For visual commands, you should use {@link #relayCmd} instead.
39135      * <b>This should only be called after the editor is initialized.</b>
39136      * @param {String} cmd The Midas command
39137      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39138      */
39139     execCmd : function(cmd, value){
39140         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39141         this.syncValue();
39142     },
39143
39144    
39145     /**
39146      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39147      * to insert tRoo.
39148      * @param {String} text
39149      */
39150     insertAtCursor : function(text){
39151         if(!this.activated){
39152             return;
39153         }
39154         if(Roo.isIE){
39155             this.win.focus();
39156             var r = this.doc.selection.createRange();
39157             if(r){
39158                 r.collapse(true);
39159                 r.pasteHTML(text);
39160                 this.syncValue();
39161                 this.deferFocus();
39162             }
39163         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39164             this.win.focus();
39165             this.execCmd('InsertHTML', text);
39166             this.deferFocus();
39167         }
39168     },
39169  // private
39170     mozKeyPress : function(e){
39171         if(e.ctrlKey){
39172             var c = e.getCharCode(), cmd;
39173           
39174             if(c > 0){
39175                 c = String.fromCharCode(c).toLowerCase();
39176                 switch(c){
39177                     case 'b':
39178                         cmd = 'bold';
39179                     break;
39180                     case 'i':
39181                         cmd = 'italic';
39182                     break;
39183                     case 'u':
39184                         cmd = 'underline';
39185                     case 'v':
39186                         this.cleanUpPaste.defer(100, this);
39187                         return;
39188                     break;
39189                 }
39190                 if(cmd){
39191                     this.win.focus();
39192                     this.execCmd(cmd);
39193                     this.deferFocus();
39194                     e.preventDefault();
39195                 }
39196                 
39197             }
39198         }
39199     },
39200
39201     // private
39202     fixKeys : function(){ // load time branching for fastest keydown performance
39203         if(Roo.isIE){
39204             return function(e){
39205                 var k = e.getKey(), r;
39206                 if(k == e.TAB){
39207                     e.stopEvent();
39208                     r = this.doc.selection.createRange();
39209                     if(r){
39210                         r.collapse(true);
39211                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39212                         this.deferFocus();
39213                     }
39214                     return;
39215                 }
39216                 
39217                 if(k == e.ENTER){
39218                     r = this.doc.selection.createRange();
39219                     if(r){
39220                         var target = r.parentElement();
39221                         if(!target || target.tagName.toLowerCase() != 'li'){
39222                             e.stopEvent();
39223                             r.pasteHTML('<br />');
39224                             r.collapse(false);
39225                             r.select();
39226                         }
39227                     }
39228                 }
39229                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39230                     this.cleanUpPaste.defer(100, this);
39231                     return;
39232                 }
39233                 
39234                 
39235             };
39236         }else if(Roo.isOpera){
39237             return function(e){
39238                 var k = e.getKey();
39239                 if(k == e.TAB){
39240                     e.stopEvent();
39241                     this.win.focus();
39242                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39243                     this.deferFocus();
39244                 }
39245                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39246                     this.cleanUpPaste.defer(100, this);
39247                     return;
39248                 }
39249                 
39250             };
39251         }else if(Roo.isSafari){
39252             return function(e){
39253                 var k = e.getKey();
39254                 
39255                 if(k == e.TAB){
39256                     e.stopEvent();
39257                     this.execCmd('InsertText','\t');
39258                     this.deferFocus();
39259                     return;
39260                 }
39261                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39262                     this.cleanUpPaste.defer(100, this);
39263                     return;
39264                 }
39265                 
39266              };
39267         }
39268     }(),
39269     
39270     getAllAncestors: function()
39271     {
39272         var p = this.getSelectedNode();
39273         var a = [];
39274         if (!p) {
39275             a.push(p); // push blank onto stack..
39276             p = this.getParentElement();
39277         }
39278         
39279         
39280         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39281             a.push(p);
39282             p = p.parentNode;
39283         }
39284         a.push(this.doc.body);
39285         return a;
39286     },
39287     lastSel : false,
39288     lastSelNode : false,
39289     
39290     
39291     getSelection : function() 
39292     {
39293         this.assignDocWin();
39294         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39295     },
39296     
39297     getSelectedNode: function() 
39298     {
39299         // this may only work on Gecko!!!
39300         
39301         // should we cache this!!!!
39302         
39303         
39304         
39305          
39306         var range = this.createRange(this.getSelection()).cloneRange();
39307         
39308         if (Roo.isIE) {
39309             var parent = range.parentElement();
39310             while (true) {
39311                 var testRange = range.duplicate();
39312                 testRange.moveToElementText(parent);
39313                 if (testRange.inRange(range)) {
39314                     break;
39315                 }
39316                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39317                     break;
39318                 }
39319                 parent = parent.parentElement;
39320             }
39321             return parent;
39322         }
39323         
39324         // is ancestor a text element.
39325         var ac =  range.commonAncestorContainer;
39326         if (ac.nodeType == 3) {
39327             ac = ac.parentNode;
39328         }
39329         
39330         var ar = ac.childNodes;
39331          
39332         var nodes = [];
39333         var other_nodes = [];
39334         var has_other_nodes = false;
39335         for (var i=0;i<ar.length;i++) {
39336             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39337                 continue;
39338             }
39339             // fullly contained node.
39340             
39341             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39342                 nodes.push(ar[i]);
39343                 continue;
39344             }
39345             
39346             // probably selected..
39347             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39348                 other_nodes.push(ar[i]);
39349                 continue;
39350             }
39351             // outer..
39352             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39353                 continue;
39354             }
39355             
39356             
39357             has_other_nodes = true;
39358         }
39359         if (!nodes.length && other_nodes.length) {
39360             nodes= other_nodes;
39361         }
39362         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39363             return false;
39364         }
39365         
39366         return nodes[0];
39367     },
39368     createRange: function(sel)
39369     {
39370         // this has strange effects when using with 
39371         // top toolbar - not sure if it's a great idea.
39372         //this.editor.contentWindow.focus();
39373         if (typeof sel != "undefined") {
39374             try {
39375                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39376             } catch(e) {
39377                 return this.doc.createRange();
39378             }
39379         } else {
39380             return this.doc.createRange();
39381         }
39382     },
39383     getParentElement: function()
39384     {
39385         
39386         this.assignDocWin();
39387         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39388         
39389         var range = this.createRange(sel);
39390          
39391         try {
39392             var p = range.commonAncestorContainer;
39393             while (p.nodeType == 3) { // text node
39394                 p = p.parentNode;
39395             }
39396             return p;
39397         } catch (e) {
39398             return null;
39399         }
39400     
39401     },
39402     /***
39403      *
39404      * Range intersection.. the hard stuff...
39405      *  '-1' = before
39406      *  '0' = hits..
39407      *  '1' = after.
39408      *         [ -- selected range --- ]
39409      *   [fail]                        [fail]
39410      *
39411      *    basically..
39412      *      if end is before start or  hits it. fail.
39413      *      if start is after end or hits it fail.
39414      *
39415      *   if either hits (but other is outside. - then it's not 
39416      *   
39417      *    
39418      **/
39419     
39420     
39421     // @see http://www.thismuchiknow.co.uk/?p=64.
39422     rangeIntersectsNode : function(range, node)
39423     {
39424         var nodeRange = node.ownerDocument.createRange();
39425         try {
39426             nodeRange.selectNode(node);
39427         } catch (e) {
39428             nodeRange.selectNodeContents(node);
39429         }
39430     
39431         var rangeStartRange = range.cloneRange();
39432         rangeStartRange.collapse(true);
39433     
39434         var rangeEndRange = range.cloneRange();
39435         rangeEndRange.collapse(false);
39436     
39437         var nodeStartRange = nodeRange.cloneRange();
39438         nodeStartRange.collapse(true);
39439     
39440         var nodeEndRange = nodeRange.cloneRange();
39441         nodeEndRange.collapse(false);
39442     
39443         return rangeStartRange.compareBoundaryPoints(
39444                  Range.START_TO_START, nodeEndRange) == -1 &&
39445                rangeEndRange.compareBoundaryPoints(
39446                  Range.START_TO_START, nodeStartRange) == 1;
39447         
39448          
39449     },
39450     rangeCompareNode : function(range, node)
39451     {
39452         var nodeRange = node.ownerDocument.createRange();
39453         try {
39454             nodeRange.selectNode(node);
39455         } catch (e) {
39456             nodeRange.selectNodeContents(node);
39457         }
39458         
39459         
39460         range.collapse(true);
39461     
39462         nodeRange.collapse(true);
39463      
39464         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39465         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39466          
39467         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39468         
39469         var nodeIsBefore   =  ss == 1;
39470         var nodeIsAfter    = ee == -1;
39471         
39472         if (nodeIsBefore && nodeIsAfter)
39473             return 0; // outer
39474         if (!nodeIsBefore && nodeIsAfter)
39475             return 1; //right trailed.
39476         
39477         if (nodeIsBefore && !nodeIsAfter)
39478             return 2;  // left trailed.
39479         // fully contined.
39480         return 3;
39481     },
39482
39483     // private? - in a new class?
39484     cleanUpPaste :  function()
39485     {
39486         // cleans up the whole document..
39487       //  console.log('cleanuppaste');
39488         this.cleanUpChildren(this.doc.body);
39489         
39490         
39491     },
39492     cleanUpChildren : function (n)
39493     {
39494         if (!n.childNodes.length) {
39495             return;
39496         }
39497         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39498            this.cleanUpChild(n.childNodes[i]);
39499         }
39500     },
39501     
39502     
39503         
39504     
39505     cleanUpChild : function (node)
39506     {
39507         //console.log(node);
39508         if (node.nodeName == "#text") {
39509             // clean up silly Windows -- stuff?
39510             return; 
39511         }
39512         if (node.nodeName == "#comment") {
39513             node.parentNode.removeChild(node);
39514             // clean up silly Windows -- stuff?
39515             return; 
39516         }
39517         
39518         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39519             // remove node.
39520             node.parentNode.removeChild(node);
39521             return;
39522             
39523         }
39524         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39525             this.cleanUpChildren(node);
39526             // inserts everything just before this node...
39527             while (node.childNodes.length) {
39528                 var cn = node.childNodes[0];
39529                 node.removeChild(cn);
39530                 node.parentNode.insertBefore(cn, node);
39531             }
39532             node.parentNode.removeChild(node);
39533             return;
39534         }
39535         
39536         if (!node.attributes || !node.attributes.length) {
39537             this.cleanUpChildren(node);
39538             return;
39539         }
39540         
39541         function cleanAttr(n,v)
39542         {
39543             
39544             if (v.match(/^\./) || v.match(/^\//)) {
39545                 return;
39546             }
39547             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39548                 return;
39549             }
39550             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39551             node.removeAttribute(n);
39552             
39553         }
39554         
39555         function cleanStyle(n,v)
39556         {
39557             if (v.match(/expression/)) { //XSS?? should we even bother..
39558                 node.removeAttribute(n);
39559                 return;
39560             }
39561             
39562             
39563             var parts = v.split(/;/);
39564             Roo.each(parts, function(p) {
39565                 p = p.replace(/\s+/g,'');
39566                 if (!p.length) {
39567                     return true;
39568                 }
39569                 var l = p.split(':').shift().replace(/\s+/g,'');
39570                 
39571                 // only allow 'c whitelisted system attributes'
39572                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39573                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39574                     node.removeAttribute(n);
39575                     return false;
39576                 }
39577                 return true;
39578             });
39579             
39580             
39581         }
39582         
39583         
39584         for (var i = node.attributes.length-1; i > -1 ; i--) {
39585             var a = node.attributes[i];
39586             //console.log(a);
39587             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39588                 node.removeAttribute(a.name);
39589                 return;
39590             }
39591             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39592                 cleanAttr(a.name,a.value); // fixme..
39593                 return;
39594             }
39595             if (a.name == 'style') {
39596                 cleanStyle(a.name,a.value);
39597             }
39598             /// clean up MS crap..
39599             if (a.name == 'class') {
39600                 if (a.value.match(/^Mso/)) {
39601                     node.className = '';
39602                 }
39603             }
39604             
39605             // style cleanup!?
39606             // class cleanup?
39607             
39608         }
39609         
39610         
39611         this.cleanUpChildren(node);
39612         
39613         
39614     }
39615     
39616     
39617     // hide stuff that is not compatible
39618     /**
39619      * @event blur
39620      * @hide
39621      */
39622     /**
39623      * @event change
39624      * @hide
39625      */
39626     /**
39627      * @event focus
39628      * @hide
39629      */
39630     /**
39631      * @event specialkey
39632      * @hide
39633      */
39634     /**
39635      * @cfg {String} fieldClass @hide
39636      */
39637     /**
39638      * @cfg {String} focusClass @hide
39639      */
39640     /**
39641      * @cfg {String} autoCreate @hide
39642      */
39643     /**
39644      * @cfg {String} inputType @hide
39645      */
39646     /**
39647      * @cfg {String} invalidClass @hide
39648      */
39649     /**
39650      * @cfg {String} invalidText @hide
39651      */
39652     /**
39653      * @cfg {String} msgFx @hide
39654      */
39655     /**
39656      * @cfg {String} validateOnBlur @hide
39657      */
39658 });
39659
39660 Roo.form.HtmlEditor.white = [
39661         'area', 'br', 'img', 'input', 'hr', 'wbr',
39662         
39663        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39664        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39665        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39666        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39667        'table',   'ul',         'xmp', 
39668        
39669        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39670       'thead',   'tr', 
39671      
39672       'dir', 'menu', 'ol', 'ul', 'dl',
39673        
39674       'embed',  'object'
39675 ];
39676
39677
39678 Roo.form.HtmlEditor.black = [
39679     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39680         'applet', // 
39681         'base',   'basefont', 'bgsound', 'blink',  'body', 
39682         'frame',  'frameset', 'head',    'html',   'ilayer', 
39683         'iframe', 'layer',  'link',     'meta',    'object',   
39684         'script', 'style' ,'title',  'xml' // clean later..
39685 ];
39686 Roo.form.HtmlEditor.clean = [
39687     'script', 'style', 'title', 'xml'
39688 ];
39689 Roo.form.HtmlEditor.remove = [
39690     'font'
39691 ];
39692 // attributes..
39693
39694 Roo.form.HtmlEditor.ablack = [
39695     'on'
39696 ];
39697     
39698 Roo.form.HtmlEditor.aclean = [ 
39699     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39700 ];
39701
39702 // protocols..
39703 Roo.form.HtmlEditor.pwhite= [
39704         'http',  'https',  'mailto'
39705 ];
39706
39707 // white listed style attributes.
39708 Roo.form.HtmlEditor.cwhite= [
39709         'text-align',
39710         'font-size'
39711 ];
39712
39713 // <script type="text/javascript">
39714 /*
39715  * Based on
39716  * Ext JS Library 1.1.1
39717  * Copyright(c) 2006-2007, Ext JS, LLC.
39718  *  
39719  
39720  */
39721
39722 /**
39723  * @class Roo.form.HtmlEditorToolbar1
39724  * Basic Toolbar
39725  * 
39726  * Usage:
39727  *
39728  new Roo.form.HtmlEditor({
39729     ....
39730     toolbars : [
39731         new Roo.form.HtmlEditorToolbar1({
39732             disable : { fonts: 1 , format: 1, ..., ... , ...],
39733             btns : [ .... ]
39734         })
39735     }
39736      
39737  * 
39738  * @cfg {Object} disable List of elements to disable..
39739  * @cfg {Array} btns List of additional buttons.
39740  * 
39741  * 
39742  * NEEDS Extra CSS? 
39743  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39744  */
39745  
39746 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39747 {
39748     
39749     Roo.apply(this, config);
39750     
39751     // default disabled, based on 'good practice'..
39752     this.disable = this.disable || {};
39753     Roo.applyIf(this.disable, {
39754         fontSize : true,
39755         colors : true,
39756         specialElements : true
39757     });
39758     
39759     
39760     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39761     // dont call parent... till later.
39762 }
39763
39764 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39765     
39766     tb: false,
39767     
39768     rendered: false,
39769     
39770     editor : false,
39771     /**
39772      * @cfg {Object} disable  List of toolbar elements to disable
39773          
39774      */
39775     disable : false,
39776       /**
39777      * @cfg {Array} fontFamilies An array of available font families
39778      */
39779     fontFamilies : [
39780         'Arial',
39781         'Courier New',
39782         'Tahoma',
39783         'Times New Roman',
39784         'Verdana'
39785     ],
39786     
39787     specialChars : [
39788            "&#169;",
39789           "&#174;",     
39790           "&#8482;",    
39791           "&#163;" ,    
39792          // "&#8212;",    
39793           "&#8230;",    
39794           "&#247;" ,    
39795         //  "&#225;" ,     ?? a acute?
39796            "&#8364;"    , //Euro
39797        //   "&#8220;"    ,
39798         //  "&#8221;"    ,
39799         //  "&#8226;"    ,
39800           "&#176;"  //   , // degrees
39801
39802          // "&#233;"     , // e ecute
39803          // "&#250;"     , // u ecute?
39804     ],
39805     
39806     specialElements : [
39807         {
39808             text: "Insert Table",
39809             xtype: 'MenuItem',
39810             xns : Roo.Menu,
39811             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39812                 
39813         },
39814         {    
39815             text: "Insert Image",
39816             xtype: 'MenuItem',
39817             xns : Roo.Menu,
39818             ihtml : '<img src="about:blank"/>'
39819             
39820         }
39821         
39822          
39823     ],
39824     
39825     
39826     inputElements : [ 
39827             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39828             "input:submit", "input:button", "select", "textarea", "label" ],
39829     formats : [
39830         ["p"] ,  
39831         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39832         ["pre"],[ "code"], 
39833         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39834     ],
39835      /**
39836      * @cfg {String} defaultFont default font to use.
39837      */
39838     defaultFont: 'tahoma',
39839    
39840     fontSelect : false,
39841     
39842     
39843     formatCombo : false,
39844     
39845     init : function(editor)
39846     {
39847         this.editor = editor;
39848         
39849         
39850         var fid = editor.frameId;
39851         var etb = this;
39852         function btn(id, toggle, handler){
39853             var xid = fid + '-'+ id ;
39854             return {
39855                 id : xid,
39856                 cmd : id,
39857                 cls : 'x-btn-icon x-edit-'+id,
39858                 enableToggle:toggle !== false,
39859                 scope: editor, // was editor...
39860                 handler:handler||editor.relayBtnCmd,
39861                 clickEvent:'mousedown',
39862                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39863                 tabIndex:-1
39864             };
39865         }
39866         
39867         
39868         
39869         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39870         this.tb = tb;
39871          // stop form submits
39872         tb.el.on('click', function(e){
39873             e.preventDefault(); // what does this do?
39874         });
39875
39876         if(!this.disable.font && !Roo.isSafari){
39877             /* why no safari for fonts
39878             editor.fontSelect = tb.el.createChild({
39879                 tag:'select',
39880                 tabIndex: -1,
39881                 cls:'x-font-select',
39882                 html: editor.createFontOptions()
39883             });
39884             editor.fontSelect.on('change', function(){
39885                 var font = editor.fontSelect.dom.value;
39886                 editor.relayCmd('fontname', font);
39887                 editor.deferFocus();
39888             }, editor);
39889             tb.add(
39890                 editor.fontSelect.dom,
39891                 '-'
39892             );
39893             */
39894         };
39895         if(!this.disable.formats){
39896             this.formatCombo = new Roo.form.ComboBox({
39897                 store: new Roo.data.SimpleStore({
39898                     id : 'tag',
39899                     fields: ['tag'],
39900                     data : this.formats // from states.js
39901                 }),
39902                 blockFocus : true,
39903                 //autoCreate : {tag: "div",  size: "20"},
39904                 displayField:'tag',
39905                 typeAhead: false,
39906                 mode: 'local',
39907                 editable : false,
39908                 triggerAction: 'all',
39909                 emptyText:'Add tag',
39910                 selectOnFocus:true,
39911                 width:135,
39912                 listeners : {
39913                     'select': function(c, r, i) {
39914                         editor.insertTag(r.get('tag'));
39915                         editor.focus();
39916                     }
39917                 }
39918
39919             });
39920             tb.addField(this.formatCombo);
39921             
39922         }
39923         
39924         if(!this.disable.format){
39925             tb.add(
39926                 btn('bold'),
39927                 btn('italic'),
39928                 btn('underline')
39929             );
39930         };
39931         if(!this.disable.fontSize){
39932             tb.add(
39933                 '-',
39934                 
39935                 
39936                 btn('increasefontsize', false, editor.adjustFont),
39937                 btn('decreasefontsize', false, editor.adjustFont)
39938             );
39939         };
39940         
39941         
39942         if(!this.disable.colors){
39943             tb.add(
39944                 '-', {
39945                     id:editor.frameId +'-forecolor',
39946                     cls:'x-btn-icon x-edit-forecolor',
39947                     clickEvent:'mousedown',
39948                     tooltip: this.buttonTips['forecolor'] || undefined,
39949                     tabIndex:-1,
39950                     menu : new Roo.menu.ColorMenu({
39951                         allowReselect: true,
39952                         focus: Roo.emptyFn,
39953                         value:'000000',
39954                         plain:true,
39955                         selectHandler: function(cp, color){
39956                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39957                             editor.deferFocus();
39958                         },
39959                         scope: editor,
39960                         clickEvent:'mousedown'
39961                     })
39962                 }, {
39963                     id:editor.frameId +'backcolor',
39964                     cls:'x-btn-icon x-edit-backcolor',
39965                     clickEvent:'mousedown',
39966                     tooltip: this.buttonTips['backcolor'] || undefined,
39967                     tabIndex:-1,
39968                     menu : new Roo.menu.ColorMenu({
39969                         focus: Roo.emptyFn,
39970                         value:'FFFFFF',
39971                         plain:true,
39972                         allowReselect: true,
39973                         selectHandler: function(cp, color){
39974                             if(Roo.isGecko){
39975                                 editor.execCmd('useCSS', false);
39976                                 editor.execCmd('hilitecolor', color);
39977                                 editor.execCmd('useCSS', true);
39978                                 editor.deferFocus();
39979                             }else{
39980                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39981                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39982                                 editor.deferFocus();
39983                             }
39984                         },
39985                         scope:editor,
39986                         clickEvent:'mousedown'
39987                     })
39988                 }
39989             );
39990         };
39991         // now add all the items...
39992         
39993
39994         if(!this.disable.alignments){
39995             tb.add(
39996                 '-',
39997                 btn('justifyleft'),
39998                 btn('justifycenter'),
39999                 btn('justifyright')
40000             );
40001         };
40002
40003         //if(!Roo.isSafari){
40004             if(!this.disable.links){
40005                 tb.add(
40006                     '-',
40007                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40008                 );
40009             };
40010
40011             if(!this.disable.lists){
40012                 tb.add(
40013                     '-',
40014                     btn('insertorderedlist'),
40015                     btn('insertunorderedlist')
40016                 );
40017             }
40018             if(!this.disable.sourceEdit){
40019                 tb.add(
40020                     '-',
40021                     btn('sourceedit', true, function(btn){
40022                         this.toggleSourceEdit(btn.pressed);
40023                     })
40024                 );
40025             }
40026         //}
40027         
40028         var smenu = { };
40029         // special menu.. - needs to be tidied up..
40030         if (!this.disable.special) {
40031             smenu = {
40032                 text: "&#169;",
40033                 cls: 'x-edit-none',
40034                 
40035                 menu : {
40036                     items : []
40037                 }
40038             };
40039             for (var i =0; i < this.specialChars.length; i++) {
40040                 smenu.menu.items.push({
40041                     
40042                     html: this.specialChars[i],
40043                     handler: function(a,b) {
40044                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40045                         
40046                     },
40047                     tabIndex:-1
40048                 });
40049             }
40050             
40051             
40052             tb.add(smenu);
40053             
40054             
40055         }
40056          
40057         if (!this.disable.specialElements) {
40058             var semenu = {
40059                 text: "Other;",
40060                 cls: 'x-edit-none',
40061                 menu : {
40062                     items : []
40063                 }
40064             };
40065             for (var i =0; i < this.specialElements.length; i++) {
40066                 semenu.menu.items.push(
40067                     Roo.apply({ 
40068                         handler: function(a,b) {
40069                             editor.insertAtCursor(this.ihtml);
40070                         }
40071                     }, this.specialElements[i])
40072                 );
40073                     
40074             }
40075             
40076             tb.add(semenu);
40077             
40078             
40079         }
40080          
40081         
40082         if (this.btns) {
40083             for(var i =0; i< this.btns.length;i++) {
40084                 var b = this.btns[i];
40085                 b.cls =  'x-edit-none';
40086                 b.scope = editor;
40087                 tb.add(b);
40088             }
40089         
40090         }
40091         
40092         
40093         
40094         // disable everything...
40095         
40096         this.tb.items.each(function(item){
40097            if(item.id != editor.frameId+ '-sourceedit'){
40098                 item.disable();
40099             }
40100         });
40101         this.rendered = true;
40102         
40103         // the all the btns;
40104         editor.on('editorevent', this.updateToolbar, this);
40105         // other toolbars need to implement this..
40106         //editor.on('editmodechange', this.updateToolbar, this);
40107     },
40108     
40109     
40110     
40111     /**
40112      * Protected method that will not generally be called directly. It triggers
40113      * a toolbar update by reading the markup state of the current selection in the editor.
40114      */
40115     updateToolbar: function(){
40116
40117         if(!this.editor.activated){
40118             this.editor.onFirstFocus();
40119             return;
40120         }
40121
40122         var btns = this.tb.items.map, 
40123             doc = this.editor.doc,
40124             frameId = this.editor.frameId;
40125
40126         if(!this.disable.font && !Roo.isSafari){
40127             /*
40128             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40129             if(name != this.fontSelect.dom.value){
40130                 this.fontSelect.dom.value = name;
40131             }
40132             */
40133         }
40134         if(!this.disable.format){
40135             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40136             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40137             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40138         }
40139         if(!this.disable.alignments){
40140             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40141             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40142             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40143         }
40144         if(!Roo.isSafari && !this.disable.lists){
40145             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40146             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40147         }
40148         
40149         var ans = this.editor.getAllAncestors();
40150         if (this.formatCombo) {
40151             
40152             
40153             var store = this.formatCombo.store;
40154             this.formatCombo.setValue("");
40155             for (var i =0; i < ans.length;i++) {
40156                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40157                     // select it..
40158                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40159                     break;
40160                 }
40161             }
40162         }
40163         
40164         
40165         
40166         // hides menus... - so this cant be on a menu...
40167         Roo.menu.MenuMgr.hideAll();
40168
40169         //this.editorsyncValue();
40170     },
40171    
40172     
40173     createFontOptions : function(){
40174         var buf = [], fs = this.fontFamilies, ff, lc;
40175         for(var i = 0, len = fs.length; i< len; i++){
40176             ff = fs[i];
40177             lc = ff.toLowerCase();
40178             buf.push(
40179                 '<option value="',lc,'" style="font-family:',ff,';"',
40180                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40181                     ff,
40182                 '</option>'
40183             );
40184         }
40185         return buf.join('');
40186     },
40187     
40188     toggleSourceEdit : function(sourceEditMode){
40189         if(sourceEditMode === undefined){
40190             sourceEditMode = !this.sourceEditMode;
40191         }
40192         this.sourceEditMode = sourceEditMode === true;
40193         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40194         // just toggle the button?
40195         if(btn.pressed !== this.editor.sourceEditMode){
40196             btn.toggle(this.editor.sourceEditMode);
40197             return;
40198         }
40199         
40200         if(this.sourceEditMode){
40201             this.tb.items.each(function(item){
40202                 if(item.cmd != 'sourceedit'){
40203                     item.disable();
40204                 }
40205             });
40206           
40207         }else{
40208             if(this.initialized){
40209                 this.tb.items.each(function(item){
40210                     item.enable();
40211                 });
40212             }
40213             
40214         }
40215         // tell the editor that it's been pressed..
40216         this.editor.toggleSourceEdit(sourceEditMode);
40217        
40218     },
40219      /**
40220      * Object collection of toolbar tooltips for the buttons in the editor. The key
40221      * is the command id associated with that button and the value is a valid QuickTips object.
40222      * For example:
40223 <pre><code>
40224 {
40225     bold : {
40226         title: 'Bold (Ctrl+B)',
40227         text: 'Make the selected text bold.',
40228         cls: 'x-html-editor-tip'
40229     },
40230     italic : {
40231         title: 'Italic (Ctrl+I)',
40232         text: 'Make the selected text italic.',
40233         cls: 'x-html-editor-tip'
40234     },
40235     ...
40236 </code></pre>
40237     * @type Object
40238      */
40239     buttonTips : {
40240         bold : {
40241             title: 'Bold (Ctrl+B)',
40242             text: 'Make the selected text bold.',
40243             cls: 'x-html-editor-tip'
40244         },
40245         italic : {
40246             title: 'Italic (Ctrl+I)',
40247             text: 'Make the selected text italic.',
40248             cls: 'x-html-editor-tip'
40249         },
40250         underline : {
40251             title: 'Underline (Ctrl+U)',
40252             text: 'Underline the selected text.',
40253             cls: 'x-html-editor-tip'
40254         },
40255         increasefontsize : {
40256             title: 'Grow Text',
40257             text: 'Increase the font size.',
40258             cls: 'x-html-editor-tip'
40259         },
40260         decreasefontsize : {
40261             title: 'Shrink Text',
40262             text: 'Decrease the font size.',
40263             cls: 'x-html-editor-tip'
40264         },
40265         backcolor : {
40266             title: 'Text Highlight Color',
40267             text: 'Change the background color of the selected text.',
40268             cls: 'x-html-editor-tip'
40269         },
40270         forecolor : {
40271             title: 'Font Color',
40272             text: 'Change the color of the selected text.',
40273             cls: 'x-html-editor-tip'
40274         },
40275         justifyleft : {
40276             title: 'Align Text Left',
40277             text: 'Align text to the left.',
40278             cls: 'x-html-editor-tip'
40279         },
40280         justifycenter : {
40281             title: 'Center Text',
40282             text: 'Center text in the editor.',
40283             cls: 'x-html-editor-tip'
40284         },
40285         justifyright : {
40286             title: 'Align Text Right',
40287             text: 'Align text to the right.',
40288             cls: 'x-html-editor-tip'
40289         },
40290         insertunorderedlist : {
40291             title: 'Bullet List',
40292             text: 'Start a bulleted list.',
40293             cls: 'x-html-editor-tip'
40294         },
40295         insertorderedlist : {
40296             title: 'Numbered List',
40297             text: 'Start a numbered list.',
40298             cls: 'x-html-editor-tip'
40299         },
40300         createlink : {
40301             title: 'Hyperlink',
40302             text: 'Make the selected text a hyperlink.',
40303             cls: 'x-html-editor-tip'
40304         },
40305         sourceedit : {
40306             title: 'Source Edit',
40307             text: 'Switch to source editing mode.',
40308             cls: 'x-html-editor-tip'
40309         }
40310     },
40311     // private
40312     onDestroy : function(){
40313         if(this.rendered){
40314             
40315             this.tb.items.each(function(item){
40316                 if(item.menu){
40317                     item.menu.removeAll();
40318                     if(item.menu.el){
40319                         item.menu.el.destroy();
40320                     }
40321                 }
40322                 item.destroy();
40323             });
40324              
40325         }
40326     },
40327     onFirstFocus: function() {
40328         this.tb.items.each(function(item){
40329            item.enable();
40330         });
40331     }
40332 });
40333
40334
40335
40336
40337 // <script type="text/javascript">
40338 /*
40339  * Based on
40340  * Ext JS Library 1.1.1
40341  * Copyright(c) 2006-2007, Ext JS, LLC.
40342  *  
40343  
40344  */
40345
40346  
40347 /**
40348  * @class Roo.form.HtmlEditor.ToolbarContext
40349  * Context Toolbar
40350  * 
40351  * Usage:
40352  *
40353  new Roo.form.HtmlEditor({
40354     ....
40355     toolbars : [
40356         { xtype: 'ToolbarStandard', styles : {} }
40357         { xtype: 'ToolbarContext', disable : {} }
40358     ]
40359 })
40360
40361      
40362  * 
40363  * @config : {Object} disable List of elements to disable.. (not done yet.)
40364  * @config : {Object} styles  Map of styles available.
40365  * 
40366  */
40367
40368 Roo.form.HtmlEditor.ToolbarContext = function(config)
40369 {
40370     
40371     Roo.apply(this, config);
40372     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40373     // dont call parent... till later.
40374     this.styles = this.styles || {};
40375 }
40376 Roo.form.HtmlEditor.ToolbarContext.types = {
40377     'IMG' : {
40378         width : {
40379             title: "Width",
40380             width: 40
40381         },
40382         height:  {
40383             title: "Height",
40384             width: 40
40385         },
40386         align: {
40387             title: "Align",
40388             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40389             width : 80
40390             
40391         },
40392         border: {
40393             title: "Border",
40394             width: 40
40395         },
40396         alt: {
40397             title: "Alt",
40398             width: 120
40399         },
40400         src : {
40401             title: "Src",
40402             width: 220
40403         }
40404         
40405     },
40406     'A' : {
40407         name : {
40408             title: "Name",
40409             width: 50
40410         },
40411         href:  {
40412             title: "Href",
40413             width: 220
40414         } // border?
40415         
40416     },
40417     'TABLE' : {
40418         rows : {
40419             title: "Rows",
40420             width: 20
40421         },
40422         cols : {
40423             title: "Cols",
40424             width: 20
40425         },
40426         width : {
40427             title: "Width",
40428             width: 40
40429         },
40430         height : {
40431             title: "Height",
40432             width: 40
40433         },
40434         border : {
40435             title: "Border",
40436             width: 20
40437         }
40438     },
40439     'TD' : {
40440         width : {
40441             title: "Width",
40442             width: 40
40443         },
40444         height : {
40445             title: "Height",
40446             width: 40
40447         },   
40448         align: {
40449             title: "Align",
40450             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40451             width: 80
40452         },
40453         valign: {
40454             title: "Valign",
40455             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40456             width: 80
40457         },
40458         colspan: {
40459             title: "Colspan",
40460             width: 20
40461             
40462         }
40463     },
40464     'INPUT' : {
40465         name : {
40466             title: "name",
40467             width: 120
40468         },
40469         value : {
40470             title: "Value",
40471             width: 120
40472         },
40473         width : {
40474             title: "Width",
40475             width: 40
40476         }
40477     },
40478     'LABEL' : {
40479         'for' : {
40480             title: "For",
40481             width: 120
40482         }
40483     },
40484     'TEXTAREA' : {
40485           name : {
40486             title: "name",
40487             width: 120
40488         },
40489         rows : {
40490             title: "Rows",
40491             width: 20
40492         },
40493         cols : {
40494             title: "Cols",
40495             width: 20
40496         }
40497     },
40498     'SELECT' : {
40499         name : {
40500             title: "name",
40501             width: 120
40502         },
40503         selectoptions : {
40504             title: "Options",
40505             width: 200
40506         }
40507     },
40508     
40509     // should we really allow this??
40510     // should this just be 
40511     'BODY' : {
40512         title : {
40513             title: "title",
40514             width: 200,
40515             disabled : true
40516         }
40517     },
40518     '*' : {
40519         // empty..
40520     }
40521 };
40522
40523
40524
40525 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40526     
40527     tb: false,
40528     
40529     rendered: false,
40530     
40531     editor : false,
40532     /**
40533      * @cfg {Object} disable  List of toolbar elements to disable
40534          
40535      */
40536     disable : false,
40537     /**
40538      * @cfg {Object} styles List of styles 
40539      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40540      *
40541      * These must be defined in the page, so they get rendered correctly..
40542      * .headline { }
40543      * TD.underline { }
40544      * 
40545      */
40546     styles : false,
40547     
40548     
40549     
40550     toolbars : false,
40551     
40552     init : function(editor)
40553     {
40554         this.editor = editor;
40555         
40556         
40557         var fid = editor.frameId;
40558         var etb = this;
40559         function btn(id, toggle, handler){
40560             var xid = fid + '-'+ id ;
40561             return {
40562                 id : xid,
40563                 cmd : id,
40564                 cls : 'x-btn-icon x-edit-'+id,
40565                 enableToggle:toggle !== false,
40566                 scope: editor, // was editor...
40567                 handler:handler||editor.relayBtnCmd,
40568                 clickEvent:'mousedown',
40569                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40570                 tabIndex:-1
40571             };
40572         }
40573         // create a new element.
40574         var wdiv = editor.wrap.createChild({
40575                 tag: 'div'
40576             }, editor.wrap.dom.firstChild.nextSibling, true);
40577         
40578         // can we do this more than once??
40579         
40580          // stop form submits
40581       
40582  
40583         // disable everything...
40584         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40585         this.toolbars = {};
40586            
40587         for (var i in  ty) {
40588           
40589             this.toolbars[i] = this.buildToolbar(ty[i],i);
40590         }
40591         this.tb = this.toolbars.BODY;
40592         this.tb.el.show();
40593         this.buildFooter();
40594         this.footer.show();
40595          
40596         this.rendered = true;
40597         
40598         // the all the btns;
40599         editor.on('editorevent', this.updateToolbar, this);
40600         // other toolbars need to implement this..
40601         //editor.on('editmodechange', this.updateToolbar, this);
40602     },
40603     
40604     
40605     
40606     /**
40607      * Protected method that will not generally be called directly. It triggers
40608      * a toolbar update by reading the markup state of the current selection in the editor.
40609      */
40610     updateToolbar: function(ignore_a,ignore_b,sel){
40611
40612         
40613         if(!this.editor.activated){
40614              this.editor.onFirstFocus();
40615             return;
40616         }
40617         var updateFooter = sel ? false : true;
40618         
40619         
40620         var ans = this.editor.getAllAncestors();
40621         
40622         // pick
40623         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40624         
40625         if (!sel) { 
40626             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40627             sel = sel ? sel : this.editor.doc.body;
40628             sel = sel.tagName.length ? sel : this.editor.doc.body;
40629             
40630         }
40631         // pick a menu that exists..
40632         var tn = sel.tagName.toUpperCase();
40633         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40634         
40635         tn = sel.tagName.toUpperCase();
40636         
40637         var lastSel = this.tb.selectedNode
40638         
40639         this.tb.selectedNode = sel;
40640         
40641         // if current menu does not match..
40642         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40643                 
40644             this.tb.el.hide();
40645             ///console.log("show: " + tn);
40646             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40647             this.tb.el.show();
40648             // update name
40649             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40650             
40651             
40652             // update attributes
40653             if (this.tb.fields) {
40654                 this.tb.fields.each(function(e) {
40655                    e.setValue(sel.getAttribute(e.name));
40656                 });
40657             }
40658             
40659             // update styles
40660             var st = this.tb.fields.item(0);
40661             st.store.removeAll();
40662             var cn = sel.className.split(/\s+/);
40663             
40664             var avs = [];
40665             if (this.styles['*']) {
40666                 
40667                 Roo.each(this.styles['*'], function(v) {
40668                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40669                 });
40670             }
40671             if (this.styles[tn]) { 
40672                 Roo.each(this.styles[tn], function(v) {
40673                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40674                 });
40675             }
40676             
40677             st.store.loadData(avs);
40678             st.collapse();
40679             st.setValue(cn);
40680             
40681             // flag our selected Node.
40682             this.tb.selectedNode = sel;
40683            
40684            
40685             Roo.menu.MenuMgr.hideAll();
40686
40687         }
40688         
40689         if (!updateFooter) {
40690             return;
40691         }
40692         // update the footer
40693         //
40694         var html = '';
40695         
40696         this.footerEls = ans.reverse();
40697         Roo.each(this.footerEls, function(a,i) {
40698             if (!a) { return; }
40699             html += html.length ? ' &gt; '  :  '';
40700             
40701             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40702             
40703         });
40704        
40705         // 
40706         var sz = this.footDisp.up('td').getSize();
40707         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40708         this.footDisp.dom.style.marginLeft = '5px';
40709         
40710         this.footDisp.dom.style.overflow = 'hidden';
40711         
40712         this.footDisp.dom.innerHTML = html;
40713             
40714         //this.editorsyncValue();
40715     },
40716    
40717        
40718     // private
40719     onDestroy : function(){
40720         if(this.rendered){
40721             
40722             this.tb.items.each(function(item){
40723                 if(item.menu){
40724                     item.menu.removeAll();
40725                     if(item.menu.el){
40726                         item.menu.el.destroy();
40727                     }
40728                 }
40729                 item.destroy();
40730             });
40731              
40732         }
40733     },
40734     onFirstFocus: function() {
40735         // need to do this for all the toolbars..
40736         this.tb.items.each(function(item){
40737            item.enable();
40738         });
40739     },
40740     buildToolbar: function(tlist, nm)
40741     {
40742         var editor = this.editor;
40743          // create a new element.
40744         var wdiv = editor.wrap.createChild({
40745                 tag: 'div'
40746             }, editor.wrap.dom.firstChild.nextSibling, true);
40747         
40748        
40749         var tb = new Roo.Toolbar(wdiv);
40750         // add the name..
40751         
40752         tb.add(nm+ ":&nbsp;");
40753         
40754         // styles...
40755         if (this.styles) {
40756             
40757             // this needs a multi-select checkbox...
40758             tb.addField( new Roo.form.ComboBox({
40759                 store: new Roo.data.SimpleStore({
40760                     id : 'val',
40761                     fields: ['val', 'selected'],
40762                     data : [] 
40763                 }),
40764                 name : 'className',
40765                 displayField:'val',
40766                 typeAhead: false,
40767                 mode: 'local',
40768                 editable : false,
40769                 triggerAction: 'all',
40770                 emptyText:'Select Style',
40771                 selectOnFocus:true,
40772                 width: 130,
40773                 listeners : {
40774                     'select': function(c, r, i) {
40775                         // initial support only for on class per el..
40776                         tb.selectedNode.className =  r ? r.get('val') : '';
40777                     }
40778                 }
40779     
40780             }));
40781         }
40782             
40783         
40784         
40785         for (var i in tlist) {
40786             
40787             var item = tlist[i];
40788             tb.add(item.title + ":&nbsp;");
40789             
40790             
40791             
40792             
40793             if (item.opts) {
40794                 // opts == pulldown..
40795                 tb.addField( new Roo.form.ComboBox({
40796                     store: new Roo.data.SimpleStore({
40797                         id : 'val',
40798                         fields: ['val'],
40799                         data : item.opts  
40800                     }),
40801                     name : i,
40802                     displayField:'val',
40803                     typeAhead: false,
40804                     mode: 'local',
40805                     editable : false,
40806                     triggerAction: 'all',
40807                     emptyText:'Select',
40808                     selectOnFocus:true,
40809                     width: item.width ? item.width  : 130,
40810                     listeners : {
40811                         'select': function(c, r, i) {
40812                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40813                         }
40814                     }
40815
40816                 }));
40817                 continue;
40818                     
40819                  
40820                 
40821                 tb.addField( new Roo.form.TextField({
40822                     name: i,
40823                     width: 100,
40824                     //allowBlank:false,
40825                     value: ''
40826                 }));
40827                 continue;
40828             }
40829             tb.addField( new Roo.form.TextField({
40830                 name: i,
40831                 width: item.width,
40832                 //allowBlank:true,
40833                 value: '',
40834                 listeners: {
40835                     'change' : function(f, nv, ov) {
40836                         tb.selectedNode.setAttribute(f.name, nv);
40837                     }
40838                 }
40839             }));
40840              
40841         }
40842         tb.el.on('click', function(e){
40843             e.preventDefault(); // what does this do?
40844         });
40845         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40846         tb.el.hide();
40847         tb.name = nm;
40848         // dont need to disable them... as they will get hidden
40849         return tb;
40850          
40851         
40852     },
40853     buildFooter : function()
40854     {
40855         
40856         var fel = this.editor.wrap.createChild();
40857         this.footer = new Roo.Toolbar(fel);
40858         // toolbar has scrolly on left / right?
40859         var footDisp= new Roo.Toolbar.Fill();
40860         var _t = this;
40861         this.footer.add(
40862             {
40863                 text : '&lt;',
40864                 xtype: 'Button',
40865                 handler : function() {
40866                     _t.footDisp.scrollTo('left',0,true)
40867                 }
40868             }
40869         );
40870         this.footer.add( footDisp );
40871         this.footer.add( 
40872             {
40873                 text : '&gt;',
40874                 xtype: 'Button',
40875                 handler : function() {
40876                     // no animation..
40877                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40878                 }
40879             }
40880         );
40881         var fel = Roo.get(footDisp.el);
40882         fel.addClass('x-editor-context');
40883         this.footDispWrap = fel; 
40884         this.footDispWrap.overflow  = 'hidden';
40885         
40886         this.footDisp = fel.createChild();
40887         this.footDispWrap.on('click', this.onContextClick, this)
40888         
40889         
40890     },
40891     onContextClick : function (ev,dom)
40892     {
40893         ev.preventDefault();
40894         var  cn = dom.className;
40895         Roo.log(cn);
40896         if (!cn.match(/x-ed-loc-/)) {
40897             return;
40898         }
40899         var n = cn.split('-').pop();
40900         var ans = this.footerEls;
40901         var sel = ans[n];
40902         
40903          // pick
40904         var range = this.editor.createRange();
40905         
40906         range.selectNodeContents(sel);
40907         //range.selectNode(sel);
40908         
40909         
40910         var selection = this.editor.getSelection();
40911         selection.removeAllRanges();
40912         selection.addRange(range);
40913         
40914         
40915         
40916         this.updateToolbar(null, null, sel);
40917         
40918         
40919     }
40920     
40921     
40922     
40923     
40924     
40925 });
40926
40927
40928
40929
40930
40931 /*
40932  * Based on:
40933  * Ext JS Library 1.1.1
40934  * Copyright(c) 2006-2007, Ext JS, LLC.
40935  *
40936  * Originally Released Under LGPL - original licence link has changed is not relivant.
40937  *
40938  * Fork - LGPL
40939  * <script type="text/javascript">
40940  */
40941  
40942 /**
40943  * @class Roo.form.BasicForm
40944  * @extends Roo.util.Observable
40945  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40946  * @constructor
40947  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40948  * @param {Object} config Configuration options
40949  */
40950 Roo.form.BasicForm = function(el, config){
40951     this.allItems = [];
40952     this.childForms = [];
40953     Roo.apply(this, config);
40954     /*
40955      * The Roo.form.Field items in this form.
40956      * @type MixedCollection
40957      */
40958      
40959      
40960     this.items = new Roo.util.MixedCollection(false, function(o){
40961         return o.id || (o.id = Roo.id());
40962     });
40963     this.addEvents({
40964         /**
40965          * @event beforeaction
40966          * Fires before any action is performed. Return false to cancel the action.
40967          * @param {Form} this
40968          * @param {Action} action The action to be performed
40969          */
40970         beforeaction: true,
40971         /**
40972          * @event actionfailed
40973          * Fires when an action fails.
40974          * @param {Form} this
40975          * @param {Action} action The action that failed
40976          */
40977         actionfailed : true,
40978         /**
40979          * @event actioncomplete
40980          * Fires when an action is completed.
40981          * @param {Form} this
40982          * @param {Action} action The action that completed
40983          */
40984         actioncomplete : true
40985     });
40986     if(el){
40987         this.initEl(el);
40988     }
40989     Roo.form.BasicForm.superclass.constructor.call(this);
40990 };
40991
40992 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40993     /**
40994      * @cfg {String} method
40995      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40996      */
40997     /**
40998      * @cfg {DataReader} reader
40999      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41000      * This is optional as there is built-in support for processing JSON.
41001      */
41002     /**
41003      * @cfg {DataReader} errorReader
41004      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41005      * This is completely optional as there is built-in support for processing JSON.
41006      */
41007     /**
41008      * @cfg {String} url
41009      * The URL to use for form actions if one isn't supplied in the action options.
41010      */
41011     /**
41012      * @cfg {Boolean} fileUpload
41013      * Set to true if this form is a file upload.
41014      */
41015      
41016     /**
41017      * @cfg {Object} baseParams
41018      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41019      */
41020      /**
41021      
41022     /**
41023      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41024      */
41025     timeout: 30,
41026
41027     // private
41028     activeAction : null,
41029
41030     /**
41031      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41032      * or setValues() data instead of when the form was first created.
41033      */
41034     trackResetOnLoad : false,
41035     
41036     
41037     /**
41038      * childForms - used for multi-tab forms
41039      * @type {Array}
41040      */
41041     childForms : false,
41042     
41043     /**
41044      * allItems - full list of fields.
41045      * @type {Array}
41046      */
41047     allItems : false,
41048     
41049     /**
41050      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41051      * element by passing it or its id or mask the form itself by passing in true.
41052      * @type Mixed
41053      */
41054     waitMsgTarget : false,
41055
41056     // private
41057     initEl : function(el){
41058         this.el = Roo.get(el);
41059         this.id = this.el.id || Roo.id();
41060         this.el.on('submit', this.onSubmit, this);
41061         this.el.addClass('x-form');
41062     },
41063
41064     // private
41065     onSubmit : function(e){
41066         e.stopEvent();
41067     },
41068
41069     /**
41070      * Returns true if client-side validation on the form is successful.
41071      * @return Boolean
41072      */
41073     isValid : function(){
41074         var valid = true;
41075         this.items.each(function(f){
41076            if(!f.validate()){
41077                valid = false;
41078            }
41079         });
41080         return valid;
41081     },
41082
41083     /**
41084      * Returns true if any fields in this form have changed since their original load.
41085      * @return Boolean
41086      */
41087     isDirty : function(){
41088         var dirty = false;
41089         this.items.each(function(f){
41090            if(f.isDirty()){
41091                dirty = true;
41092                return false;
41093            }
41094         });
41095         return dirty;
41096     },
41097
41098     /**
41099      * Performs a predefined action (submit or load) or custom actions you define on this form.
41100      * @param {String} actionName The name of the action type
41101      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41102      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41103      * accept other config options):
41104      * <pre>
41105 Property          Type             Description
41106 ----------------  ---------------  ----------------------------------------------------------------------------------
41107 url               String           The url for the action (defaults to the form's url)
41108 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41109 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41110 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41111                                    validate the form on the client (defaults to false)
41112      * </pre>
41113      * @return {BasicForm} this
41114      */
41115     doAction : function(action, options){
41116         if(typeof action == 'string'){
41117             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41118         }
41119         if(this.fireEvent('beforeaction', this, action) !== false){
41120             this.beforeAction(action);
41121             action.run.defer(100, action);
41122         }
41123         return this;
41124     },
41125
41126     /**
41127      * Shortcut to do a submit action.
41128      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41129      * @return {BasicForm} this
41130      */
41131     submit : function(options){
41132         this.doAction('submit', options);
41133         return this;
41134     },
41135
41136     /**
41137      * Shortcut to do a load action.
41138      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41139      * @return {BasicForm} this
41140      */
41141     load : function(options){
41142         this.doAction('load', options);
41143         return this;
41144     },
41145
41146     /**
41147      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41148      * @param {Record} record The record to edit
41149      * @return {BasicForm} this
41150      */
41151     updateRecord : function(record){
41152         record.beginEdit();
41153         var fs = record.fields;
41154         fs.each(function(f){
41155             var field = this.findField(f.name);
41156             if(field){
41157                 record.set(f.name, field.getValue());
41158             }
41159         }, this);
41160         record.endEdit();
41161         return this;
41162     },
41163
41164     /**
41165      * Loads an Roo.data.Record into this form.
41166      * @param {Record} record The record to load
41167      * @return {BasicForm} this
41168      */
41169     loadRecord : function(record){
41170         this.setValues(record.data);
41171         return this;
41172     },
41173
41174     // private
41175     beforeAction : function(action){
41176         var o = action.options;
41177         
41178        
41179         if(this.waitMsgTarget === true){
41180             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41181         }else if(this.waitMsgTarget){
41182             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41183             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41184         }else {
41185             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41186         }
41187          
41188     },
41189
41190     // private
41191     afterAction : function(action, success){
41192         this.activeAction = null;
41193         var o = action.options;
41194         
41195         if(this.waitMsgTarget === true){
41196             this.el.unmask();
41197         }else if(this.waitMsgTarget){
41198             this.waitMsgTarget.unmask();
41199         }else{
41200             Roo.MessageBox.updateProgress(1);
41201             Roo.MessageBox.hide();
41202         }
41203          
41204         if(success){
41205             if(o.reset){
41206                 this.reset();
41207             }
41208             Roo.callback(o.success, o.scope, [this, action]);
41209             this.fireEvent('actioncomplete', this, action);
41210             
41211         }else{
41212             Roo.callback(o.failure, o.scope, [this, action]);
41213             // show an error message if no failed handler is set..
41214             if (!this.hasListener('actionfailed')) {
41215                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41216             }
41217             
41218             this.fireEvent('actionfailed', this, action);
41219         }
41220         
41221     },
41222
41223     /**
41224      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41225      * @param {String} id The value to search for
41226      * @return Field
41227      */
41228     findField : function(id){
41229         var field = this.items.get(id);
41230         if(!field){
41231             this.items.each(function(f){
41232                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41233                     field = f;
41234                     return false;
41235                 }
41236             });
41237         }
41238         return field || null;
41239     },
41240
41241     /**
41242      * Add a secondary form to this one, 
41243      * Used to provide tabbed forms. One form is primary, with hidden values 
41244      * which mirror the elements from the other forms.
41245      * 
41246      * @param {Roo.form.Form} form to add.
41247      * 
41248      */
41249     addForm : function(form)
41250     {
41251        
41252         if (this.childForms.indexOf(form) > -1) {
41253             // already added..
41254             return;
41255         }
41256         this.childForms.push(form);
41257         var n = '';
41258         Roo.each(form.allItems, function (fe) {
41259             
41260             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41261             if (this.findField(n)) { // already added..
41262                 return;
41263             }
41264             var add = new Roo.form.Hidden({
41265                 name : n
41266             });
41267             add.render(this.el);
41268             
41269             this.add( add );
41270         }, this);
41271         
41272     },
41273     /**
41274      * Mark fields in this form invalid in bulk.
41275      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41276      * @return {BasicForm} this
41277      */
41278     markInvalid : function(errors){
41279         if(errors instanceof Array){
41280             for(var i = 0, len = errors.length; i < len; i++){
41281                 var fieldError = errors[i];
41282                 var f = this.findField(fieldError.id);
41283                 if(f){
41284                     f.markInvalid(fieldError.msg);
41285                 }
41286             }
41287         }else{
41288             var field, id;
41289             for(id in errors){
41290                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41291                     field.markInvalid(errors[id]);
41292                 }
41293             }
41294         }
41295         Roo.each(this.childForms || [], function (f) {
41296             f.markInvalid(errors);
41297         });
41298         
41299         return this;
41300     },
41301
41302     /**
41303      * Set values for fields in this form in bulk.
41304      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41305      * @return {BasicForm} this
41306      */
41307     setValues : function(values){
41308         if(values instanceof Array){ // array of objects
41309             for(var i = 0, len = values.length; i < len; i++){
41310                 var v = values[i];
41311                 var f = this.findField(v.id);
41312                 if(f){
41313                     f.setValue(v.value);
41314                     if(this.trackResetOnLoad){
41315                         f.originalValue = f.getValue();
41316                     }
41317                 }
41318             }
41319         }else{ // object hash
41320             var field, id;
41321             for(id in values){
41322                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41323                     
41324                     if (field.setFromData && 
41325                         field.valueField && 
41326                         field.displayField &&
41327                         // combos' with local stores can 
41328                         // be queried via setValue()
41329                         // to set their value..
41330                         (field.store && !field.store.isLocal)
41331                         ) {
41332                         // it's a combo
41333                         var sd = { };
41334                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41335                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41336                         field.setFromData(sd);
41337                         
41338                     } else {
41339                         field.setValue(values[id]);
41340                     }
41341                     
41342                     
41343                     if(this.trackResetOnLoad){
41344                         field.originalValue = field.getValue();
41345                     }
41346                 }
41347             }
41348         }
41349          
41350         Roo.each(this.childForms || [], function (f) {
41351             f.setValues(values);
41352         });
41353                 
41354         return this;
41355     },
41356
41357     /**
41358      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41359      * they are returned as an array.
41360      * @param {Boolean} asString
41361      * @return {Object}
41362      */
41363     getValues : function(asString){
41364         if (this.childForms) {
41365             // copy values from the child forms
41366             Roo.each(this.childForms, function (f) {
41367                 this.setValues(f.getValues());
41368             }, this);
41369         }
41370         
41371         
41372         
41373         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41374         if(asString === true){
41375             return fs;
41376         }
41377         return Roo.urlDecode(fs);
41378     },
41379     
41380     /**
41381      * Returns the fields in this form as an object with key/value pairs. 
41382      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41383      * @return {Object}
41384      */
41385     getFieldValues : function()
41386     {
41387         if (this.childForms) {
41388             // copy values from the child forms
41389             Roo.each(this.childForms, function (f) {
41390                 this.setValues(f.getValues());
41391             }, this);
41392         }
41393         
41394         var ret = {};
41395         this.items.each(function(f){
41396             if (!f.getName()) {
41397                 return;
41398             }
41399             var v = f.getValue();
41400             if ((typeof(v) == 'object') && f.getRawValue) {
41401                 v = f.getRawValue() ; // dates..
41402             }
41403             ret[f.getName()] = v;
41404         });
41405         
41406         return ret;
41407     },
41408
41409     /**
41410      * Clears all invalid messages in this form.
41411      * @return {BasicForm} this
41412      */
41413     clearInvalid : function(){
41414         this.items.each(function(f){
41415            f.clearInvalid();
41416         });
41417         
41418         Roo.each(this.childForms || [], function (f) {
41419             f.clearInvalid();
41420         });
41421         
41422         
41423         return this;
41424     },
41425
41426     /**
41427      * Resets this form.
41428      * @return {BasicForm} this
41429      */
41430     reset : function(){
41431         this.items.each(function(f){
41432             f.reset();
41433         });
41434         
41435         Roo.each(this.childForms || [], function (f) {
41436             f.reset();
41437         });
41438        
41439         
41440         return this;
41441     },
41442
41443     /**
41444      * Add Roo.form components to this form.
41445      * @param {Field} field1
41446      * @param {Field} field2 (optional)
41447      * @param {Field} etc (optional)
41448      * @return {BasicForm} this
41449      */
41450     add : function(){
41451         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41452         return this;
41453     },
41454
41455
41456     /**
41457      * Removes a field from the items collection (does NOT remove its markup).
41458      * @param {Field} field
41459      * @return {BasicForm} this
41460      */
41461     remove : function(field){
41462         this.items.remove(field);
41463         return this;
41464     },
41465
41466     /**
41467      * Looks at the fields in this form, checks them for an id attribute,
41468      * and calls applyTo on the existing dom element with that id.
41469      * @return {BasicForm} this
41470      */
41471     render : function(){
41472         this.items.each(function(f){
41473             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41474                 f.applyTo(f.id);
41475             }
41476         });
41477         return this;
41478     },
41479
41480     /**
41481      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41482      * @param {Object} values
41483      * @return {BasicForm} this
41484      */
41485     applyToFields : function(o){
41486         this.items.each(function(f){
41487            Roo.apply(f, o);
41488         });
41489         return this;
41490     },
41491
41492     /**
41493      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41494      * @param {Object} values
41495      * @return {BasicForm} this
41496      */
41497     applyIfToFields : function(o){
41498         this.items.each(function(f){
41499            Roo.applyIf(f, o);
41500         });
41501         return this;
41502     }
41503 });
41504
41505 // back compat
41506 Roo.BasicForm = Roo.form.BasicForm;/*
41507  * Based on:
41508  * Ext JS Library 1.1.1
41509  * Copyright(c) 2006-2007, Ext JS, LLC.
41510  *
41511  * Originally Released Under LGPL - original licence link has changed is not relivant.
41512  *
41513  * Fork - LGPL
41514  * <script type="text/javascript">
41515  */
41516
41517 /**
41518  * @class Roo.form.Form
41519  * @extends Roo.form.BasicForm
41520  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41521  * @constructor
41522  * @param {Object} config Configuration options
41523  */
41524 Roo.form.Form = function(config){
41525     var xitems =  [];
41526     if (config.items) {
41527         xitems = config.items;
41528         delete config.items;
41529     }
41530    
41531     
41532     Roo.form.Form.superclass.constructor.call(this, null, config);
41533     this.url = this.url || this.action;
41534     if(!this.root){
41535         this.root = new Roo.form.Layout(Roo.applyIf({
41536             id: Roo.id()
41537         }, config));
41538     }
41539     this.active = this.root;
41540     /**
41541      * Array of all the buttons that have been added to this form via {@link addButton}
41542      * @type Array
41543      */
41544     this.buttons = [];
41545     this.allItems = [];
41546     this.addEvents({
41547         /**
41548          * @event clientvalidation
41549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41550          * @param {Form} this
41551          * @param {Boolean} valid true if the form has passed client-side validation
41552          */
41553         clientvalidation: true,
41554         /**
41555          * @event rendered
41556          * Fires when the form is rendered
41557          * @param {Roo.form.Form} form
41558          */
41559         rendered : true
41560     });
41561     
41562     if (this.progressUrl) {
41563             // push a hidden field onto the list of fields..
41564             this.addxtype( {
41565                     xns: Roo.form, 
41566                     xtype : 'Hidden', 
41567                     name : 'UPLOAD_IDENTIFIER' 
41568             });
41569         }
41570         
41571     
41572     Roo.each(xitems, this.addxtype, this);
41573     
41574     
41575     
41576 };
41577
41578 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41579     /**
41580      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41581      */
41582     /**
41583      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41584      */
41585     /**
41586      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41587      */
41588     buttonAlign:'center',
41589
41590     /**
41591      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41592      */
41593     minButtonWidth:75,
41594
41595     /**
41596      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41597      * This property cascades to child containers if not set.
41598      */
41599     labelAlign:'left',
41600
41601     /**
41602      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41603      * fires a looping event with that state. This is required to bind buttons to the valid
41604      * state using the config value formBind:true on the button.
41605      */
41606     monitorValid : false,
41607
41608     /**
41609      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41610      */
41611     monitorPoll : 200,
41612     
41613     /**
41614      * @cfg {String} progressUrl - Url to return progress data 
41615      */
41616     
41617     progressUrl : false,
41618   
41619     /**
41620      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41621      * fields are added and the column is closed. If no fields are passed the column remains open
41622      * until end() is called.
41623      * @param {Object} config The config to pass to the column
41624      * @param {Field} field1 (optional)
41625      * @param {Field} field2 (optional)
41626      * @param {Field} etc (optional)
41627      * @return Column The column container object
41628      */
41629     column : function(c){
41630         var col = new Roo.form.Column(c);
41631         this.start(col);
41632         if(arguments.length > 1){ // duplicate code required because of Opera
41633             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41634             this.end();
41635         }
41636         return col;
41637     },
41638
41639     /**
41640      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41641      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41642      * until end() is called.
41643      * @param {Object} config The config to pass to the fieldset
41644      * @param {Field} field1 (optional)
41645      * @param {Field} field2 (optional)
41646      * @param {Field} etc (optional)
41647      * @return FieldSet The fieldset container object
41648      */
41649     fieldset : function(c){
41650         var fs = new Roo.form.FieldSet(c);
41651         this.start(fs);
41652         if(arguments.length > 1){ // duplicate code required because of Opera
41653             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41654             this.end();
41655         }
41656         return fs;
41657     },
41658
41659     /**
41660      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41661      * fields are added and the container is closed. If no fields are passed the container remains open
41662      * until end() is called.
41663      * @param {Object} config The config to pass to the Layout
41664      * @param {Field} field1 (optional)
41665      * @param {Field} field2 (optional)
41666      * @param {Field} etc (optional)
41667      * @return Layout The container object
41668      */
41669     container : function(c){
41670         var l = new Roo.form.Layout(c);
41671         this.start(l);
41672         if(arguments.length > 1){ // duplicate code required because of Opera
41673             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41674             this.end();
41675         }
41676         return l;
41677     },
41678
41679     /**
41680      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41681      * @param {Object} container A Roo.form.Layout or subclass of Layout
41682      * @return {Form} this
41683      */
41684     start : function(c){
41685         // cascade label info
41686         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41687         this.active.stack.push(c);
41688         c.ownerCt = this.active;
41689         this.active = c;
41690         return this;
41691     },
41692
41693     /**
41694      * Closes the current open container
41695      * @return {Form} this
41696      */
41697     end : function(){
41698         if(this.active == this.root){
41699             return this;
41700         }
41701         this.active = this.active.ownerCt;
41702         return this;
41703     },
41704
41705     /**
41706      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41707      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41708      * as the label of the field.
41709      * @param {Field} field1
41710      * @param {Field} field2 (optional)
41711      * @param {Field} etc. (optional)
41712      * @return {Form} this
41713      */
41714     add : function(){
41715         this.active.stack.push.apply(this.active.stack, arguments);
41716         this.allItems.push.apply(this.allItems,arguments);
41717         var r = [];
41718         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41719             if(a[i].isFormField){
41720                 r.push(a[i]);
41721             }
41722         }
41723         if(r.length > 0){
41724             Roo.form.Form.superclass.add.apply(this, r);
41725         }
41726         return this;
41727     },
41728     
41729
41730     
41731     
41732     
41733      /**
41734      * Find any element that has been added to a form, using it's ID or name
41735      * This can include framesets, columns etc. along with regular fields..
41736      * @param {String} id - id or name to find.
41737      
41738      * @return {Element} e - or false if nothing found.
41739      */
41740     findbyId : function(id)
41741     {
41742         var ret = false;
41743         if (!id) {
41744             return ret;
41745         }
41746         Roo.each(this.allItems, function(f){
41747             if (f.id == id || f.name == id ){
41748                 ret = f;
41749                 return false;
41750             }
41751         });
41752         return ret;
41753     },
41754
41755     
41756     
41757     /**
41758      * Render this form into the passed container. This should only be called once!
41759      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41760      * @return {Form} this
41761      */
41762     render : function(ct)
41763     {
41764         
41765         
41766         
41767         ct = Roo.get(ct);
41768         var o = this.autoCreate || {
41769             tag: 'form',
41770             method : this.method || 'POST',
41771             id : this.id || Roo.id()
41772         };
41773         this.initEl(ct.createChild(o));
41774
41775         this.root.render(this.el);
41776         
41777        
41778              
41779         this.items.each(function(f){
41780             f.render('x-form-el-'+f.id);
41781         });
41782
41783         if(this.buttons.length > 0){
41784             // tables are required to maintain order and for correct IE layout
41785             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41786                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41787                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41788             }}, null, true);
41789             var tr = tb.getElementsByTagName('tr')[0];
41790             for(var i = 0, len = this.buttons.length; i < len; i++) {
41791                 var b = this.buttons[i];
41792                 var td = document.createElement('td');
41793                 td.className = 'x-form-btn-td';
41794                 b.render(tr.appendChild(td));
41795             }
41796         }
41797         if(this.monitorValid){ // initialize after render
41798             this.startMonitoring();
41799         }
41800         this.fireEvent('rendered', this);
41801         return this;
41802     },
41803
41804     /**
41805      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41806      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41807      * object or a valid Roo.DomHelper element config
41808      * @param {Function} handler The function called when the button is clicked
41809      * @param {Object} scope (optional) The scope of the handler function
41810      * @return {Roo.Button}
41811      */
41812     addButton : function(config, handler, scope){
41813         var bc = {
41814             handler: handler,
41815             scope: scope,
41816             minWidth: this.minButtonWidth,
41817             hideParent:true
41818         };
41819         if(typeof config == "string"){
41820             bc.text = config;
41821         }else{
41822             Roo.apply(bc, config);
41823         }
41824         var btn = new Roo.Button(null, bc);
41825         this.buttons.push(btn);
41826         return btn;
41827     },
41828
41829      /**
41830      * Adds a series of form elements (using the xtype property as the factory method.
41831      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41832      * @param {Object} config 
41833      */
41834     
41835     addxtype : function()
41836     {
41837         var ar = Array.prototype.slice.call(arguments, 0);
41838         var ret = false;
41839         for(var i = 0; i < ar.length; i++) {
41840             if (!ar[i]) {
41841                 continue; // skip -- if this happends something invalid got sent, we 
41842                 // should ignore it, as basically that interface element will not show up
41843                 // and that should be pretty obvious!!
41844             }
41845             
41846             if (Roo.form[ar[i].xtype]) {
41847                 ar[i].form = this;
41848                 var fe = Roo.factory(ar[i], Roo.form);
41849                 if (!ret) {
41850                     ret = fe;
41851                 }
41852                 fe.form = this;
41853                 if (fe.store) {
41854                     fe.store.form = this;
41855                 }
41856                 if (fe.isLayout) {  
41857                          
41858                     this.start(fe);
41859                     this.allItems.push(fe);
41860                     if (fe.items && fe.addxtype) {
41861                         fe.addxtype.apply(fe, fe.items);
41862                         delete fe.items;
41863                     }
41864                      this.end();
41865                     continue;
41866                 }
41867                 
41868                 
41869                  
41870                 this.add(fe);
41871               //  console.log('adding ' + ar[i].xtype);
41872             }
41873             if (ar[i].xtype == 'Button') {  
41874                 //console.log('adding button');
41875                 //console.log(ar[i]);
41876                 this.addButton(ar[i]);
41877                 this.allItems.push(fe);
41878                 continue;
41879             }
41880             
41881             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41882                 alert('end is not supported on xtype any more, use items');
41883             //    this.end();
41884             //    //console.log('adding end');
41885             }
41886             
41887         }
41888         return ret;
41889     },
41890     
41891     /**
41892      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41893      * option "monitorValid"
41894      */
41895     startMonitoring : function(){
41896         if(!this.bound){
41897             this.bound = true;
41898             Roo.TaskMgr.start({
41899                 run : this.bindHandler,
41900                 interval : this.monitorPoll || 200,
41901                 scope: this
41902             });
41903         }
41904     },
41905
41906     /**
41907      * Stops monitoring of the valid state of this form
41908      */
41909     stopMonitoring : function(){
41910         this.bound = false;
41911     },
41912
41913     // private
41914     bindHandler : function(){
41915         if(!this.bound){
41916             return false; // stops binding
41917         }
41918         var valid = true;
41919         this.items.each(function(f){
41920             if(!f.isValid(true)){
41921                 valid = false;
41922                 return false;
41923             }
41924         });
41925         for(var i = 0, len = this.buttons.length; i < len; i++){
41926             var btn = this.buttons[i];
41927             if(btn.formBind === true && btn.disabled === valid){
41928                 btn.setDisabled(!valid);
41929             }
41930         }
41931         this.fireEvent('clientvalidation', this, valid);
41932     }
41933     
41934     
41935     
41936     
41937     
41938     
41939     
41940     
41941 });
41942
41943
41944 // back compat
41945 Roo.Form = Roo.form.Form;
41946 /*
41947  * Based on:
41948  * Ext JS Library 1.1.1
41949  * Copyright(c) 2006-2007, Ext JS, LLC.
41950  *
41951  * Originally Released Under LGPL - original licence link has changed is not relivant.
41952  *
41953  * Fork - LGPL
41954  * <script type="text/javascript">
41955  */
41956  
41957  /**
41958  * @class Roo.form.Action
41959  * Internal Class used to handle form actions
41960  * @constructor
41961  * @param {Roo.form.BasicForm} el The form element or its id
41962  * @param {Object} config Configuration options
41963  */
41964  
41965  
41966 // define the action interface
41967 Roo.form.Action = function(form, options){
41968     this.form = form;
41969     this.options = options || {};
41970 };
41971 /**
41972  * Client Validation Failed
41973  * @const 
41974  */
41975 Roo.form.Action.CLIENT_INVALID = 'client';
41976 /**
41977  * Server Validation Failed
41978  * @const 
41979  */
41980  Roo.form.Action.SERVER_INVALID = 'server';
41981  /**
41982  * Connect to Server Failed
41983  * @const 
41984  */
41985 Roo.form.Action.CONNECT_FAILURE = 'connect';
41986 /**
41987  * Reading Data from Server Failed
41988  * @const 
41989  */
41990 Roo.form.Action.LOAD_FAILURE = 'load';
41991
41992 Roo.form.Action.prototype = {
41993     type : 'default',
41994     failureType : undefined,
41995     response : undefined,
41996     result : undefined,
41997
41998     // interface method
41999     run : function(options){
42000
42001     },
42002
42003     // interface method
42004     success : function(response){
42005
42006     },
42007
42008     // interface method
42009     handleResponse : function(response){
42010
42011     },
42012
42013     // default connection failure
42014     failure : function(response){
42015         
42016         this.response = response;
42017         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42018         this.form.afterAction(this, false);
42019     },
42020
42021     processResponse : function(response){
42022         this.response = response;
42023         if(!response.responseText){
42024             return true;
42025         }
42026         this.result = this.handleResponse(response);
42027         return this.result;
42028     },
42029
42030     // utility functions used internally
42031     getUrl : function(appendParams){
42032         var url = this.options.url || this.form.url || this.form.el.dom.action;
42033         if(appendParams){
42034             var p = this.getParams();
42035             if(p){
42036                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42037             }
42038         }
42039         return url;
42040     },
42041
42042     getMethod : function(){
42043         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42044     },
42045
42046     getParams : function(){
42047         var bp = this.form.baseParams;
42048         var p = this.options.params;
42049         if(p){
42050             if(typeof p == "object"){
42051                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42052             }else if(typeof p == 'string' && bp){
42053                 p += '&' + Roo.urlEncode(bp);
42054             }
42055         }else if(bp){
42056             p = Roo.urlEncode(bp);
42057         }
42058         return p;
42059     },
42060
42061     createCallback : function(){
42062         return {
42063             success: this.success,
42064             failure: this.failure,
42065             scope: this,
42066             timeout: (this.form.timeout*1000),
42067             upload: this.form.fileUpload ? this.success : undefined
42068         };
42069     }
42070 };
42071
42072 Roo.form.Action.Submit = function(form, options){
42073     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42074 };
42075
42076 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42077     type : 'submit',
42078
42079     haveProgress : false,
42080     uploadComplete : false,
42081     
42082     // uploadProgress indicator.
42083     uploadProgress : function()
42084     {
42085         if (!this.form.progressUrl) {
42086             return;
42087         }
42088         
42089         if (!this.haveProgress) {
42090             Roo.MessageBox.progress("Uploading", "Uploading");
42091         }
42092         if (this.uploadComplete) {
42093            Roo.MessageBox.hide();
42094            return;
42095         }
42096         
42097         this.haveProgress = true;
42098    
42099         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42100         
42101         var c = new Roo.data.Connection();
42102         c.request({
42103             url : this.form.progressUrl,
42104             params: {
42105                 id : uid
42106             },
42107             method: 'GET',
42108             success : function(req){
42109                //console.log(data);
42110                 var rdata = false;
42111                 var edata;
42112                 try  {
42113                    rdata = Roo.decode(req.responseText)
42114                 } catch (e) {
42115                     Roo.log("Invalid data from server..");
42116                     Roo.log(edata);
42117                     return;
42118                 }
42119                 if (!rdata || !rdata.success) {
42120                     Roo.log(rdata);
42121                     return;
42122                 }
42123                 var data = rdata.data;
42124                 
42125                 if (this.uploadComplete) {
42126                    Roo.MessageBox.hide();
42127                    return;
42128                 }
42129                    
42130                 if (data){
42131                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42132                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42133                     );
42134                 }
42135                 this.uploadProgress.defer(2000,this);
42136             },
42137        
42138             failure: function(data) {
42139                 Roo.log('progress url failed ');
42140                 Roo.log(data);
42141             },
42142             scope : this
42143         });
42144            
42145     },
42146     
42147     
42148     run : function()
42149     {
42150         // run get Values on the form, so it syncs any secondary forms.
42151         this.form.getValues();
42152         
42153         var o = this.options;
42154         var method = this.getMethod();
42155         var isPost = method == 'POST';
42156         if(o.clientValidation === false || this.form.isValid()){
42157             
42158             if (this.form.progressUrl) {
42159                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42160                     (new Date() * 1) + '' + Math.random());
42161                     
42162             } 
42163             
42164             
42165             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42166                 form:this.form.el.dom,
42167                 url:this.getUrl(!isPost),
42168                 method: method,
42169                 params:isPost ? this.getParams() : null,
42170                 isUpload: this.form.fileUpload
42171             }));
42172             
42173             this.uploadProgress();
42174
42175         }else if (o.clientValidation !== false){ // client validation failed
42176             this.failureType = Roo.form.Action.CLIENT_INVALID;
42177             this.form.afterAction(this, false);
42178         }
42179     },
42180
42181     success : function(response)
42182     {
42183         this.uploadComplete= true;
42184         if (this.haveProgress) {
42185             Roo.MessageBox.hide();
42186         }
42187         
42188         
42189         var result = this.processResponse(response);
42190         if(result === true || result.success){
42191             this.form.afterAction(this, true);
42192             return;
42193         }
42194         if(result.errors){
42195             this.form.markInvalid(result.errors);
42196             this.failureType = Roo.form.Action.SERVER_INVALID;
42197         }
42198         this.form.afterAction(this, false);
42199     },
42200     failure : function(response)
42201     {
42202         this.uploadComplete= true;
42203         if (this.haveProgress) {
42204             Roo.MessageBox.hide();
42205         }
42206         
42207         
42208         this.response = response;
42209         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42210         this.form.afterAction(this, false);
42211     },
42212     
42213     handleResponse : function(response){
42214         if(this.form.errorReader){
42215             var rs = this.form.errorReader.read(response);
42216             var errors = [];
42217             if(rs.records){
42218                 for(var i = 0, len = rs.records.length; i < len; i++) {
42219                     var r = rs.records[i];
42220                     errors[i] = r.data;
42221                 }
42222             }
42223             if(errors.length < 1){
42224                 errors = null;
42225             }
42226             return {
42227                 success : rs.success,
42228                 errors : errors
42229             };
42230         }
42231         var ret = false;
42232         try {
42233             ret = Roo.decode(response.responseText);
42234         } catch (e) {
42235             ret = {
42236                 success: false,
42237                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42238                 errors : []
42239             };
42240         }
42241         return ret;
42242         
42243     }
42244 });
42245
42246
42247 Roo.form.Action.Load = function(form, options){
42248     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42249     this.reader = this.form.reader;
42250 };
42251
42252 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42253     type : 'load',
42254
42255     run : function(){
42256         
42257         Roo.Ajax.request(Roo.apply(
42258                 this.createCallback(), {
42259                     method:this.getMethod(),
42260                     url:this.getUrl(false),
42261                     params:this.getParams()
42262         }));
42263     },
42264
42265     success : function(response){
42266         
42267         var result = this.processResponse(response);
42268         if(result === true || !result.success || !result.data){
42269             this.failureType = Roo.form.Action.LOAD_FAILURE;
42270             this.form.afterAction(this, false);
42271             return;
42272         }
42273         this.form.clearInvalid();
42274         this.form.setValues(result.data);
42275         this.form.afterAction(this, true);
42276     },
42277
42278     handleResponse : function(response){
42279         if(this.form.reader){
42280             var rs = this.form.reader.read(response);
42281             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42282             return {
42283                 success : rs.success,
42284                 data : data
42285             };
42286         }
42287         return Roo.decode(response.responseText);
42288     }
42289 });
42290
42291 Roo.form.Action.ACTION_TYPES = {
42292     'load' : Roo.form.Action.Load,
42293     'submit' : Roo.form.Action.Submit
42294 };/*
42295  * Based on:
42296  * Ext JS Library 1.1.1
42297  * Copyright(c) 2006-2007, Ext JS, LLC.
42298  *
42299  * Originally Released Under LGPL - original licence link has changed is not relivant.
42300  *
42301  * Fork - LGPL
42302  * <script type="text/javascript">
42303  */
42304  
42305 /**
42306  * @class Roo.form.Layout
42307  * @extends Roo.Component
42308  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42309  * @constructor
42310  * @param {Object} config Configuration options
42311  */
42312 Roo.form.Layout = function(config){
42313     var xitems = [];
42314     if (config.items) {
42315         xitems = config.items;
42316         delete config.items;
42317     }
42318     Roo.form.Layout.superclass.constructor.call(this, config);
42319     this.stack = [];
42320     Roo.each(xitems, this.addxtype, this);
42321      
42322 };
42323
42324 Roo.extend(Roo.form.Layout, Roo.Component, {
42325     /**
42326      * @cfg {String/Object} autoCreate
42327      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42328      */
42329     /**
42330      * @cfg {String/Object/Function} style
42331      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42332      * a function which returns such a specification.
42333      */
42334     /**
42335      * @cfg {String} labelAlign
42336      * Valid values are "left," "top" and "right" (defaults to "left")
42337      */
42338     /**
42339      * @cfg {Number} labelWidth
42340      * Fixed width in pixels of all field labels (defaults to undefined)
42341      */
42342     /**
42343      * @cfg {Boolean} clear
42344      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42345      */
42346     clear : true,
42347     /**
42348      * @cfg {String} labelSeparator
42349      * The separator to use after field labels (defaults to ':')
42350      */
42351     labelSeparator : ':',
42352     /**
42353      * @cfg {Boolean} hideLabels
42354      * True to suppress the display of field labels in this layout (defaults to false)
42355      */
42356     hideLabels : false,
42357
42358     // private
42359     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42360     
42361     isLayout : true,
42362     
42363     // private
42364     onRender : function(ct, position){
42365         if(this.el){ // from markup
42366             this.el = Roo.get(this.el);
42367         }else {  // generate
42368             var cfg = this.getAutoCreate();
42369             this.el = ct.createChild(cfg, position);
42370         }
42371         if(this.style){
42372             this.el.applyStyles(this.style);
42373         }
42374         if(this.labelAlign){
42375             this.el.addClass('x-form-label-'+this.labelAlign);
42376         }
42377         if(this.hideLabels){
42378             this.labelStyle = "display:none";
42379             this.elementStyle = "padding-left:0;";
42380         }else{
42381             if(typeof this.labelWidth == 'number'){
42382                 this.labelStyle = "width:"+this.labelWidth+"px;";
42383                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42384             }
42385             if(this.labelAlign == 'top'){
42386                 this.labelStyle = "width:auto;";
42387                 this.elementStyle = "padding-left:0;";
42388             }
42389         }
42390         var stack = this.stack;
42391         var slen = stack.length;
42392         if(slen > 0){
42393             if(!this.fieldTpl){
42394                 var t = new Roo.Template(
42395                     '<div class="x-form-item {5}">',
42396                         '<label for="{0}" style="{2}">{1}{4}</label>',
42397                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42398                         '</div>',
42399                     '</div><div class="x-form-clear-left"></div>'
42400                 );
42401                 t.disableFormats = true;
42402                 t.compile();
42403                 Roo.form.Layout.prototype.fieldTpl = t;
42404             }
42405             for(var i = 0; i < slen; i++) {
42406                 if(stack[i].isFormField){
42407                     this.renderField(stack[i]);
42408                 }else{
42409                     this.renderComponent(stack[i]);
42410                 }
42411             }
42412         }
42413         if(this.clear){
42414             this.el.createChild({cls:'x-form-clear'});
42415         }
42416     },
42417
42418     // private
42419     renderField : function(f){
42420         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42421                f.id, //0
42422                f.fieldLabel, //1
42423                f.labelStyle||this.labelStyle||'', //2
42424                this.elementStyle||'', //3
42425                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42426                f.itemCls||this.itemCls||''  //5
42427        ], true).getPrevSibling());
42428     },
42429
42430     // private
42431     renderComponent : function(c){
42432         c.render(c.isLayout ? this.el : this.el.createChild());    
42433     },
42434     /**
42435      * Adds a object form elements (using the xtype property as the factory method.)
42436      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42437      * @param {Object} config 
42438      */
42439     addxtype : function(o)
42440     {
42441         // create the lement.
42442         o.form = this.form;
42443         var fe = Roo.factory(o, Roo.form);
42444         this.form.allItems.push(fe);
42445         this.stack.push(fe);
42446         
42447         if (fe.isFormField) {
42448             this.form.items.add(fe);
42449         }
42450          
42451         return fe;
42452     }
42453 });
42454
42455 /**
42456  * @class Roo.form.Column
42457  * @extends Roo.form.Layout
42458  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42459  * @constructor
42460  * @param {Object} config Configuration options
42461  */
42462 Roo.form.Column = function(config){
42463     Roo.form.Column.superclass.constructor.call(this, config);
42464 };
42465
42466 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42467     /**
42468      * @cfg {Number/String} width
42469      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42470      */
42471     /**
42472      * @cfg {String/Object} autoCreate
42473      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42474      */
42475
42476     // private
42477     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42478
42479     // private
42480     onRender : function(ct, position){
42481         Roo.form.Column.superclass.onRender.call(this, ct, position);
42482         if(this.width){
42483             this.el.setWidth(this.width);
42484         }
42485     }
42486 });
42487
42488
42489 /**
42490  * @class Roo.form.Row
42491  * @extends Roo.form.Layout
42492  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42493  * @constructor
42494  * @param {Object} config Configuration options
42495  */
42496
42497  
42498 Roo.form.Row = function(config){
42499     Roo.form.Row.superclass.constructor.call(this, config);
42500 };
42501  
42502 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42503       /**
42504      * @cfg {Number/String} width
42505      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42506      */
42507     /**
42508      * @cfg {Number/String} height
42509      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42510      */
42511     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42512     
42513     padWidth : 20,
42514     // private
42515     onRender : function(ct, position){
42516         //console.log('row render');
42517         if(!this.rowTpl){
42518             var t = new Roo.Template(
42519                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42520                     '<label for="{0}" style="{2}">{1}{4}</label>',
42521                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42522                     '</div>',
42523                 '</div>'
42524             );
42525             t.disableFormats = true;
42526             t.compile();
42527             Roo.form.Layout.prototype.rowTpl = t;
42528         }
42529         this.fieldTpl = this.rowTpl;
42530         
42531         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42532         var labelWidth = 100;
42533         
42534         if ((this.labelAlign != 'top')) {
42535             if (typeof this.labelWidth == 'number') {
42536                 labelWidth = this.labelWidth
42537             }
42538             this.padWidth =  20 + labelWidth;
42539             
42540         }
42541         
42542         Roo.form.Column.superclass.onRender.call(this, ct, position);
42543         if(this.width){
42544             this.el.setWidth(this.width);
42545         }
42546         if(this.height){
42547             this.el.setHeight(this.height);
42548         }
42549     },
42550     
42551     // private
42552     renderField : function(f){
42553         f.fieldEl = this.fieldTpl.append(this.el, [
42554                f.id, f.fieldLabel,
42555                f.labelStyle||this.labelStyle||'',
42556                this.elementStyle||'',
42557                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42558                f.itemCls||this.itemCls||'',
42559                f.width ? f.width + this.padWidth : 160 + this.padWidth
42560        ],true);
42561     }
42562 });
42563  
42564
42565 /**
42566  * @class Roo.form.FieldSet
42567  * @extends Roo.form.Layout
42568  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42569  * @constructor
42570  * @param {Object} config Configuration options
42571  */
42572 Roo.form.FieldSet = function(config){
42573     Roo.form.FieldSet.superclass.constructor.call(this, config);
42574 };
42575
42576 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42577     /**
42578      * @cfg {String} legend
42579      * The text to display as the legend for the FieldSet (defaults to '')
42580      */
42581     /**
42582      * @cfg {String/Object} autoCreate
42583      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42584      */
42585
42586     // private
42587     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42588
42589     // private
42590     onRender : function(ct, position){
42591         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42592         if(this.legend){
42593             this.setLegend(this.legend);
42594         }
42595     },
42596
42597     // private
42598     setLegend : function(text){
42599         if(this.rendered){
42600             this.el.child('legend').update(text);
42601         }
42602     }
42603 });/*
42604  * Based on:
42605  * Ext JS Library 1.1.1
42606  * Copyright(c) 2006-2007, Ext JS, LLC.
42607  *
42608  * Originally Released Under LGPL - original licence link has changed is not relivant.
42609  *
42610  * Fork - LGPL
42611  * <script type="text/javascript">
42612  */
42613 /**
42614  * @class Roo.form.VTypes
42615  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42616  * @singleton
42617  */
42618 Roo.form.VTypes = function(){
42619     // closure these in so they are only created once.
42620     var alpha = /^[a-zA-Z_]+$/;
42621     var alphanum = /^[a-zA-Z0-9_]+$/;
42622     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42623     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42624
42625     // All these messages and functions are configurable
42626     return {
42627         /**
42628          * The function used to validate email addresses
42629          * @param {String} value The email address
42630          */
42631         'email' : function(v){
42632             return email.test(v);
42633         },
42634         /**
42635          * The error text to display when the email validation function returns false
42636          * @type String
42637          */
42638         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42639         /**
42640          * The keystroke filter mask to be applied on email input
42641          * @type RegExp
42642          */
42643         'emailMask' : /[a-z0-9_\.\-@]/i,
42644
42645         /**
42646          * The function used to validate URLs
42647          * @param {String} value The URL
42648          */
42649         'url' : function(v){
42650             return url.test(v);
42651         },
42652         /**
42653          * The error text to display when the url validation function returns false
42654          * @type String
42655          */
42656         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42657         
42658         /**
42659          * The function used to validate alpha values
42660          * @param {String} value The value
42661          */
42662         'alpha' : function(v){
42663             return alpha.test(v);
42664         },
42665         /**
42666          * The error text to display when the alpha validation function returns false
42667          * @type String
42668          */
42669         'alphaText' : 'This field should only contain letters and _',
42670         /**
42671          * The keystroke filter mask to be applied on alpha input
42672          * @type RegExp
42673          */
42674         'alphaMask' : /[a-z_]/i,
42675
42676         /**
42677          * The function used to validate alphanumeric values
42678          * @param {String} value The value
42679          */
42680         'alphanum' : function(v){
42681             return alphanum.test(v);
42682         },
42683         /**
42684          * The error text to display when the alphanumeric validation function returns false
42685          * @type String
42686          */
42687         'alphanumText' : 'This field should only contain letters, numbers and _',
42688         /**
42689          * The keystroke filter mask to be applied on alphanumeric input
42690          * @type RegExp
42691          */
42692         'alphanumMask' : /[a-z0-9_]/i
42693     };
42694 }();//<script type="text/javascript">
42695
42696 /**
42697  * @class Roo.form.FCKeditor
42698  * @extends Roo.form.TextArea
42699  * Wrapper around the FCKEditor http://www.fckeditor.net
42700  * @constructor
42701  * Creates a new FCKeditor
42702  * @param {Object} config Configuration options
42703  */
42704 Roo.form.FCKeditor = function(config){
42705     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42706     this.addEvents({
42707          /**
42708          * @event editorinit
42709          * Fired when the editor is initialized - you can add extra handlers here..
42710          * @param {FCKeditor} this
42711          * @param {Object} the FCK object.
42712          */
42713         editorinit : true
42714     });
42715     
42716     
42717 };
42718 Roo.form.FCKeditor.editors = { };
42719 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42720 {
42721     //defaultAutoCreate : {
42722     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42723     //},
42724     // private
42725     /**
42726      * @cfg {Object} fck options - see fck manual for details.
42727      */
42728     fckconfig : false,
42729     
42730     /**
42731      * @cfg {Object} fck toolbar set (Basic or Default)
42732      */
42733     toolbarSet : 'Basic',
42734     /**
42735      * @cfg {Object} fck BasePath
42736      */ 
42737     basePath : '/fckeditor/',
42738     
42739     
42740     frame : false,
42741     
42742     value : '',
42743     
42744    
42745     onRender : function(ct, position)
42746     {
42747         if(!this.el){
42748             this.defaultAutoCreate = {
42749                 tag: "textarea",
42750                 style:"width:300px;height:60px;",
42751                 autocomplete: "off"
42752             };
42753         }
42754         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42755         /*
42756         if(this.grow){
42757             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42758             if(this.preventScrollbars){
42759                 this.el.setStyle("overflow", "hidden");
42760             }
42761             this.el.setHeight(this.growMin);
42762         }
42763         */
42764         //console.log('onrender' + this.getId() );
42765         Roo.form.FCKeditor.editors[this.getId()] = this;
42766          
42767
42768         this.replaceTextarea() ;
42769         
42770     },
42771     
42772     getEditor : function() {
42773         return this.fckEditor;
42774     },
42775     /**
42776      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42777      * @param {Mixed} value The value to set
42778      */
42779     
42780     
42781     setValue : function(value)
42782     {
42783         //console.log('setValue: ' + value);
42784         
42785         if(typeof(value) == 'undefined') { // not sure why this is happending...
42786             return;
42787         }
42788         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42789         
42790         //if(!this.el || !this.getEditor()) {
42791         //    this.value = value;
42792             //this.setValue.defer(100,this,[value]);    
42793         //    return;
42794         //} 
42795         
42796         if(!this.getEditor()) {
42797             return;
42798         }
42799         
42800         this.getEditor().SetData(value);
42801         
42802         //
42803
42804     },
42805
42806     /**
42807      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42808      * @return {Mixed} value The field value
42809      */
42810     getValue : function()
42811     {
42812         
42813         if (this.frame && this.frame.dom.style.display == 'none') {
42814             return Roo.form.FCKeditor.superclass.getValue.call(this);
42815         }
42816         
42817         if(!this.el || !this.getEditor()) {
42818            
42819            // this.getValue.defer(100,this); 
42820             return this.value;
42821         }
42822        
42823         
42824         var value=this.getEditor().GetData();
42825         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42826         return Roo.form.FCKeditor.superclass.getValue.call(this);
42827         
42828
42829     },
42830
42831     /**
42832      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42833      * @return {Mixed} value The field value
42834      */
42835     getRawValue : function()
42836     {
42837         if (this.frame && this.frame.dom.style.display == 'none') {
42838             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42839         }
42840         
42841         if(!this.el || !this.getEditor()) {
42842             //this.getRawValue.defer(100,this); 
42843             return this.value;
42844             return;
42845         }
42846         
42847         
42848         
42849         var value=this.getEditor().GetData();
42850         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42851         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42852          
42853     },
42854     
42855     setSize : function(w,h) {
42856         
42857         
42858         
42859         //if (this.frame && this.frame.dom.style.display == 'none') {
42860         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42861         //    return;
42862         //}
42863         //if(!this.el || !this.getEditor()) {
42864         //    this.setSize.defer(100,this, [w,h]); 
42865         //    return;
42866         //}
42867         
42868         
42869         
42870         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42871         
42872         this.frame.dom.setAttribute('width', w);
42873         this.frame.dom.setAttribute('height', h);
42874         this.frame.setSize(w,h);
42875         
42876     },
42877     
42878     toggleSourceEdit : function(value) {
42879         
42880       
42881          
42882         this.el.dom.style.display = value ? '' : 'none';
42883         this.frame.dom.style.display = value ?  'none' : '';
42884         
42885     },
42886     
42887     
42888     focus: function(tag)
42889     {
42890         if (this.frame.dom.style.display == 'none') {
42891             return Roo.form.FCKeditor.superclass.focus.call(this);
42892         }
42893         if(!this.el || !this.getEditor()) {
42894             this.focus.defer(100,this, [tag]); 
42895             return;
42896         }
42897         
42898         
42899         
42900         
42901         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42902         this.getEditor().Focus();
42903         if (tgs.length) {
42904             if (!this.getEditor().Selection.GetSelection()) {
42905                 this.focus.defer(100,this, [tag]); 
42906                 return;
42907             }
42908             
42909             
42910             var r = this.getEditor().EditorDocument.createRange();
42911             r.setStart(tgs[0],0);
42912             r.setEnd(tgs[0],0);
42913             this.getEditor().Selection.GetSelection().removeAllRanges();
42914             this.getEditor().Selection.GetSelection().addRange(r);
42915             this.getEditor().Focus();
42916         }
42917         
42918     },
42919     
42920     
42921     
42922     replaceTextarea : function()
42923     {
42924         if ( document.getElementById( this.getId() + '___Frame' ) )
42925             return ;
42926         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42927         //{
42928             // We must check the elements firstly using the Id and then the name.
42929         var oTextarea = document.getElementById( this.getId() );
42930         
42931         var colElementsByName = document.getElementsByName( this.getId() ) ;
42932          
42933         oTextarea.style.display = 'none' ;
42934
42935         if ( oTextarea.tabIndex ) {            
42936             this.TabIndex = oTextarea.tabIndex ;
42937         }
42938         
42939         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42940         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42941         this.frame = Roo.get(this.getId() + '___Frame')
42942     },
42943     
42944     _getConfigHtml : function()
42945     {
42946         var sConfig = '' ;
42947
42948         for ( var o in this.fckconfig ) {
42949             sConfig += sConfig.length > 0  ? '&amp;' : '';
42950             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42951         }
42952
42953         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42954     },
42955     
42956     
42957     _getIFrameHtml : function()
42958     {
42959         var sFile = 'fckeditor.html' ;
42960         /* no idea what this is about..
42961         try
42962         {
42963             if ( (/fcksource=true/i).test( window.top.location.search ) )
42964                 sFile = 'fckeditor.original.html' ;
42965         }
42966         catch (e) { 
42967         */
42968
42969         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42970         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42971         
42972         
42973         var html = '<iframe id="' + this.getId() +
42974             '___Frame" src="' + sLink +
42975             '" width="' + this.width +
42976             '" height="' + this.height + '"' +
42977             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42978             ' frameborder="0" scrolling="no"></iframe>' ;
42979
42980         return html ;
42981     },
42982     
42983     _insertHtmlBefore : function( html, element )
42984     {
42985         if ( element.insertAdjacentHTML )       {
42986             // IE
42987             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42988         } else { // Gecko
42989             var oRange = document.createRange() ;
42990             oRange.setStartBefore( element ) ;
42991             var oFragment = oRange.createContextualFragment( html );
42992             element.parentNode.insertBefore( oFragment, element ) ;
42993         }
42994     }
42995     
42996     
42997   
42998     
42999     
43000     
43001     
43002
43003 });
43004
43005 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43006
43007 function FCKeditor_OnComplete(editorInstance){
43008     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43009     f.fckEditor = editorInstance;
43010     //console.log("loaded");
43011     f.fireEvent('editorinit', f, editorInstance);
43012
43013   
43014
43015  
43016
43017
43018
43019
43020
43021
43022
43023
43024
43025
43026
43027
43028
43029
43030
43031 //<script type="text/javascript">
43032 /**
43033  * @class Roo.form.GridField
43034  * @extends Roo.form.Field
43035  * Embed a grid (or editable grid into a form)
43036  * STATUS ALPHA
43037  * 
43038  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43039  * it needs 
43040  * xgrid.store = Roo.data.Store
43041  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43042  * xgrid.store.reader = Roo.data.JsonReader 
43043  * 
43044  * 
43045  * @constructor
43046  * Creates a new GridField
43047  * @param {Object} config Configuration options
43048  */
43049 Roo.form.GridField = function(config){
43050     Roo.form.GridField.superclass.constructor.call(this, config);
43051      
43052 };
43053
43054 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43055     /**
43056      * @cfg {Number} width  - used to restrict width of grid..
43057      */
43058     width : 100,
43059     /**
43060      * @cfg {Number} height - used to restrict height of grid..
43061      */
43062     height : 50,
43063      /**
43064      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43065          * 
43066          *}
43067      */
43068     xgrid : false, 
43069     /**
43070      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43071      * {tag: "input", type: "checkbox", autocomplete: "off"})
43072      */
43073    // defaultAutoCreate : { tag: 'div' },
43074     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43075     /**
43076      * @cfg {String} addTitle Text to include for adding a title.
43077      */
43078     addTitle : false,
43079     //
43080     onResize : function(){
43081         Roo.form.Field.superclass.onResize.apply(this, arguments);
43082     },
43083
43084     initEvents : function(){
43085         // Roo.form.Checkbox.superclass.initEvents.call(this);
43086         // has no events...
43087        
43088     },
43089
43090
43091     getResizeEl : function(){
43092         return this.wrap;
43093     },
43094
43095     getPositionEl : function(){
43096         return this.wrap;
43097     },
43098
43099     // private
43100     onRender : function(ct, position){
43101         
43102         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43103         var style = this.style;
43104         delete this.style;
43105         
43106         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43107         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43108         this.viewEl = this.wrap.createChild({ tag: 'div' });
43109         if (style) {
43110             this.viewEl.applyStyles(style);
43111         }
43112         if (this.width) {
43113             this.viewEl.setWidth(this.width);
43114         }
43115         if (this.height) {
43116             this.viewEl.setHeight(this.height);
43117         }
43118         //if(this.inputValue !== undefined){
43119         //this.setValue(this.value);
43120         
43121         
43122         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43123         
43124         
43125         this.grid.render();
43126         this.grid.getDataSource().on('remove', this.refreshValue, this);
43127         this.grid.getDataSource().on('update', this.refreshValue, this);
43128         this.grid.on('afteredit', this.refreshValue, this);
43129  
43130     },
43131      
43132     
43133     /**
43134      * Sets the value of the item. 
43135      * @param {String} either an object  or a string..
43136      */
43137     setValue : function(v){
43138         //this.value = v;
43139         v = v || []; // empty set..
43140         // this does not seem smart - it really only affects memoryproxy grids..
43141         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43142             var ds = this.grid.getDataSource();
43143             // assumes a json reader..
43144             var data = {}
43145             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43146             ds.loadData( data);
43147         }
43148         // clear selection so it does not get stale.
43149         if (this.grid.sm) { 
43150             this.grid.sm.clearSelections();
43151         }
43152         
43153         Roo.form.GridField.superclass.setValue.call(this, v);
43154         this.refreshValue();
43155         // should load data in the grid really....
43156     },
43157     
43158     // private
43159     refreshValue: function() {
43160          var val = [];
43161         this.grid.getDataSource().each(function(r) {
43162             val.push(r.data);
43163         });
43164         this.el.dom.value = Roo.encode(val);
43165     }
43166     
43167      
43168     
43169     
43170 });/*
43171  * Based on:
43172  * Ext JS Library 1.1.1
43173  * Copyright(c) 2006-2007, Ext JS, LLC.
43174  *
43175  * Originally Released Under LGPL - original licence link has changed is not relivant.
43176  *
43177  * Fork - LGPL
43178  * <script type="text/javascript">
43179  */
43180 /**
43181  * @class Roo.form.DisplayField
43182  * @extends Roo.form.Field
43183  * A generic Field to display non-editable data.
43184  * @constructor
43185  * Creates a new Display Field item.
43186  * @param {Object} config Configuration options
43187  */
43188 Roo.form.DisplayField = function(config){
43189     Roo.form.DisplayField.superclass.constructor.call(this, config);
43190     
43191 };
43192
43193 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43194     inputType:      'hidden',
43195     allowBlank:     true,
43196     readOnly:         true,
43197     
43198  
43199     /**
43200      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43201      */
43202     focusClass : undefined,
43203     /**
43204      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43205      */
43206     fieldClass: 'x-form-field',
43207     
43208      /**
43209      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43210      */
43211     valueRenderer: undefined,
43212     
43213     width: 100,
43214     /**
43215      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43216      * {tag: "input", type: "checkbox", autocomplete: "off"})
43217      */
43218      
43219  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43220
43221     onResize : function(){
43222         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43223         
43224     },
43225
43226     initEvents : function(){
43227         // Roo.form.Checkbox.superclass.initEvents.call(this);
43228         // has no events...
43229        
43230     },
43231
43232
43233     getResizeEl : function(){
43234         return this.wrap;
43235     },
43236
43237     getPositionEl : function(){
43238         return this.wrap;
43239     },
43240
43241     // private
43242     onRender : function(ct, position){
43243         
43244         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43245         //if(this.inputValue !== undefined){
43246         this.wrap = this.el.wrap();
43247         
43248         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43249         
43250         if (this.bodyStyle) {
43251             this.viewEl.applyStyles(this.bodyStyle);
43252         }
43253         //this.viewEl.setStyle('padding', '2px');
43254         
43255         this.setValue(this.value);
43256         
43257     },
43258 /*
43259     // private
43260     initValue : Roo.emptyFn,
43261
43262   */
43263
43264         // private
43265     onClick : function(){
43266         
43267     },
43268
43269     /**
43270      * Sets the checked state of the checkbox.
43271      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43272      */
43273     setValue : function(v){
43274         this.value = v;
43275         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43276         // this might be called before we have a dom element..
43277         if (!this.viewEl) {
43278             return;
43279         }
43280         this.viewEl.dom.innerHTML = html;
43281         Roo.form.DisplayField.superclass.setValue.call(this, v);
43282
43283     }
43284 });/*
43285  * 
43286  * Licence- LGPL
43287  * 
43288  */
43289
43290 /**
43291  * @class Roo.form.DayPicker
43292  * @extends Roo.form.Field
43293  * A Day picker show [M] [T] [W] ....
43294  * @constructor
43295  * Creates a new Day Picker
43296  * @param {Object} config Configuration options
43297  */
43298 Roo.form.DayPicker= function(config){
43299     Roo.form.DayPicker.superclass.constructor.call(this, config);
43300      
43301 };
43302
43303 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43304     /**
43305      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43306      */
43307     focusClass : undefined,
43308     /**
43309      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43310      */
43311     fieldClass: "x-form-field",
43312    
43313     /**
43314      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43315      * {tag: "input", type: "checkbox", autocomplete: "off"})
43316      */
43317     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43318     
43319    
43320     actionMode : 'viewEl', 
43321     //
43322     // private
43323  
43324     inputType : 'hidden',
43325     
43326      
43327     inputElement: false, // real input element?
43328     basedOn: false, // ????
43329     
43330     isFormField: true, // not sure where this is needed!!!!
43331
43332     onResize : function(){
43333         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43334         if(!this.boxLabel){
43335             this.el.alignTo(this.wrap, 'c-c');
43336         }
43337     },
43338
43339     initEvents : function(){
43340         Roo.form.Checkbox.superclass.initEvents.call(this);
43341         this.el.on("click", this.onClick,  this);
43342         this.el.on("change", this.onClick,  this);
43343     },
43344
43345
43346     getResizeEl : function(){
43347         return this.wrap;
43348     },
43349
43350     getPositionEl : function(){
43351         return this.wrap;
43352     },
43353
43354     
43355     // private
43356     onRender : function(ct, position){
43357         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43358        
43359         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43360         
43361         var r1 = '<table><tr>';
43362         var r2 = '<tr class="x-form-daypick-icons">';
43363         for (var i=0; i < 7; i++) {
43364             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43365             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43366         }
43367         
43368         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43369         viewEl.select('img').on('click', this.onClick, this);
43370         this.viewEl = viewEl;   
43371         
43372         
43373         // this will not work on Chrome!!!
43374         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43375         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43376         
43377         
43378           
43379
43380     },
43381
43382     // private
43383     initValue : Roo.emptyFn,
43384
43385     /**
43386      * Returns the checked state of the checkbox.
43387      * @return {Boolean} True if checked, else false
43388      */
43389     getValue : function(){
43390         return this.el.dom.value;
43391         
43392     },
43393
43394         // private
43395     onClick : function(e){ 
43396         //this.setChecked(!this.checked);
43397         Roo.get(e.target).toggleClass('x-menu-item-checked');
43398         this.refreshValue();
43399         //if(this.el.dom.checked != this.checked){
43400         //    this.setValue(this.el.dom.checked);
43401        // }
43402     },
43403     
43404     // private
43405     refreshValue : function()
43406     {
43407         var val = '';
43408         this.viewEl.select('img',true).each(function(e,i,n)  {
43409             val += e.is(".x-menu-item-checked") ? String(n) : '';
43410         });
43411         this.setValue(val, true);
43412     },
43413
43414     /**
43415      * Sets the checked state of the checkbox.
43416      * On is always based on a string comparison between inputValue and the param.
43417      * @param {Boolean/String} value - the value to set 
43418      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43419      */
43420     setValue : function(v,suppressEvent){
43421         if (!this.el.dom) {
43422             return;
43423         }
43424         var old = this.el.dom.value ;
43425         this.el.dom.value = v;
43426         if (suppressEvent) {
43427             return ;
43428         }
43429          
43430         // update display..
43431         this.viewEl.select('img',true).each(function(e,i,n)  {
43432             
43433             var on = e.is(".x-menu-item-checked");
43434             var newv = v.indexOf(String(n)) > -1;
43435             if (on != newv) {
43436                 e.toggleClass('x-menu-item-checked');
43437             }
43438             
43439         });
43440         
43441         
43442         this.fireEvent('change', this, v, old);
43443         
43444         
43445     },
43446    
43447     // handle setting of hidden value by some other method!!?!?
43448     setFromHidden: function()
43449     {
43450         if(!this.el){
43451             return;
43452         }
43453         //console.log("SET FROM HIDDEN");
43454         //alert('setFrom hidden');
43455         this.setValue(this.el.dom.value);
43456     },
43457     
43458     onDestroy : function()
43459     {
43460         if(this.viewEl){
43461             Roo.get(this.viewEl).remove();
43462         }
43463          
43464         Roo.form.DayPicker.superclass.onDestroy.call(this);
43465     }
43466
43467 });/*
43468  * RooJS Library 1.1.1
43469  * Copyright(c) 2008-2011  Alan Knowles
43470  *
43471  * License - LGPL
43472  */
43473  
43474
43475 /**
43476  * @class Roo.form.ComboCheck
43477  * @extends Roo.form.ComboBox
43478  * A combobox for multiple select items.
43479  *
43480  * FIXME - could do with a reset button..
43481  * 
43482  * @constructor
43483  * Create a new ComboCheck
43484  * @param {Object} config Configuration options
43485  */
43486 Roo.form.ComboCheck = function(config){
43487     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43488     // should verify some data...
43489     // like
43490     // hiddenName = required..
43491     // displayField = required
43492     // valudField == required
43493     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43494     var _t = this;
43495     Roo.each(req, function(e) {
43496         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43497             throw "Roo.form.ComboCheck : missing value for: " + e;
43498         }
43499     });
43500     
43501     
43502 };
43503
43504 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43505      
43506      
43507     editable : false,
43508      
43509     selectedClass: 'x-menu-item-checked', 
43510     
43511     // private
43512     onRender : function(ct, position){
43513         var _t = this;
43514         
43515         
43516         
43517         if(!this.tpl){
43518             var cls = 'x-combo-list';
43519
43520             
43521             this.tpl =  new Roo.Template({
43522                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43523                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43524                    '<span>{' + this.displayField + '}</span>' +
43525                     '</div>' 
43526                 
43527             });
43528         }
43529  
43530         
43531         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43532         this.view.singleSelect = false;
43533         this.view.multiSelect = true;
43534         this.view.toggleSelect = true;
43535         this.pageTb.add(new Roo.Toolbar.Fill(), {
43536             
43537             text: 'Done',
43538             handler: function()
43539             {
43540                 _t.collapse();
43541             }
43542         });
43543     },
43544     
43545     onViewOver : function(e, t){
43546         // do nothing...
43547         return;
43548         
43549     },
43550     
43551     onViewClick : function(doFocus,index){
43552         return;
43553         
43554     },
43555     select: function () {
43556         //Roo.log("SELECT CALLED");
43557     },
43558      
43559     selectByValue : function(xv, scrollIntoView){
43560         var ar = this.getValueArray();
43561         var sels = [];
43562         
43563         Roo.each(ar, function(v) {
43564             if(v === undefined || v === null){
43565                 return;
43566             }
43567             var r = this.findRecord(this.valueField, v);
43568             if(r){
43569                 sels.push(this.store.indexOf(r))
43570                 
43571             }
43572         },this);
43573         this.view.select(sels);
43574         return false;
43575     },
43576     
43577     
43578     
43579     onSelect : function(record, index){
43580        // Roo.log("onselect Called");
43581        // this is only called by the clear button now..
43582         this.view.clearSelections();
43583         this.setValue('[]');
43584         if (this.value != this.valueBefore) {
43585             this.fireEvent('change', this, this.value, this.valueBefore);
43586         }
43587     },
43588     getValueArray : function()
43589     {
43590         var ar = [] ;
43591         
43592         try {
43593             Roo.log(this.value);
43594             var ar = Roo.decode(this.value);
43595             return  ar instanceof Array ? ar : []; //?? valid?
43596             
43597         } catch(e) {
43598             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43599             return [];
43600         }
43601          
43602     },
43603     expand : function ()
43604     {
43605         Roo.form.ComboCheck.superclass.expand.call(this);
43606         this.valueBefore = this.value;
43607         
43608
43609     },
43610     
43611     collapse : function(){
43612         Roo.form.ComboCheck.superclass.collapse.call(this);
43613         var sl = this.view.getSelectedIndexes();
43614         var st = this.store;
43615         var nv = [];
43616         var tv = [];
43617         var r;
43618         Roo.each(sl, function(i) {
43619             r = st.getAt(i);
43620             nv.push(r.get(this.valueField));
43621         },this);
43622         this.setValue(Roo.encode(nv));
43623         if (this.value != this.valueBefore) {
43624
43625             this.fireEvent('change', this, this.value, this.valueBefore);
43626         }
43627         
43628     },
43629     
43630     setValue : function(v){
43631         // Roo.log(v);
43632         this.value = v;
43633         
43634         var vals = this.getValueArray();
43635         var tv = [];
43636         Roo.each(vals, function(k) {
43637             var r = this.findRecord(this.valueField, k);
43638             if(r){
43639                 tv.push(r.data[this.displayField]);
43640             }else if(this.valueNotFoundText !== undefined){
43641                 tv.push( this.valueNotFoundText );
43642             }
43643         },this);
43644        // Roo.log(tv);
43645         
43646         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43647         this.hiddenField.value = v;
43648         this.value = v;
43649     }
43650     
43651 });//<script type="text/javasscript">
43652  
43653
43654 /**
43655  * @class Roo.DDView
43656  * A DnD enabled version of Roo.View.
43657  * @param {Element/String} container The Element in which to create the View.
43658  * @param {String} tpl The template string used to create the markup for each element of the View
43659  * @param {Object} config The configuration properties. These include all the config options of
43660  * {@link Roo.View} plus some specific to this class.<br>
43661  * <p>
43662  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43663  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43664  * <p>
43665  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43666 .x-view-drag-insert-above {
43667         border-top:1px dotted #3366cc;
43668 }
43669 .x-view-drag-insert-below {
43670         border-bottom:1px dotted #3366cc;
43671 }
43672 </code></pre>
43673  * 
43674  */
43675  
43676 Roo.DDView = function(container, tpl, config) {
43677     Roo.DDView.superclass.constructor.apply(this, arguments);
43678     this.getEl().setStyle("outline", "0px none");
43679     this.getEl().unselectable();
43680     if (this.dragGroup) {
43681                 this.setDraggable(this.dragGroup.split(","));
43682     }
43683     if (this.dropGroup) {
43684                 this.setDroppable(this.dropGroup.split(","));
43685     }
43686     if (this.deletable) {
43687         this.setDeletable();
43688     }
43689     this.isDirtyFlag = false;
43690         this.addEvents({
43691                 "drop" : true
43692         });
43693 };
43694
43695 Roo.extend(Roo.DDView, Roo.View, {
43696 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43697 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43698 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43699 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43700
43701         isFormField: true,
43702
43703         reset: Roo.emptyFn,
43704         
43705         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43706
43707         validate: function() {
43708                 return true;
43709         },
43710         
43711         destroy: function() {
43712                 this.purgeListeners();
43713                 this.getEl.removeAllListeners();
43714                 this.getEl().remove();
43715                 if (this.dragZone) {
43716                         if (this.dragZone.destroy) {
43717                                 this.dragZone.destroy();
43718                         }
43719                 }
43720                 if (this.dropZone) {
43721                         if (this.dropZone.destroy) {
43722                                 this.dropZone.destroy();
43723                         }
43724                 }
43725         },
43726
43727 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43728         getName: function() {
43729                 return this.name;
43730         },
43731
43732 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43733         setValue: function(v) {
43734                 if (!this.store) {
43735                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43736                 }
43737                 var data = {};
43738                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43739                 this.store.proxy = new Roo.data.MemoryProxy(data);
43740                 this.store.load();
43741         },
43742
43743 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43744         getValue: function() {
43745                 var result = '(';
43746                 this.store.each(function(rec) {
43747                         result += rec.id + ',';
43748                 });
43749                 return result.substr(0, result.length - 1) + ')';
43750         },
43751         
43752         getIds: function() {
43753                 var i = 0, result = new Array(this.store.getCount());
43754                 this.store.each(function(rec) {
43755                         result[i++] = rec.id;
43756                 });
43757                 return result;
43758         },
43759         
43760         isDirty: function() {
43761                 return this.isDirtyFlag;
43762         },
43763
43764 /**
43765  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43766  *      whole Element becomes the target, and this causes the drop gesture to append.
43767  */
43768     getTargetFromEvent : function(e) {
43769                 var target = e.getTarget();
43770                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43771                 target = target.parentNode;
43772                 }
43773                 if (!target) {
43774                         target = this.el.dom.lastChild || this.el.dom;
43775                 }
43776                 return target;
43777     },
43778
43779 /**
43780  *      Create the drag data which consists of an object which has the property "ddel" as
43781  *      the drag proxy element. 
43782  */
43783     getDragData : function(e) {
43784         var target = this.findItemFromChild(e.getTarget());
43785                 if(target) {
43786                         this.handleSelection(e);
43787                         var selNodes = this.getSelectedNodes();
43788             var dragData = {
43789                 source: this,
43790                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43791                 nodes: selNodes,
43792                 records: []
43793                         };
43794                         var selectedIndices = this.getSelectedIndexes();
43795                         for (var i = 0; i < selectedIndices.length; i++) {
43796                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43797                         }
43798                         if (selNodes.length == 1) {
43799                                 dragData.ddel = target.cloneNode(true); // the div element
43800                         } else {
43801                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43802                                 div.className = 'multi-proxy';
43803                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43804                                         div.appendChild(selNodes[i].cloneNode(true));
43805                                 }
43806                                 dragData.ddel = div;
43807                         }
43808             //console.log(dragData)
43809             //console.log(dragData.ddel.innerHTML)
43810                         return dragData;
43811                 }
43812         //console.log('nodragData')
43813                 return false;
43814     },
43815     
43816 /**     Specify to which ddGroup items in this DDView may be dragged. */
43817     setDraggable: function(ddGroup) {
43818         if (ddGroup instanceof Array) {
43819                 Roo.each(ddGroup, this.setDraggable, this);
43820                 return;
43821         }
43822         if (this.dragZone) {
43823                 this.dragZone.addToGroup(ddGroup);
43824         } else {
43825                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43826                                 containerScroll: true,
43827                                 ddGroup: ddGroup 
43828
43829                         });
43830 //                      Draggability implies selection. DragZone's mousedown selects the element.
43831                         if (!this.multiSelect) { this.singleSelect = true; }
43832
43833 //                      Wire the DragZone's handlers up to methods in *this*
43834                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43835                 }
43836     },
43837
43838 /**     Specify from which ddGroup this DDView accepts drops. */
43839     setDroppable: function(ddGroup) {
43840         if (ddGroup instanceof Array) {
43841                 Roo.each(ddGroup, this.setDroppable, this);
43842                 return;
43843         }
43844         if (this.dropZone) {
43845                 this.dropZone.addToGroup(ddGroup);
43846         } else {
43847                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43848                                 containerScroll: true,
43849                                 ddGroup: ddGroup
43850                         });
43851
43852 //                      Wire the DropZone's handlers up to methods in *this*
43853                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43854                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43855                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43856                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43857                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43858                 }
43859     },
43860
43861 /**     Decide whether to drop above or below a View node. */
43862     getDropPoint : function(e, n, dd){
43863         if (n == this.el.dom) { return "above"; }
43864                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43865                 var c = t + (b - t) / 2;
43866                 var y = Roo.lib.Event.getPageY(e);
43867                 if(y <= c) {
43868                         return "above";
43869                 }else{
43870                         return "below";
43871                 }
43872     },
43873
43874     onNodeEnter : function(n, dd, e, data){
43875                 return false;
43876     },
43877     
43878     onNodeOver : function(n, dd, e, data){
43879                 var pt = this.getDropPoint(e, n, dd);
43880                 // set the insert point style on the target node
43881                 var dragElClass = this.dropNotAllowed;
43882                 if (pt) {
43883                         var targetElClass;
43884                         if (pt == "above"){
43885                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43886                                 targetElClass = "x-view-drag-insert-above";
43887                         } else {
43888                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43889                                 targetElClass = "x-view-drag-insert-below";
43890                         }
43891                         if (this.lastInsertClass != targetElClass){
43892                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43893                                 this.lastInsertClass = targetElClass;
43894                         }
43895                 }
43896                 return dragElClass;
43897         },
43898
43899     onNodeOut : function(n, dd, e, data){
43900                 this.removeDropIndicators(n);
43901     },
43902
43903     onNodeDrop : function(n, dd, e, data){
43904         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43905                 return false;
43906         }
43907         var pt = this.getDropPoint(e, n, dd);
43908                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43909                 if (pt == "below") { insertAt++; }
43910                 for (var i = 0; i < data.records.length; i++) {
43911                         var r = data.records[i];
43912                         var dup = this.store.getById(r.id);
43913                         if (dup && (dd != this.dragZone)) {
43914                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43915                         } else {
43916                                 if (data.copy) {
43917                                         this.store.insert(insertAt++, r.copy());
43918                                 } else {
43919                                         data.source.isDirtyFlag = true;
43920                                         r.store.remove(r);
43921                                         this.store.insert(insertAt++, r);
43922                                 }
43923                                 this.isDirtyFlag = true;
43924                         }
43925                 }
43926                 this.dragZone.cachedTarget = null;
43927                 return true;
43928     },
43929
43930     removeDropIndicators : function(n){
43931                 if(n){
43932                         Roo.fly(n).removeClass([
43933                                 "x-view-drag-insert-above",
43934                                 "x-view-drag-insert-below"]);
43935                         this.lastInsertClass = "_noclass";
43936                 }
43937     },
43938
43939 /**
43940  *      Utility method. Add a delete option to the DDView's context menu.
43941  *      @param {String} imageUrl The URL of the "delete" icon image.
43942  */
43943         setDeletable: function(imageUrl) {
43944                 if (!this.singleSelect && !this.multiSelect) {
43945                         this.singleSelect = true;
43946                 }
43947                 var c = this.getContextMenu();
43948                 this.contextMenu.on("itemclick", function(item) {
43949                         switch (item.id) {
43950                                 case "delete":
43951                                         this.remove(this.getSelectedIndexes());
43952                                         break;
43953                         }
43954                 }, this);
43955                 this.contextMenu.add({
43956                         icon: imageUrl,
43957                         id: "delete",
43958                         text: 'Delete'
43959                 });
43960         },
43961         
43962 /**     Return the context menu for this DDView. */
43963         getContextMenu: function() {
43964                 if (!this.contextMenu) {
43965 //                      Create the View's context menu
43966                         this.contextMenu = new Roo.menu.Menu({
43967                                 id: this.id + "-contextmenu"
43968                         });
43969                         this.el.on("contextmenu", this.showContextMenu, this);
43970                 }
43971                 return this.contextMenu;
43972         },
43973         
43974         disableContextMenu: function() {
43975                 if (this.contextMenu) {
43976                         this.el.un("contextmenu", this.showContextMenu, this);
43977                 }
43978         },
43979
43980         showContextMenu: function(e, item) {
43981         item = this.findItemFromChild(e.getTarget());
43982                 if (item) {
43983                         e.stopEvent();
43984                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43985                         this.contextMenu.showAt(e.getXY());
43986             }
43987     },
43988
43989 /**
43990  *      Remove {@link Roo.data.Record}s at the specified indices.
43991  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43992  */
43993     remove: function(selectedIndices) {
43994                 selectedIndices = [].concat(selectedIndices);
43995                 for (var i = 0; i < selectedIndices.length; i++) {
43996                         var rec = this.store.getAt(selectedIndices[i]);
43997                         this.store.remove(rec);
43998                 }
43999     },
44000
44001 /**
44002  *      Double click fires the event, but also, if this is draggable, and there is only one other
44003  *      related DropZone, it transfers the selected node.
44004  */
44005     onDblClick : function(e){
44006         var item = this.findItemFromChild(e.getTarget());
44007         if(item){
44008             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44009                 return false;
44010             }
44011             if (this.dragGroup) {
44012                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44013                     while (targets.indexOf(this.dropZone) > -1) {
44014                             targets.remove(this.dropZone);
44015                                 }
44016                     if (targets.length == 1) {
44017                                         this.dragZone.cachedTarget = null;
44018                         var el = Roo.get(targets[0].getEl());
44019                         var box = el.getBox(true);
44020                         targets[0].onNodeDrop(el.dom, {
44021                                 target: el.dom,
44022                                 xy: [box.x, box.y + box.height - 1]
44023                         }, null, this.getDragData(e));
44024                     }
44025                 }
44026         }
44027     },
44028     
44029     handleSelection: function(e) {
44030                 this.dragZone.cachedTarget = null;
44031         var item = this.findItemFromChild(e.getTarget());
44032         if (!item) {
44033                 this.clearSelections(true);
44034                 return;
44035         }
44036                 if (item && (this.multiSelect || this.singleSelect)){
44037                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44038                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44039                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44040                                 this.unselect(item);
44041                         } else {
44042                                 this.select(item, this.multiSelect && e.ctrlKey);
44043                                 this.lastSelection = item;
44044                         }
44045                 }
44046     },
44047
44048     onItemClick : function(item, index, e){
44049                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44050                         return false;
44051                 }
44052                 return true;
44053     },
44054
44055     unselect : function(nodeInfo, suppressEvent){
44056                 var node = this.getNode(nodeInfo);
44057                 if(node && this.isSelected(node)){
44058                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44059                                 Roo.fly(node).removeClass(this.selectedClass);
44060                                 this.selections.remove(node);
44061                                 if(!suppressEvent){
44062                                         this.fireEvent("selectionchange", this, this.selections);
44063                                 }
44064                         }
44065                 }
44066     }
44067 });
44068 /*
44069  * Based on:
44070  * Ext JS Library 1.1.1
44071  * Copyright(c) 2006-2007, Ext JS, LLC.
44072  *
44073  * Originally Released Under LGPL - original licence link has changed is not relivant.
44074  *
44075  * Fork - LGPL
44076  * <script type="text/javascript">
44077  */
44078  
44079 /**
44080  * @class Roo.LayoutManager
44081  * @extends Roo.util.Observable
44082  * Base class for layout managers.
44083  */
44084 Roo.LayoutManager = function(container, config){
44085     Roo.LayoutManager.superclass.constructor.call(this);
44086     this.el = Roo.get(container);
44087     // ie scrollbar fix
44088     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44089         document.body.scroll = "no";
44090     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44091         this.el.position('relative');
44092     }
44093     this.id = this.el.id;
44094     this.el.addClass("x-layout-container");
44095     /** false to disable window resize monitoring @type Boolean */
44096     this.monitorWindowResize = true;
44097     this.regions = {};
44098     this.addEvents({
44099         /**
44100          * @event layout
44101          * Fires when a layout is performed. 
44102          * @param {Roo.LayoutManager} this
44103          */
44104         "layout" : true,
44105         /**
44106          * @event regionresized
44107          * Fires when the user resizes a region. 
44108          * @param {Roo.LayoutRegion} region The resized region
44109          * @param {Number} newSize The new size (width for east/west, height for north/south)
44110          */
44111         "regionresized" : true,
44112         /**
44113          * @event regioncollapsed
44114          * Fires when a region is collapsed. 
44115          * @param {Roo.LayoutRegion} region The collapsed region
44116          */
44117         "regioncollapsed" : true,
44118         /**
44119          * @event regionexpanded
44120          * Fires when a region is expanded.  
44121          * @param {Roo.LayoutRegion} region The expanded region
44122          */
44123         "regionexpanded" : true
44124     });
44125     this.updating = false;
44126     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44127 };
44128
44129 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44130     /**
44131      * Returns true if this layout is currently being updated
44132      * @return {Boolean}
44133      */
44134     isUpdating : function(){
44135         return this.updating; 
44136     },
44137     
44138     /**
44139      * Suspend the LayoutManager from doing auto-layouts while
44140      * making multiple add or remove calls
44141      */
44142     beginUpdate : function(){
44143         this.updating = true;    
44144     },
44145     
44146     /**
44147      * Restore auto-layouts and optionally disable the manager from performing a layout
44148      * @param {Boolean} noLayout true to disable a layout update 
44149      */
44150     endUpdate : function(noLayout){
44151         this.updating = false;
44152         if(!noLayout){
44153             this.layout();
44154         }    
44155     },
44156     
44157     layout: function(){
44158         
44159     },
44160     
44161     onRegionResized : function(region, newSize){
44162         this.fireEvent("regionresized", region, newSize);
44163         this.layout();
44164     },
44165     
44166     onRegionCollapsed : function(region){
44167         this.fireEvent("regioncollapsed", region);
44168     },
44169     
44170     onRegionExpanded : function(region){
44171         this.fireEvent("regionexpanded", region);
44172     },
44173         
44174     /**
44175      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44176      * performs box-model adjustments.
44177      * @return {Object} The size as an object {width: (the width), height: (the height)}
44178      */
44179     getViewSize : function(){
44180         var size;
44181         if(this.el.dom != document.body){
44182             size = this.el.getSize();
44183         }else{
44184             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44185         }
44186         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44187         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44188         return size;
44189     },
44190     
44191     /**
44192      * Returns the Element this layout is bound to.
44193      * @return {Roo.Element}
44194      */
44195     getEl : function(){
44196         return this.el;
44197     },
44198     
44199     /**
44200      * Returns the specified region.
44201      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44202      * @return {Roo.LayoutRegion}
44203      */
44204     getRegion : function(target){
44205         return this.regions[target.toLowerCase()];
44206     },
44207     
44208     onWindowResize : function(){
44209         if(this.monitorWindowResize){
44210             this.layout();
44211         }
44212     }
44213 });/*
44214  * Based on:
44215  * Ext JS Library 1.1.1
44216  * Copyright(c) 2006-2007, Ext JS, LLC.
44217  *
44218  * Originally Released Under LGPL - original licence link has changed is not relivant.
44219  *
44220  * Fork - LGPL
44221  * <script type="text/javascript">
44222  */
44223 /**
44224  * @class Roo.BorderLayout
44225  * @extends Roo.LayoutManager
44226  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44227  * please see: <br><br>
44228  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
44229  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
44230  * Example:
44231  <pre><code>
44232  var layout = new Roo.BorderLayout(document.body, {
44233     north: {
44234         initialSize: 25,
44235         titlebar: false
44236     },
44237     west: {
44238         split:true,
44239         initialSize: 200,
44240         minSize: 175,
44241         maxSize: 400,
44242         titlebar: true,
44243         collapsible: true
44244     },
44245     east: {
44246         split:true,
44247         initialSize: 202,
44248         minSize: 175,
44249         maxSize: 400,
44250         titlebar: true,
44251         collapsible: true
44252     },
44253     south: {
44254         split:true,
44255         initialSize: 100,
44256         minSize: 100,
44257         maxSize: 200,
44258         titlebar: true,
44259         collapsible: true
44260     },
44261     center: {
44262         titlebar: true,
44263         autoScroll:true,
44264         resizeTabs: true,
44265         minTabWidth: 50,
44266         preferredTabWidth: 150
44267     }
44268 });
44269
44270 // shorthand
44271 var CP = Roo.ContentPanel;
44272
44273 layout.beginUpdate();
44274 layout.add("north", new CP("north", "North"));
44275 layout.add("south", new CP("south", {title: "South", closable: true}));
44276 layout.add("west", new CP("west", {title: "West"}));
44277 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44278 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44279 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44280 layout.getRegion("center").showPanel("center1");
44281 layout.endUpdate();
44282 </code></pre>
44283
44284 <b>The container the layout is rendered into can be either the body element or any other element.
44285 If it is not the body element, the container needs to either be an absolute positioned element,
44286 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44287 the container size if it is not the body element.</b>
44288
44289 * @constructor
44290 * Create a new BorderLayout
44291 * @param {String/HTMLElement/Element} container The container this layout is bound to
44292 * @param {Object} config Configuration options
44293  */
44294 Roo.BorderLayout = function(container, config){
44295     config = config || {};
44296     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44297     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44298     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44299         var target = this.factory.validRegions[i];
44300         if(config[target]){
44301             this.addRegion(target, config[target]);
44302         }
44303     }
44304 };
44305
44306 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44307     /**
44308      * Creates and adds a new region if it doesn't already exist.
44309      * @param {String} target The target region key (north, south, east, west or center).
44310      * @param {Object} config The regions config object
44311      * @return {BorderLayoutRegion} The new region
44312      */
44313     addRegion : function(target, config){
44314         if(!this.regions[target]){
44315             var r = this.factory.create(target, this, config);
44316             this.bindRegion(target, r);
44317         }
44318         return this.regions[target];
44319     },
44320
44321     // private (kinda)
44322     bindRegion : function(name, r){
44323         this.regions[name] = r;
44324         r.on("visibilitychange", this.layout, this);
44325         r.on("paneladded", this.layout, this);
44326         r.on("panelremoved", this.layout, this);
44327         r.on("invalidated", this.layout, this);
44328         r.on("resized", this.onRegionResized, this);
44329         r.on("collapsed", this.onRegionCollapsed, this);
44330         r.on("expanded", this.onRegionExpanded, this);
44331     },
44332
44333     /**
44334      * Performs a layout update.
44335      */
44336     layout : function(){
44337         if(this.updating) return;
44338         var size = this.getViewSize();
44339         var w = size.width;
44340         var h = size.height;
44341         var centerW = w;
44342         var centerH = h;
44343         var centerY = 0;
44344         var centerX = 0;
44345         //var x = 0, y = 0;
44346
44347         var rs = this.regions;
44348         var north = rs["north"];
44349         var south = rs["south"]; 
44350         var west = rs["west"];
44351         var east = rs["east"];
44352         var center = rs["center"];
44353         //if(this.hideOnLayout){ // not supported anymore
44354             //c.el.setStyle("display", "none");
44355         //}
44356         if(north && north.isVisible()){
44357             var b = north.getBox();
44358             var m = north.getMargins();
44359             b.width = w - (m.left+m.right);
44360             b.x = m.left;
44361             b.y = m.top;
44362             centerY = b.height + b.y + m.bottom;
44363             centerH -= centerY;
44364             north.updateBox(this.safeBox(b));
44365         }
44366         if(south && south.isVisible()){
44367             var b = south.getBox();
44368             var m = south.getMargins();
44369             b.width = w - (m.left+m.right);
44370             b.x = m.left;
44371             var totalHeight = (b.height + m.top + m.bottom);
44372             b.y = h - totalHeight + m.top;
44373             centerH -= totalHeight;
44374             south.updateBox(this.safeBox(b));
44375         }
44376         if(west && west.isVisible()){
44377             var b = west.getBox();
44378             var m = west.getMargins();
44379             b.height = centerH - (m.top+m.bottom);
44380             b.x = m.left;
44381             b.y = centerY + m.top;
44382             var totalWidth = (b.width + m.left + m.right);
44383             centerX += totalWidth;
44384             centerW -= totalWidth;
44385             west.updateBox(this.safeBox(b));
44386         }
44387         if(east && east.isVisible()){
44388             var b = east.getBox();
44389             var m = east.getMargins();
44390             b.height = centerH - (m.top+m.bottom);
44391             var totalWidth = (b.width + m.left + m.right);
44392             b.x = w - totalWidth + m.left;
44393             b.y = centerY + m.top;
44394             centerW -= totalWidth;
44395             east.updateBox(this.safeBox(b));
44396         }
44397         if(center){
44398             var m = center.getMargins();
44399             var centerBox = {
44400                 x: centerX + m.left,
44401                 y: centerY + m.top,
44402                 width: centerW - (m.left+m.right),
44403                 height: centerH - (m.top+m.bottom)
44404             };
44405             //if(this.hideOnLayout){
44406                 //center.el.setStyle("display", "block");
44407             //}
44408             center.updateBox(this.safeBox(centerBox));
44409         }
44410         this.el.repaint();
44411         this.fireEvent("layout", this);
44412     },
44413
44414     // private
44415     safeBox : function(box){
44416         box.width = Math.max(0, box.width);
44417         box.height = Math.max(0, box.height);
44418         return box;
44419     },
44420
44421     /**
44422      * Adds a ContentPanel (or subclass) to this layout.
44423      * @param {String} target The target region key (north, south, east, west or center).
44424      * @param {Roo.ContentPanel} panel The panel to add
44425      * @return {Roo.ContentPanel} The added panel
44426      */
44427     add : function(target, panel){
44428          
44429         target = target.toLowerCase();
44430         return this.regions[target].add(panel);
44431     },
44432
44433     /**
44434      * Remove a ContentPanel (or subclass) to this layout.
44435      * @param {String} target The target region key (north, south, east, west or center).
44436      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44437      * @return {Roo.ContentPanel} The removed panel
44438      */
44439     remove : function(target, panel){
44440         target = target.toLowerCase();
44441         return this.regions[target].remove(panel);
44442     },
44443
44444     /**
44445      * Searches all regions for a panel with the specified id
44446      * @param {String} panelId
44447      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44448      */
44449     findPanel : function(panelId){
44450         var rs = this.regions;
44451         for(var target in rs){
44452             if(typeof rs[target] != "function"){
44453                 var p = rs[target].getPanel(panelId);
44454                 if(p){
44455                     return p;
44456                 }
44457             }
44458         }
44459         return null;
44460     },
44461
44462     /**
44463      * Searches all regions for a panel with the specified id and activates (shows) it.
44464      * @param {String/ContentPanel} panelId The panels id or the panel itself
44465      * @return {Roo.ContentPanel} The shown panel or null
44466      */
44467     showPanel : function(panelId) {
44468       var rs = this.regions;
44469       for(var target in rs){
44470          var r = rs[target];
44471          if(typeof r != "function"){
44472             if(r.hasPanel(panelId)){
44473                return r.showPanel(panelId);
44474             }
44475          }
44476       }
44477       return null;
44478    },
44479
44480    /**
44481      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44482      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44483      */
44484     restoreState : function(provider){
44485         if(!provider){
44486             provider = Roo.state.Manager;
44487         }
44488         var sm = new Roo.LayoutStateManager();
44489         sm.init(this, provider);
44490     },
44491
44492     /**
44493      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44494      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44495      * a valid ContentPanel config object.  Example:
44496      * <pre><code>
44497 // Create the main layout
44498 var layout = new Roo.BorderLayout('main-ct', {
44499     west: {
44500         split:true,
44501         minSize: 175,
44502         titlebar: true
44503     },
44504     center: {
44505         title:'Components'
44506     }
44507 }, 'main-ct');
44508
44509 // Create and add multiple ContentPanels at once via configs
44510 layout.batchAdd({
44511    west: {
44512        id: 'source-files',
44513        autoCreate:true,
44514        title:'Ext Source Files',
44515        autoScroll:true,
44516        fitToFrame:true
44517    },
44518    center : {
44519        el: cview,
44520        autoScroll:true,
44521        fitToFrame:true,
44522        toolbar: tb,
44523        resizeEl:'cbody'
44524    }
44525 });
44526 </code></pre>
44527      * @param {Object} regions An object containing ContentPanel configs by region name
44528      */
44529     batchAdd : function(regions){
44530         this.beginUpdate();
44531         for(var rname in regions){
44532             var lr = this.regions[rname];
44533             if(lr){
44534                 this.addTypedPanels(lr, regions[rname]);
44535             }
44536         }
44537         this.endUpdate();
44538     },
44539
44540     // private
44541     addTypedPanels : function(lr, ps){
44542         if(typeof ps == 'string'){
44543             lr.add(new Roo.ContentPanel(ps));
44544         }
44545         else if(ps instanceof Array){
44546             for(var i =0, len = ps.length; i < len; i++){
44547                 this.addTypedPanels(lr, ps[i]);
44548             }
44549         }
44550         else if(!ps.events){ // raw config?
44551             var el = ps.el;
44552             delete ps.el; // prevent conflict
44553             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44554         }
44555         else {  // panel object assumed!
44556             lr.add(ps);
44557         }
44558     },
44559     /**
44560      * Adds a xtype elements to the layout.
44561      * <pre><code>
44562
44563 layout.addxtype({
44564        xtype : 'ContentPanel',
44565        region: 'west',
44566        items: [ .... ]
44567    }
44568 );
44569
44570 layout.addxtype({
44571         xtype : 'NestedLayoutPanel',
44572         region: 'west',
44573         layout: {
44574            center: { },
44575            west: { }   
44576         },
44577         items : [ ... list of content panels or nested layout panels.. ]
44578    }
44579 );
44580 </code></pre>
44581      * @param {Object} cfg Xtype definition of item to add.
44582      */
44583     addxtype : function(cfg)
44584     {
44585         // basically accepts a pannel...
44586         // can accept a layout region..!?!?
44587        // console.log('BorderLayout add ' + cfg.xtype)
44588         
44589         if (!cfg.xtype.match(/Panel$/)) {
44590             return false;
44591         }
44592         var ret = false;
44593         var region = cfg.region;
44594         delete cfg.region;
44595         
44596           
44597         var xitems = [];
44598         if (cfg.items) {
44599             xitems = cfg.items;
44600             delete cfg.items;
44601         }
44602         
44603         
44604         switch(cfg.xtype) 
44605         {
44606             case 'ContentPanel':  // ContentPanel (el, cfg)
44607             case 'ScrollPanel':  // ContentPanel (el, cfg)
44608                 if(cfg.autoCreate) {
44609                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44610                 } else {
44611                     var el = this.el.createChild();
44612                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44613                 }
44614                 
44615                 this.add(region, ret);
44616                 break;
44617             
44618             
44619             case 'TreePanel': // our new panel!
44620                 cfg.el = this.el.createChild();
44621                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44622                 this.add(region, ret);
44623                 break;
44624             
44625             case 'NestedLayoutPanel': 
44626                 // create a new Layout (which is  a Border Layout...
44627                 var el = this.el.createChild();
44628                 var clayout = cfg.layout;
44629                 delete cfg.layout;
44630                 clayout.items   = clayout.items  || [];
44631                 // replace this exitems with the clayout ones..
44632                 xitems = clayout.items;
44633                  
44634                 
44635                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44636                     cfg.background = false;
44637                 }
44638                 var layout = new Roo.BorderLayout(el, clayout);
44639                 
44640                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44641                 //console.log('adding nested layout panel '  + cfg.toSource());
44642                 this.add(region, ret);
44643                 
44644                 break;
44645                 
44646             case 'GridPanel': 
44647             
44648                 // needs grid and region
44649                 
44650                 //var el = this.getRegion(region).el.createChild();
44651                 var el = this.el.createChild();
44652                 // create the grid first...
44653                 
44654                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44655                 delete cfg.grid;
44656                 if (region == 'center' && this.active ) {
44657                     cfg.background = false;
44658                 }
44659                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44660                 
44661                 this.add(region, ret);
44662                 if (cfg.background) {
44663                     ret.on('activate', function(gp) {
44664                         if (!gp.grid.rendered) {
44665                             gp.grid.render();
44666                         }
44667                     });
44668                 } else {
44669                     grid.render();
44670                 }
44671                 break;
44672            
44673                
44674                 
44675                 
44676             default: 
44677                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44678                 return null;
44679              // GridPanel (grid, cfg)
44680             
44681         }
44682         this.beginUpdate();
44683         // add children..
44684         Roo.each(xitems, function(i)  {
44685             ret.addxtype(i);
44686         });
44687         this.endUpdate();
44688         return ret;
44689         
44690     }
44691 });
44692
44693 /**
44694  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44695  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44696  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44697  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44698  * <pre><code>
44699 // shorthand
44700 var CP = Roo.ContentPanel;
44701
44702 var layout = Roo.BorderLayout.create({
44703     north: {
44704         initialSize: 25,
44705         titlebar: false,
44706         panels: [new CP("north", "North")]
44707     },
44708     west: {
44709         split:true,
44710         initialSize: 200,
44711         minSize: 175,
44712         maxSize: 400,
44713         titlebar: true,
44714         collapsible: true,
44715         panels: [new CP("west", {title: "West"})]
44716     },
44717     east: {
44718         split:true,
44719         initialSize: 202,
44720         minSize: 175,
44721         maxSize: 400,
44722         titlebar: true,
44723         collapsible: true,
44724         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44725     },
44726     south: {
44727         split:true,
44728         initialSize: 100,
44729         minSize: 100,
44730         maxSize: 200,
44731         titlebar: true,
44732         collapsible: true,
44733         panels: [new CP("south", {title: "South", closable: true})]
44734     },
44735     center: {
44736         titlebar: true,
44737         autoScroll:true,
44738         resizeTabs: true,
44739         minTabWidth: 50,
44740         preferredTabWidth: 150,
44741         panels: [
44742             new CP("center1", {title: "Close Me", closable: true}),
44743             new CP("center2", {title: "Center Panel", closable: false})
44744         ]
44745     }
44746 }, document.body);
44747
44748 layout.getRegion("center").showPanel("center1");
44749 </code></pre>
44750  * @param config
44751  * @param targetEl
44752  */
44753 Roo.BorderLayout.create = function(config, targetEl){
44754     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44755     layout.beginUpdate();
44756     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44757     for(var j = 0, jlen = regions.length; j < jlen; j++){
44758         var lr = regions[j];
44759         if(layout.regions[lr] && config[lr].panels){
44760             var r = layout.regions[lr];
44761             var ps = config[lr].panels;
44762             layout.addTypedPanels(r, ps);
44763         }
44764     }
44765     layout.endUpdate();
44766     return layout;
44767 };
44768
44769 // private
44770 Roo.BorderLayout.RegionFactory = {
44771     // private
44772     validRegions : ["north","south","east","west","center"],
44773
44774     // private
44775     create : function(target, mgr, config){
44776         target = target.toLowerCase();
44777         if(config.lightweight || config.basic){
44778             return new Roo.BasicLayoutRegion(mgr, config, target);
44779         }
44780         switch(target){
44781             case "north":
44782                 return new Roo.NorthLayoutRegion(mgr, config);
44783             case "south":
44784                 return new Roo.SouthLayoutRegion(mgr, config);
44785             case "east":
44786                 return new Roo.EastLayoutRegion(mgr, config);
44787             case "west":
44788                 return new Roo.WestLayoutRegion(mgr, config);
44789             case "center":
44790                 return new Roo.CenterLayoutRegion(mgr, config);
44791         }
44792         throw 'Layout region "'+target+'" not supported.';
44793     }
44794 };/*
44795  * Based on:
44796  * Ext JS Library 1.1.1
44797  * Copyright(c) 2006-2007, Ext JS, LLC.
44798  *
44799  * Originally Released Under LGPL - original licence link has changed is not relivant.
44800  *
44801  * Fork - LGPL
44802  * <script type="text/javascript">
44803  */
44804  
44805 /**
44806  * @class Roo.BasicLayoutRegion
44807  * @extends Roo.util.Observable
44808  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44809  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44810  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44811  */
44812 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44813     this.mgr = mgr;
44814     this.position  = pos;
44815     this.events = {
44816         /**
44817          * @scope Roo.BasicLayoutRegion
44818          */
44819         
44820         /**
44821          * @event beforeremove
44822          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44823          * @param {Roo.LayoutRegion} this
44824          * @param {Roo.ContentPanel} panel The panel
44825          * @param {Object} e The cancel event object
44826          */
44827         "beforeremove" : true,
44828         /**
44829          * @event invalidated
44830          * Fires when the layout for this region is changed.
44831          * @param {Roo.LayoutRegion} this
44832          */
44833         "invalidated" : true,
44834         /**
44835          * @event visibilitychange
44836          * Fires when this region is shown or hidden 
44837          * @param {Roo.LayoutRegion} this
44838          * @param {Boolean} visibility true or false
44839          */
44840         "visibilitychange" : true,
44841         /**
44842          * @event paneladded
44843          * Fires when a panel is added. 
44844          * @param {Roo.LayoutRegion} this
44845          * @param {Roo.ContentPanel} panel The panel
44846          */
44847         "paneladded" : true,
44848         /**
44849          * @event panelremoved
44850          * Fires when a panel is removed. 
44851          * @param {Roo.LayoutRegion} this
44852          * @param {Roo.ContentPanel} panel The panel
44853          */
44854         "panelremoved" : true,
44855         /**
44856          * @event collapsed
44857          * Fires when this region is collapsed.
44858          * @param {Roo.LayoutRegion} this
44859          */
44860         "collapsed" : true,
44861         /**
44862          * @event expanded
44863          * Fires when this region is expanded.
44864          * @param {Roo.LayoutRegion} this
44865          */
44866         "expanded" : true,
44867         /**
44868          * @event slideshow
44869          * Fires when this region is slid into view.
44870          * @param {Roo.LayoutRegion} this
44871          */
44872         "slideshow" : true,
44873         /**
44874          * @event slidehide
44875          * Fires when this region slides out of view. 
44876          * @param {Roo.LayoutRegion} this
44877          */
44878         "slidehide" : true,
44879         /**
44880          * @event panelactivated
44881          * Fires when a panel is activated. 
44882          * @param {Roo.LayoutRegion} this
44883          * @param {Roo.ContentPanel} panel The activated panel
44884          */
44885         "panelactivated" : true,
44886         /**
44887          * @event resized
44888          * Fires when the user resizes this region. 
44889          * @param {Roo.LayoutRegion} this
44890          * @param {Number} newSize The new size (width for east/west, height for north/south)
44891          */
44892         "resized" : true
44893     };
44894     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44895     this.panels = new Roo.util.MixedCollection();
44896     this.panels.getKey = this.getPanelId.createDelegate(this);
44897     this.box = null;
44898     this.activePanel = null;
44899     // ensure listeners are added...
44900     
44901     if (config.listeners || config.events) {
44902         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44903             listeners : config.listeners || {},
44904             events : config.events || {}
44905         });
44906     }
44907     
44908     if(skipConfig !== true){
44909         this.applyConfig(config);
44910     }
44911 };
44912
44913 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44914     getPanelId : function(p){
44915         return p.getId();
44916     },
44917     
44918     applyConfig : function(config){
44919         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44920         this.config = config;
44921         
44922     },
44923     
44924     /**
44925      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44926      * the width, for horizontal (north, south) the height.
44927      * @param {Number} newSize The new width or height
44928      */
44929     resizeTo : function(newSize){
44930         var el = this.el ? this.el :
44931                  (this.activePanel ? this.activePanel.getEl() : null);
44932         if(el){
44933             switch(this.position){
44934                 case "east":
44935                 case "west":
44936                     el.setWidth(newSize);
44937                     this.fireEvent("resized", this, newSize);
44938                 break;
44939                 case "north":
44940                 case "south":
44941                     el.setHeight(newSize);
44942                     this.fireEvent("resized", this, newSize);
44943                 break;                
44944             }
44945         }
44946     },
44947     
44948     getBox : function(){
44949         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44950     },
44951     
44952     getMargins : function(){
44953         return this.margins;
44954     },
44955     
44956     updateBox : function(box){
44957         this.box = box;
44958         var el = this.activePanel.getEl();
44959         el.dom.style.left = box.x + "px";
44960         el.dom.style.top = box.y + "px";
44961         this.activePanel.setSize(box.width, box.height);
44962     },
44963     
44964     /**
44965      * Returns the container element for this region.
44966      * @return {Roo.Element}
44967      */
44968     getEl : function(){
44969         return this.activePanel;
44970     },
44971     
44972     /**
44973      * Returns true if this region is currently visible.
44974      * @return {Boolean}
44975      */
44976     isVisible : function(){
44977         return this.activePanel ? true : false;
44978     },
44979     
44980     setActivePanel : function(panel){
44981         panel = this.getPanel(panel);
44982         if(this.activePanel && this.activePanel != panel){
44983             this.activePanel.setActiveState(false);
44984             this.activePanel.getEl().setLeftTop(-10000,-10000);
44985         }
44986         this.activePanel = panel;
44987         panel.setActiveState(true);
44988         if(this.box){
44989             panel.setSize(this.box.width, this.box.height);
44990         }
44991         this.fireEvent("panelactivated", this, panel);
44992         this.fireEvent("invalidated");
44993     },
44994     
44995     /**
44996      * Show the specified panel.
44997      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44998      * @return {Roo.ContentPanel} The shown panel or null
44999      */
45000     showPanel : function(panel){
45001         if(panel = this.getPanel(panel)){
45002             this.setActivePanel(panel);
45003         }
45004         return panel;
45005     },
45006     
45007     /**
45008      * Get the active panel for this region.
45009      * @return {Roo.ContentPanel} The active panel or null
45010      */
45011     getActivePanel : function(){
45012         return this.activePanel;
45013     },
45014     
45015     /**
45016      * Add the passed ContentPanel(s)
45017      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45018      * @return {Roo.ContentPanel} The panel added (if only one was added)
45019      */
45020     add : function(panel){
45021         if(arguments.length > 1){
45022             for(var i = 0, len = arguments.length; i < len; i++) {
45023                 this.add(arguments[i]);
45024             }
45025             return null;
45026         }
45027         if(this.hasPanel(panel)){
45028             this.showPanel(panel);
45029             return panel;
45030         }
45031         var el = panel.getEl();
45032         if(el.dom.parentNode != this.mgr.el.dom){
45033             this.mgr.el.dom.appendChild(el.dom);
45034         }
45035         if(panel.setRegion){
45036             panel.setRegion(this);
45037         }
45038         this.panels.add(panel);
45039         el.setStyle("position", "absolute");
45040         if(!panel.background){
45041             this.setActivePanel(panel);
45042             if(this.config.initialSize && this.panels.getCount()==1){
45043                 this.resizeTo(this.config.initialSize);
45044             }
45045         }
45046         this.fireEvent("paneladded", this, panel);
45047         return panel;
45048     },
45049     
45050     /**
45051      * Returns true if the panel is in this region.
45052      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45053      * @return {Boolean}
45054      */
45055     hasPanel : function(panel){
45056         if(typeof panel == "object"){ // must be panel obj
45057             panel = panel.getId();
45058         }
45059         return this.getPanel(panel) ? true : false;
45060     },
45061     
45062     /**
45063      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45064      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45065      * @param {Boolean} preservePanel Overrides the config preservePanel option
45066      * @return {Roo.ContentPanel} The panel that was removed
45067      */
45068     remove : function(panel, preservePanel){
45069         panel = this.getPanel(panel);
45070         if(!panel){
45071             return null;
45072         }
45073         var e = {};
45074         this.fireEvent("beforeremove", this, panel, e);
45075         if(e.cancel === true){
45076             return null;
45077         }
45078         var panelId = panel.getId();
45079         this.panels.removeKey(panelId);
45080         return panel;
45081     },
45082     
45083     /**
45084      * Returns the panel specified or null if it's not in this region.
45085      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45086      * @return {Roo.ContentPanel}
45087      */
45088     getPanel : function(id){
45089         if(typeof id == "object"){ // must be panel obj
45090             return id;
45091         }
45092         return this.panels.get(id);
45093     },
45094     
45095     /**
45096      * Returns this regions position (north/south/east/west/center).
45097      * @return {String} 
45098      */
45099     getPosition: function(){
45100         return this.position;    
45101     }
45102 });/*
45103  * Based on:
45104  * Ext JS Library 1.1.1
45105  * Copyright(c) 2006-2007, Ext JS, LLC.
45106  *
45107  * Originally Released Under LGPL - original licence link has changed is not relivant.
45108  *
45109  * Fork - LGPL
45110  * <script type="text/javascript">
45111  */
45112  
45113 /**
45114  * @class Roo.LayoutRegion
45115  * @extends Roo.BasicLayoutRegion
45116  * This class represents a region in a layout manager.
45117  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45118  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45119  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45120  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45121  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
45122  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45123  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45124  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45125  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45126  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45127  * @cfg {String}    title           The title for the region (overrides panel titles)
45128  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45129  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45130  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45131  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45132  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45133  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45134  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45135  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45136  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45137  * @cfg {Boolean}   showPin         True to show a pin button
45138  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45139  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45140  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45141  * @cfg {Number}    width           For East/West panels
45142  * @cfg {Number}    height          For North/South panels
45143  * @cfg {Boolean}   split           To show the splitter
45144  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45145  */
45146 Roo.LayoutRegion = function(mgr, config, pos){
45147     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45148     var dh = Roo.DomHelper;
45149     /** This region's container element 
45150     * @type Roo.Element */
45151     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45152     /** This region's title element 
45153     * @type Roo.Element */
45154
45155     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45156         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45157         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45158     ]}, true);
45159     this.titleEl.enableDisplayMode();
45160     /** This region's title text element 
45161     * @type HTMLElement */
45162     this.titleTextEl = this.titleEl.dom.firstChild;
45163     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45164     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45165     this.closeBtn.enableDisplayMode();
45166     this.closeBtn.on("click", this.closeClicked, this);
45167     this.closeBtn.hide();
45168
45169     this.createBody(config);
45170     this.visible = true;
45171     this.collapsed = false;
45172
45173     if(config.hideWhenEmpty){
45174         this.hide();
45175         this.on("paneladded", this.validateVisibility, this);
45176         this.on("panelremoved", this.validateVisibility, this);
45177     }
45178     this.applyConfig(config);
45179 };
45180
45181 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45182
45183     createBody : function(){
45184         /** This region's body element 
45185         * @type Roo.Element */
45186         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45187     },
45188
45189     applyConfig : function(c){
45190         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45191             var dh = Roo.DomHelper;
45192             if(c.titlebar !== false){
45193                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45194                 this.collapseBtn.on("click", this.collapse, this);
45195                 this.collapseBtn.enableDisplayMode();
45196
45197                 if(c.showPin === true || this.showPin){
45198                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45199                     this.stickBtn.enableDisplayMode();
45200                     this.stickBtn.on("click", this.expand, this);
45201                     this.stickBtn.hide();
45202                 }
45203             }
45204             /** This region's collapsed element
45205             * @type Roo.Element */
45206             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45207                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45208             ]}, true);
45209             if(c.floatable !== false){
45210                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45211                this.collapsedEl.on("click", this.collapseClick, this);
45212             }
45213
45214             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45215                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45216                    id: "message", unselectable: "on", style:{"float":"left"}});
45217                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45218              }
45219             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45220             this.expandBtn.on("click", this.expand, this);
45221         }
45222         if(this.collapseBtn){
45223             this.collapseBtn.setVisible(c.collapsible == true);
45224         }
45225         this.cmargins = c.cmargins || this.cmargins ||
45226                          (this.position == "west" || this.position == "east" ?
45227                              {top: 0, left: 2, right:2, bottom: 0} :
45228                              {top: 2, left: 0, right:0, bottom: 2});
45229         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45230         this.bottomTabs = c.tabPosition != "top";
45231         this.autoScroll = c.autoScroll || false;
45232         if(this.autoScroll){
45233             this.bodyEl.setStyle("overflow", "auto");
45234         }else{
45235             this.bodyEl.setStyle("overflow", "hidden");
45236         }
45237         //if(c.titlebar !== false){
45238             if((!c.titlebar && !c.title) || c.titlebar === false){
45239                 this.titleEl.hide();
45240             }else{
45241                 this.titleEl.show();
45242                 if(c.title){
45243                     this.titleTextEl.innerHTML = c.title;
45244                 }
45245             }
45246         //}
45247         this.duration = c.duration || .30;
45248         this.slideDuration = c.slideDuration || .45;
45249         this.config = c;
45250         if(c.collapsed){
45251             this.collapse(true);
45252         }
45253         if(c.hidden){
45254             this.hide();
45255         }
45256     },
45257     /**
45258      * Returns true if this region is currently visible.
45259      * @return {Boolean}
45260      */
45261     isVisible : function(){
45262         return this.visible;
45263     },
45264
45265     /**
45266      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45267      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45268      */
45269     setCollapsedTitle : function(title){
45270         title = title || "&#160;";
45271         if(this.collapsedTitleTextEl){
45272             this.collapsedTitleTextEl.innerHTML = title;
45273         }
45274     },
45275
45276     getBox : function(){
45277         var b;
45278         if(!this.collapsed){
45279             b = this.el.getBox(false, true);
45280         }else{
45281             b = this.collapsedEl.getBox(false, true);
45282         }
45283         return b;
45284     },
45285
45286     getMargins : function(){
45287         return this.collapsed ? this.cmargins : this.margins;
45288     },
45289
45290     highlight : function(){
45291         this.el.addClass("x-layout-panel-dragover");
45292     },
45293
45294     unhighlight : function(){
45295         this.el.removeClass("x-layout-panel-dragover");
45296     },
45297
45298     updateBox : function(box){
45299         this.box = box;
45300         if(!this.collapsed){
45301             this.el.dom.style.left = box.x + "px";
45302             this.el.dom.style.top = box.y + "px";
45303             this.updateBody(box.width, box.height);
45304         }else{
45305             this.collapsedEl.dom.style.left = box.x + "px";
45306             this.collapsedEl.dom.style.top = box.y + "px";
45307             this.collapsedEl.setSize(box.width, box.height);
45308         }
45309         if(this.tabs){
45310             this.tabs.autoSizeTabs();
45311         }
45312     },
45313
45314     updateBody : function(w, h){
45315         if(w !== null){
45316             this.el.setWidth(w);
45317             w -= this.el.getBorderWidth("rl");
45318             if(this.config.adjustments){
45319                 w += this.config.adjustments[0];
45320             }
45321         }
45322         if(h !== null){
45323             this.el.setHeight(h);
45324             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45325             h -= this.el.getBorderWidth("tb");
45326             if(this.config.adjustments){
45327                 h += this.config.adjustments[1];
45328             }
45329             this.bodyEl.setHeight(h);
45330             if(this.tabs){
45331                 h = this.tabs.syncHeight(h);
45332             }
45333         }
45334         if(this.panelSize){
45335             w = w !== null ? w : this.panelSize.width;
45336             h = h !== null ? h : this.panelSize.height;
45337         }
45338         if(this.activePanel){
45339             var el = this.activePanel.getEl();
45340             w = w !== null ? w : el.getWidth();
45341             h = h !== null ? h : el.getHeight();
45342             this.panelSize = {width: w, height: h};
45343             this.activePanel.setSize(w, h);
45344         }
45345         if(Roo.isIE && this.tabs){
45346             this.tabs.el.repaint();
45347         }
45348     },
45349
45350     /**
45351      * Returns the container element for this region.
45352      * @return {Roo.Element}
45353      */
45354     getEl : function(){
45355         return this.el;
45356     },
45357
45358     /**
45359      * Hides this region.
45360      */
45361     hide : function(){
45362         if(!this.collapsed){
45363             this.el.dom.style.left = "-2000px";
45364             this.el.hide();
45365         }else{
45366             this.collapsedEl.dom.style.left = "-2000px";
45367             this.collapsedEl.hide();
45368         }
45369         this.visible = false;
45370         this.fireEvent("visibilitychange", this, false);
45371     },
45372
45373     /**
45374      * Shows this region if it was previously hidden.
45375      */
45376     show : function(){
45377         if(!this.collapsed){
45378             this.el.show();
45379         }else{
45380             this.collapsedEl.show();
45381         }
45382         this.visible = true;
45383         this.fireEvent("visibilitychange", this, true);
45384     },
45385
45386     closeClicked : function(){
45387         if(this.activePanel){
45388             this.remove(this.activePanel);
45389         }
45390     },
45391
45392     collapseClick : function(e){
45393         if(this.isSlid){
45394            e.stopPropagation();
45395            this.slideIn();
45396         }else{
45397            e.stopPropagation();
45398            this.slideOut();
45399         }
45400     },
45401
45402     /**
45403      * Collapses this region.
45404      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45405      */
45406     collapse : function(skipAnim){
45407         if(this.collapsed) return;
45408         this.collapsed = true;
45409         if(this.split){
45410             this.split.el.hide();
45411         }
45412         if(this.config.animate && skipAnim !== true){
45413             this.fireEvent("invalidated", this);
45414             this.animateCollapse();
45415         }else{
45416             this.el.setLocation(-20000,-20000);
45417             this.el.hide();
45418             this.collapsedEl.show();
45419             this.fireEvent("collapsed", this);
45420             this.fireEvent("invalidated", this);
45421         }
45422     },
45423
45424     animateCollapse : function(){
45425         // overridden
45426     },
45427
45428     /**
45429      * Expands this region if it was previously collapsed.
45430      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45431      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45432      */
45433     expand : function(e, skipAnim){
45434         if(e) e.stopPropagation();
45435         if(!this.collapsed || this.el.hasActiveFx()) return;
45436         if(this.isSlid){
45437             this.afterSlideIn();
45438             skipAnim = true;
45439         }
45440         this.collapsed = false;
45441         if(this.config.animate && skipAnim !== true){
45442             this.animateExpand();
45443         }else{
45444             this.el.show();
45445             if(this.split){
45446                 this.split.el.show();
45447             }
45448             this.collapsedEl.setLocation(-2000,-2000);
45449             this.collapsedEl.hide();
45450             this.fireEvent("invalidated", this);
45451             this.fireEvent("expanded", this);
45452         }
45453     },
45454
45455     animateExpand : function(){
45456         // overridden
45457     },
45458
45459     initTabs : function()
45460     {
45461         this.bodyEl.setStyle("overflow", "hidden");
45462         var ts = new Roo.TabPanel(
45463                 this.bodyEl.dom,
45464                 {
45465                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45466                     disableTooltips: this.config.disableTabTips,
45467                     toolbar : this.config.toolbar
45468                 }
45469         );
45470         if(this.config.hideTabs){
45471             ts.stripWrap.setDisplayed(false);
45472         }
45473         this.tabs = ts;
45474         ts.resizeTabs = this.config.resizeTabs === true;
45475         ts.minTabWidth = this.config.minTabWidth || 40;
45476         ts.maxTabWidth = this.config.maxTabWidth || 250;
45477         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45478         ts.monitorResize = false;
45479         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45480         ts.bodyEl.addClass('x-layout-tabs-body');
45481         this.panels.each(this.initPanelAsTab, this);
45482     },
45483
45484     initPanelAsTab : function(panel){
45485         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45486                     this.config.closeOnTab && panel.isClosable());
45487         if(panel.tabTip !== undefined){
45488             ti.setTooltip(panel.tabTip);
45489         }
45490         ti.on("activate", function(){
45491               this.setActivePanel(panel);
45492         }, this);
45493         if(this.config.closeOnTab){
45494             ti.on("beforeclose", function(t, e){
45495                 e.cancel = true;
45496                 this.remove(panel);
45497             }, this);
45498         }
45499         return ti;
45500     },
45501
45502     updatePanelTitle : function(panel, title){
45503         if(this.activePanel == panel){
45504             this.updateTitle(title);
45505         }
45506         if(this.tabs){
45507             var ti = this.tabs.getTab(panel.getEl().id);
45508             ti.setText(title);
45509             if(panel.tabTip !== undefined){
45510                 ti.setTooltip(panel.tabTip);
45511             }
45512         }
45513     },
45514
45515     updateTitle : function(title){
45516         if(this.titleTextEl && !this.config.title){
45517             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45518         }
45519     },
45520
45521     setActivePanel : function(panel){
45522         panel = this.getPanel(panel);
45523         if(this.activePanel && this.activePanel != panel){
45524             this.activePanel.setActiveState(false);
45525         }
45526         this.activePanel = panel;
45527         panel.setActiveState(true);
45528         if(this.panelSize){
45529             panel.setSize(this.panelSize.width, this.panelSize.height);
45530         }
45531         if(this.closeBtn){
45532             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45533         }
45534         this.updateTitle(panel.getTitle());
45535         if(this.tabs){
45536             this.fireEvent("invalidated", this);
45537         }
45538         this.fireEvent("panelactivated", this, panel);
45539     },
45540
45541     /**
45542      * Shows the specified panel.
45543      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45544      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45545      */
45546     showPanel : function(panel){
45547         if(panel = this.getPanel(panel)){
45548             if(this.tabs){
45549                 var tab = this.tabs.getTab(panel.getEl().id);
45550                 if(tab.isHidden()){
45551                     this.tabs.unhideTab(tab.id);
45552                 }
45553                 tab.activate();
45554             }else{
45555                 this.setActivePanel(panel);
45556             }
45557         }
45558         return panel;
45559     },
45560
45561     /**
45562      * Get the active panel for this region.
45563      * @return {Roo.ContentPanel} The active panel or null
45564      */
45565     getActivePanel : function(){
45566         return this.activePanel;
45567     },
45568
45569     validateVisibility : function(){
45570         if(this.panels.getCount() < 1){
45571             this.updateTitle("&#160;");
45572             this.closeBtn.hide();
45573             this.hide();
45574         }else{
45575             if(!this.isVisible()){
45576                 this.show();
45577             }
45578         }
45579     },
45580
45581     /**
45582      * Adds the passed ContentPanel(s) to this region.
45583      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45584      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45585      */
45586     add : function(panel){
45587         if(arguments.length > 1){
45588             for(var i = 0, len = arguments.length; i < len; i++) {
45589                 this.add(arguments[i]);
45590             }
45591             return null;
45592         }
45593         if(this.hasPanel(panel)){
45594             this.showPanel(panel);
45595             return panel;
45596         }
45597         panel.setRegion(this);
45598         this.panels.add(panel);
45599         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45600             this.bodyEl.dom.appendChild(panel.getEl().dom);
45601             if(panel.background !== true){
45602                 this.setActivePanel(panel);
45603             }
45604             this.fireEvent("paneladded", this, panel);
45605             return panel;
45606         }
45607         if(!this.tabs){
45608             this.initTabs();
45609         }else{
45610             this.initPanelAsTab(panel);
45611         }
45612         if(panel.background !== true){
45613             this.tabs.activate(panel.getEl().id);
45614         }
45615         this.fireEvent("paneladded", this, panel);
45616         return panel;
45617     },
45618
45619     /**
45620      * Hides the tab for the specified panel.
45621      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45622      */
45623     hidePanel : function(panel){
45624         if(this.tabs && (panel = this.getPanel(panel))){
45625             this.tabs.hideTab(panel.getEl().id);
45626         }
45627     },
45628
45629     /**
45630      * Unhides the tab for a previously hidden panel.
45631      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45632      */
45633     unhidePanel : function(panel){
45634         if(this.tabs && (panel = this.getPanel(panel))){
45635             this.tabs.unhideTab(panel.getEl().id);
45636         }
45637     },
45638
45639     clearPanels : function(){
45640         while(this.panels.getCount() > 0){
45641              this.remove(this.panels.first());
45642         }
45643     },
45644
45645     /**
45646      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45647      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45648      * @param {Boolean} preservePanel Overrides the config preservePanel option
45649      * @return {Roo.ContentPanel} The panel that was removed
45650      */
45651     remove : function(panel, preservePanel){
45652         panel = this.getPanel(panel);
45653         if(!panel){
45654             return null;
45655         }
45656         var e = {};
45657         this.fireEvent("beforeremove", this, panel, e);
45658         if(e.cancel === true){
45659             return null;
45660         }
45661         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45662         var panelId = panel.getId();
45663         this.panels.removeKey(panelId);
45664         if(preservePanel){
45665             document.body.appendChild(panel.getEl().dom);
45666         }
45667         if(this.tabs){
45668             this.tabs.removeTab(panel.getEl().id);
45669         }else if (!preservePanel){
45670             this.bodyEl.dom.removeChild(panel.getEl().dom);
45671         }
45672         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45673             var p = this.panels.first();
45674             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45675             tempEl.appendChild(p.getEl().dom);
45676             this.bodyEl.update("");
45677             this.bodyEl.dom.appendChild(p.getEl().dom);
45678             tempEl = null;
45679             this.updateTitle(p.getTitle());
45680             this.tabs = null;
45681             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45682             this.setActivePanel(p);
45683         }
45684         panel.setRegion(null);
45685         if(this.activePanel == panel){
45686             this.activePanel = null;
45687         }
45688         if(this.config.autoDestroy !== false && preservePanel !== true){
45689             try{panel.destroy();}catch(e){}
45690         }
45691         this.fireEvent("panelremoved", this, panel);
45692         return panel;
45693     },
45694
45695     /**
45696      * Returns the TabPanel component used by this region
45697      * @return {Roo.TabPanel}
45698      */
45699     getTabs : function(){
45700         return this.tabs;
45701     },
45702
45703     createTool : function(parentEl, className){
45704         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45705             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45706         btn.addClassOnOver("x-layout-tools-button-over");
45707         return btn;
45708     }
45709 });/*
45710  * Based on:
45711  * Ext JS Library 1.1.1
45712  * Copyright(c) 2006-2007, Ext JS, LLC.
45713  *
45714  * Originally Released Under LGPL - original licence link has changed is not relivant.
45715  *
45716  * Fork - LGPL
45717  * <script type="text/javascript">
45718  */
45719  
45720
45721
45722 /**
45723  * @class Roo.SplitLayoutRegion
45724  * @extends Roo.LayoutRegion
45725  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45726  */
45727 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45728     this.cursor = cursor;
45729     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45730 };
45731
45732 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45733     splitTip : "Drag to resize.",
45734     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45735     useSplitTips : false,
45736
45737     applyConfig : function(config){
45738         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45739         if(config.split){
45740             if(!this.split){
45741                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45742                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45743                 /** The SplitBar for this region 
45744                 * @type Roo.SplitBar */
45745                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45746                 this.split.on("moved", this.onSplitMove, this);
45747                 this.split.useShim = config.useShim === true;
45748                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45749                 if(this.useSplitTips){
45750                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45751                 }
45752                 if(config.collapsible){
45753                     this.split.el.on("dblclick", this.collapse,  this);
45754                 }
45755             }
45756             if(typeof config.minSize != "undefined"){
45757                 this.split.minSize = config.minSize;
45758             }
45759             if(typeof config.maxSize != "undefined"){
45760                 this.split.maxSize = config.maxSize;
45761             }
45762             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45763                 this.hideSplitter();
45764             }
45765         }
45766     },
45767
45768     getHMaxSize : function(){
45769          var cmax = this.config.maxSize || 10000;
45770          var center = this.mgr.getRegion("center");
45771          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45772     },
45773
45774     getVMaxSize : function(){
45775          var cmax = this.config.maxSize || 10000;
45776          var center = this.mgr.getRegion("center");
45777          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45778     },
45779
45780     onSplitMove : function(split, newSize){
45781         this.fireEvent("resized", this, newSize);
45782     },
45783     
45784     /** 
45785      * Returns the {@link Roo.SplitBar} for this region.
45786      * @return {Roo.SplitBar}
45787      */
45788     getSplitBar : function(){
45789         return this.split;
45790     },
45791     
45792     hide : function(){
45793         this.hideSplitter();
45794         Roo.SplitLayoutRegion.superclass.hide.call(this);
45795     },
45796
45797     hideSplitter : function(){
45798         if(this.split){
45799             this.split.el.setLocation(-2000,-2000);
45800             this.split.el.hide();
45801         }
45802     },
45803
45804     show : function(){
45805         if(this.split){
45806             this.split.el.show();
45807         }
45808         Roo.SplitLayoutRegion.superclass.show.call(this);
45809     },
45810     
45811     beforeSlide: function(){
45812         if(Roo.isGecko){// firefox overflow auto bug workaround
45813             this.bodyEl.clip();
45814             if(this.tabs) this.tabs.bodyEl.clip();
45815             if(this.activePanel){
45816                 this.activePanel.getEl().clip();
45817                 
45818                 if(this.activePanel.beforeSlide){
45819                     this.activePanel.beforeSlide();
45820                 }
45821             }
45822         }
45823     },
45824     
45825     afterSlide : function(){
45826         if(Roo.isGecko){// firefox overflow auto bug workaround
45827             this.bodyEl.unclip();
45828             if(this.tabs) this.tabs.bodyEl.unclip();
45829             if(this.activePanel){
45830                 this.activePanel.getEl().unclip();
45831                 if(this.activePanel.afterSlide){
45832                     this.activePanel.afterSlide();
45833                 }
45834             }
45835         }
45836     },
45837
45838     initAutoHide : function(){
45839         if(this.autoHide !== false){
45840             if(!this.autoHideHd){
45841                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45842                 this.autoHideHd = {
45843                     "mouseout": function(e){
45844                         if(!e.within(this.el, true)){
45845                             st.delay(500);
45846                         }
45847                     },
45848                     "mouseover" : function(e){
45849                         st.cancel();
45850                     },
45851                     scope : this
45852                 };
45853             }
45854             this.el.on(this.autoHideHd);
45855         }
45856     },
45857
45858     clearAutoHide : function(){
45859         if(this.autoHide !== false){
45860             this.el.un("mouseout", this.autoHideHd.mouseout);
45861             this.el.un("mouseover", this.autoHideHd.mouseover);
45862         }
45863     },
45864
45865     clearMonitor : function(){
45866         Roo.get(document).un("click", this.slideInIf, this);
45867     },
45868
45869     // these names are backwards but not changed for compat
45870     slideOut : function(){
45871         if(this.isSlid || this.el.hasActiveFx()){
45872             return;
45873         }
45874         this.isSlid = true;
45875         if(this.collapseBtn){
45876             this.collapseBtn.hide();
45877         }
45878         this.closeBtnState = this.closeBtn.getStyle('display');
45879         this.closeBtn.hide();
45880         if(this.stickBtn){
45881             this.stickBtn.show();
45882         }
45883         this.el.show();
45884         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45885         this.beforeSlide();
45886         this.el.setStyle("z-index", 10001);
45887         this.el.slideIn(this.getSlideAnchor(), {
45888             callback: function(){
45889                 this.afterSlide();
45890                 this.initAutoHide();
45891                 Roo.get(document).on("click", this.slideInIf, this);
45892                 this.fireEvent("slideshow", this);
45893             },
45894             scope: this,
45895             block: true
45896         });
45897     },
45898
45899     afterSlideIn : function(){
45900         this.clearAutoHide();
45901         this.isSlid = false;
45902         this.clearMonitor();
45903         this.el.setStyle("z-index", "");
45904         if(this.collapseBtn){
45905             this.collapseBtn.show();
45906         }
45907         this.closeBtn.setStyle('display', this.closeBtnState);
45908         if(this.stickBtn){
45909             this.stickBtn.hide();
45910         }
45911         this.fireEvent("slidehide", this);
45912     },
45913
45914     slideIn : function(cb){
45915         if(!this.isSlid || this.el.hasActiveFx()){
45916             Roo.callback(cb);
45917             return;
45918         }
45919         this.isSlid = false;
45920         this.beforeSlide();
45921         this.el.slideOut(this.getSlideAnchor(), {
45922             callback: function(){
45923                 this.el.setLeftTop(-10000, -10000);
45924                 this.afterSlide();
45925                 this.afterSlideIn();
45926                 Roo.callback(cb);
45927             },
45928             scope: this,
45929             block: true
45930         });
45931     },
45932     
45933     slideInIf : function(e){
45934         if(!e.within(this.el)){
45935             this.slideIn();
45936         }
45937     },
45938
45939     animateCollapse : function(){
45940         this.beforeSlide();
45941         this.el.setStyle("z-index", 20000);
45942         var anchor = this.getSlideAnchor();
45943         this.el.slideOut(anchor, {
45944             callback : function(){
45945                 this.el.setStyle("z-index", "");
45946                 this.collapsedEl.slideIn(anchor, {duration:.3});
45947                 this.afterSlide();
45948                 this.el.setLocation(-10000,-10000);
45949                 this.el.hide();
45950                 this.fireEvent("collapsed", this);
45951             },
45952             scope: this,
45953             block: true
45954         });
45955     },
45956
45957     animateExpand : function(){
45958         this.beforeSlide();
45959         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45960         this.el.setStyle("z-index", 20000);
45961         this.collapsedEl.hide({
45962             duration:.1
45963         });
45964         this.el.slideIn(this.getSlideAnchor(), {
45965             callback : function(){
45966                 this.el.setStyle("z-index", "");
45967                 this.afterSlide();
45968                 if(this.split){
45969                     this.split.el.show();
45970                 }
45971                 this.fireEvent("invalidated", this);
45972                 this.fireEvent("expanded", this);
45973             },
45974             scope: this,
45975             block: true
45976         });
45977     },
45978
45979     anchors : {
45980         "west" : "left",
45981         "east" : "right",
45982         "north" : "top",
45983         "south" : "bottom"
45984     },
45985
45986     sanchors : {
45987         "west" : "l",
45988         "east" : "r",
45989         "north" : "t",
45990         "south" : "b"
45991     },
45992
45993     canchors : {
45994         "west" : "tl-tr",
45995         "east" : "tr-tl",
45996         "north" : "tl-bl",
45997         "south" : "bl-tl"
45998     },
45999
46000     getAnchor : function(){
46001         return this.anchors[this.position];
46002     },
46003
46004     getCollapseAnchor : function(){
46005         return this.canchors[this.position];
46006     },
46007
46008     getSlideAnchor : function(){
46009         return this.sanchors[this.position];
46010     },
46011
46012     getAlignAdj : function(){
46013         var cm = this.cmargins;
46014         switch(this.position){
46015             case "west":
46016                 return [0, 0];
46017             break;
46018             case "east":
46019                 return [0, 0];
46020             break;
46021             case "north":
46022                 return [0, 0];
46023             break;
46024             case "south":
46025                 return [0, 0];
46026             break;
46027         }
46028     },
46029
46030     getExpandAdj : function(){
46031         var c = this.collapsedEl, cm = this.cmargins;
46032         switch(this.position){
46033             case "west":
46034                 return [-(cm.right+c.getWidth()+cm.left), 0];
46035             break;
46036             case "east":
46037                 return [cm.right+c.getWidth()+cm.left, 0];
46038             break;
46039             case "north":
46040                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46041             break;
46042             case "south":
46043                 return [0, cm.top+cm.bottom+c.getHeight()];
46044             break;
46045         }
46046     }
46047 });/*
46048  * Based on:
46049  * Ext JS Library 1.1.1
46050  * Copyright(c) 2006-2007, Ext JS, LLC.
46051  *
46052  * Originally Released Under LGPL - original licence link has changed is not relivant.
46053  *
46054  * Fork - LGPL
46055  * <script type="text/javascript">
46056  */
46057 /*
46058  * These classes are private internal classes
46059  */
46060 Roo.CenterLayoutRegion = function(mgr, config){
46061     Roo.LayoutRegion.call(this, mgr, config, "center");
46062     this.visible = true;
46063     this.minWidth = config.minWidth || 20;
46064     this.minHeight = config.minHeight || 20;
46065 };
46066
46067 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46068     hide : function(){
46069         // center panel can't be hidden
46070     },
46071     
46072     show : function(){
46073         // center panel can't be hidden
46074     },
46075     
46076     getMinWidth: function(){
46077         return this.minWidth;
46078     },
46079     
46080     getMinHeight: function(){
46081         return this.minHeight;
46082     }
46083 });
46084
46085
46086 Roo.NorthLayoutRegion = function(mgr, config){
46087     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46088     if(this.split){
46089         this.split.placement = Roo.SplitBar.TOP;
46090         this.split.orientation = Roo.SplitBar.VERTICAL;
46091         this.split.el.addClass("x-layout-split-v");
46092     }
46093     var size = config.initialSize || config.height;
46094     if(typeof size != "undefined"){
46095         this.el.setHeight(size);
46096     }
46097 };
46098 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46099     orientation: Roo.SplitBar.VERTICAL,
46100     getBox : function(){
46101         if(this.collapsed){
46102             return this.collapsedEl.getBox();
46103         }
46104         var box = this.el.getBox();
46105         if(this.split){
46106             box.height += this.split.el.getHeight();
46107         }
46108         return box;
46109     },
46110     
46111     updateBox : function(box){
46112         if(this.split && !this.collapsed){
46113             box.height -= this.split.el.getHeight();
46114             this.split.el.setLeft(box.x);
46115             this.split.el.setTop(box.y+box.height);
46116             this.split.el.setWidth(box.width);
46117         }
46118         if(this.collapsed){
46119             this.updateBody(box.width, null);
46120         }
46121         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46122     }
46123 });
46124
46125 Roo.SouthLayoutRegion = function(mgr, config){
46126     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46127     if(this.split){
46128         this.split.placement = Roo.SplitBar.BOTTOM;
46129         this.split.orientation = Roo.SplitBar.VERTICAL;
46130         this.split.el.addClass("x-layout-split-v");
46131     }
46132     var size = config.initialSize || config.height;
46133     if(typeof size != "undefined"){
46134         this.el.setHeight(size);
46135     }
46136 };
46137 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46138     orientation: Roo.SplitBar.VERTICAL,
46139     getBox : function(){
46140         if(this.collapsed){
46141             return this.collapsedEl.getBox();
46142         }
46143         var box = this.el.getBox();
46144         if(this.split){
46145             var sh = this.split.el.getHeight();
46146             box.height += sh;
46147             box.y -= sh;
46148         }
46149         return box;
46150     },
46151     
46152     updateBox : function(box){
46153         if(this.split && !this.collapsed){
46154             var sh = this.split.el.getHeight();
46155             box.height -= sh;
46156             box.y += sh;
46157             this.split.el.setLeft(box.x);
46158             this.split.el.setTop(box.y-sh);
46159             this.split.el.setWidth(box.width);
46160         }
46161         if(this.collapsed){
46162             this.updateBody(box.width, null);
46163         }
46164         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46165     }
46166 });
46167
46168 Roo.EastLayoutRegion = function(mgr, config){
46169     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46170     if(this.split){
46171         this.split.placement = Roo.SplitBar.RIGHT;
46172         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46173         this.split.el.addClass("x-layout-split-h");
46174     }
46175     var size = config.initialSize || config.width;
46176     if(typeof size != "undefined"){
46177         this.el.setWidth(size);
46178     }
46179 };
46180 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46181     orientation: Roo.SplitBar.HORIZONTAL,
46182     getBox : function(){
46183         if(this.collapsed){
46184             return this.collapsedEl.getBox();
46185         }
46186         var box = this.el.getBox();
46187         if(this.split){
46188             var sw = this.split.el.getWidth();
46189             box.width += sw;
46190             box.x -= sw;
46191         }
46192         return box;
46193     },
46194
46195     updateBox : function(box){
46196         if(this.split && !this.collapsed){
46197             var sw = this.split.el.getWidth();
46198             box.width -= sw;
46199             this.split.el.setLeft(box.x);
46200             this.split.el.setTop(box.y);
46201             this.split.el.setHeight(box.height);
46202             box.x += sw;
46203         }
46204         if(this.collapsed){
46205             this.updateBody(null, box.height);
46206         }
46207         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46208     }
46209 });
46210
46211 Roo.WestLayoutRegion = function(mgr, config){
46212     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46213     if(this.split){
46214         this.split.placement = Roo.SplitBar.LEFT;
46215         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46216         this.split.el.addClass("x-layout-split-h");
46217     }
46218     var size = config.initialSize || config.width;
46219     if(typeof size != "undefined"){
46220         this.el.setWidth(size);
46221     }
46222 };
46223 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46224     orientation: Roo.SplitBar.HORIZONTAL,
46225     getBox : function(){
46226         if(this.collapsed){
46227             return this.collapsedEl.getBox();
46228         }
46229         var box = this.el.getBox();
46230         if(this.split){
46231             box.width += this.split.el.getWidth();
46232         }
46233         return box;
46234     },
46235     
46236     updateBox : function(box){
46237         if(this.split && !this.collapsed){
46238             var sw = this.split.el.getWidth();
46239             box.width -= sw;
46240             this.split.el.setLeft(box.x+box.width);
46241             this.split.el.setTop(box.y);
46242             this.split.el.setHeight(box.height);
46243         }
46244         if(this.collapsed){
46245             this.updateBody(null, box.height);
46246         }
46247         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46248     }
46249 });
46250 /*
46251  * Based on:
46252  * Ext JS Library 1.1.1
46253  * Copyright(c) 2006-2007, Ext JS, LLC.
46254  *
46255  * Originally Released Under LGPL - original licence link has changed is not relivant.
46256  *
46257  * Fork - LGPL
46258  * <script type="text/javascript">
46259  */
46260  
46261  
46262 /*
46263  * Private internal class for reading and applying state
46264  */
46265 Roo.LayoutStateManager = function(layout){
46266      // default empty state
46267      this.state = {
46268         north: {},
46269         south: {},
46270         east: {},
46271         west: {}       
46272     };
46273 };
46274
46275 Roo.LayoutStateManager.prototype = {
46276     init : function(layout, provider){
46277         this.provider = provider;
46278         var state = provider.get(layout.id+"-layout-state");
46279         if(state){
46280             var wasUpdating = layout.isUpdating();
46281             if(!wasUpdating){
46282                 layout.beginUpdate();
46283             }
46284             for(var key in state){
46285                 if(typeof state[key] != "function"){
46286                     var rstate = state[key];
46287                     var r = layout.getRegion(key);
46288                     if(r && rstate){
46289                         if(rstate.size){
46290                             r.resizeTo(rstate.size);
46291                         }
46292                         if(rstate.collapsed == true){
46293                             r.collapse(true);
46294                         }else{
46295                             r.expand(null, true);
46296                         }
46297                     }
46298                 }
46299             }
46300             if(!wasUpdating){
46301                 layout.endUpdate();
46302             }
46303             this.state = state; 
46304         }
46305         this.layout = layout;
46306         layout.on("regionresized", this.onRegionResized, this);
46307         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46308         layout.on("regionexpanded", this.onRegionExpanded, this);
46309     },
46310     
46311     storeState : function(){
46312         this.provider.set(this.layout.id+"-layout-state", this.state);
46313     },
46314     
46315     onRegionResized : function(region, newSize){
46316         this.state[region.getPosition()].size = newSize;
46317         this.storeState();
46318     },
46319     
46320     onRegionCollapsed : function(region){
46321         this.state[region.getPosition()].collapsed = true;
46322         this.storeState();
46323     },
46324     
46325     onRegionExpanded : function(region){
46326         this.state[region.getPosition()].collapsed = false;
46327         this.storeState();
46328     }
46329 };/*
46330  * Based on:
46331  * Ext JS Library 1.1.1
46332  * Copyright(c) 2006-2007, Ext JS, LLC.
46333  *
46334  * Originally Released Under LGPL - original licence link has changed is not relivant.
46335  *
46336  * Fork - LGPL
46337  * <script type="text/javascript">
46338  */
46339 /**
46340  * @class Roo.ContentPanel
46341  * @extends Roo.util.Observable
46342  * A basic ContentPanel element.
46343  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46344  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46345  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
46346  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46347  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46348  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46349  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46350  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46351  * @cfg {String} title          The title for this panel
46352  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46353  * @cfg {String} url            Calls {@link #setUrl} with this value
46354  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46355  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46356  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46357  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46358
46359  * @constructor
46360  * Create a new ContentPanel.
46361  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46362  * @param {String/Object} config A string to set only the title or a config object
46363  * @param {String} content (optional) Set the HTML content for this panel
46364  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46365  */
46366 Roo.ContentPanel = function(el, config, content){
46367     
46368      
46369     /*
46370     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46371         config = el;
46372         el = Roo.id();
46373     }
46374     if (config && config.parentLayout) { 
46375         el = config.parentLayout.el.createChild(); 
46376     }
46377     */
46378     if(el.autoCreate){ // xtype is available if this is called from factory
46379         config = el;
46380         el = Roo.id();
46381     }
46382     this.el = Roo.get(el);
46383     if(!this.el && config && config.autoCreate){
46384         if(typeof config.autoCreate == "object"){
46385             if(!config.autoCreate.id){
46386                 config.autoCreate.id = config.id||el;
46387             }
46388             this.el = Roo.DomHelper.append(document.body,
46389                         config.autoCreate, true);
46390         }else{
46391             this.el = Roo.DomHelper.append(document.body,
46392                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46393         }
46394     }
46395     this.closable = false;
46396     this.loaded = false;
46397     this.active = false;
46398     if(typeof config == "string"){
46399         this.title = config;
46400     }else{
46401         Roo.apply(this, config);
46402     }
46403     
46404     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46405         this.wrapEl = this.el.wrap();
46406         this.toolbar.container = this.el.insertSibling(false, 'before');
46407         this.toolbar = new Roo.Toolbar(this.toolbar);
46408     }
46409     
46410     
46411     
46412     if(this.resizeEl){
46413         this.resizeEl = Roo.get(this.resizeEl, true);
46414     }else{
46415         this.resizeEl = this.el;
46416     }
46417     this.addEvents({
46418         /**
46419          * @event activate
46420          * Fires when this panel is activated. 
46421          * @param {Roo.ContentPanel} this
46422          */
46423         "activate" : true,
46424         /**
46425          * @event deactivate
46426          * Fires when this panel is activated. 
46427          * @param {Roo.ContentPanel} this
46428          */
46429         "deactivate" : true,
46430
46431         /**
46432          * @event resize
46433          * Fires when this panel is resized if fitToFrame is true.
46434          * @param {Roo.ContentPanel} this
46435          * @param {Number} width The width after any component adjustments
46436          * @param {Number} height The height after any component adjustments
46437          */
46438         "resize" : true,
46439         
46440          /**
46441          * @event render
46442          * Fires when this tab is created
46443          * @param {Roo.ContentPanel} this
46444          */
46445         "render" : true
46446         
46447         
46448         
46449     });
46450     if(this.autoScroll){
46451         this.resizeEl.setStyle("overflow", "auto");
46452     } else {
46453         // fix randome scrolling
46454         this.el.on('scroll', function() {
46455             Roo.log('fix random scolling');
46456             this.scrollTo('top',0); 
46457         });
46458     }
46459     content = content || this.content;
46460     if(content){
46461         this.setContent(content);
46462     }
46463     if(config && config.url){
46464         this.setUrl(this.url, this.params, this.loadOnce);
46465     }
46466     
46467     
46468     
46469     Roo.ContentPanel.superclass.constructor.call(this);
46470     
46471     this.fireEvent('render', this);
46472 };
46473
46474 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46475     tabTip:'',
46476     setRegion : function(region){
46477         this.region = region;
46478         if(region){
46479            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46480         }else{
46481            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46482         } 
46483     },
46484     
46485     /**
46486      * Returns the toolbar for this Panel if one was configured. 
46487      * @return {Roo.Toolbar} 
46488      */
46489     getToolbar : function(){
46490         return this.toolbar;
46491     },
46492     
46493     setActiveState : function(active){
46494         this.active = active;
46495         if(!active){
46496             this.fireEvent("deactivate", this);
46497         }else{
46498             this.fireEvent("activate", this);
46499         }
46500     },
46501     /**
46502      * Updates this panel's element
46503      * @param {String} content The new content
46504      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46505     */
46506     setContent : function(content, loadScripts){
46507         this.el.update(content, loadScripts);
46508     },
46509
46510     ignoreResize : function(w, h){
46511         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46512             return true;
46513         }else{
46514             this.lastSize = {width: w, height: h};
46515             return false;
46516         }
46517     },
46518     /**
46519      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46520      * @return {Roo.UpdateManager} The UpdateManager
46521      */
46522     getUpdateManager : function(){
46523         return this.el.getUpdateManager();
46524     },
46525      /**
46526      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46527      * @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:
46528 <pre><code>
46529 panel.load({
46530     url: "your-url.php",
46531     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46532     callback: yourFunction,
46533     scope: yourObject, //(optional scope)
46534     discardUrl: false,
46535     nocache: false,
46536     text: "Loading...",
46537     timeout: 30,
46538     scripts: false
46539 });
46540 </code></pre>
46541      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46542      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
46543      * @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}
46544      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46545      * @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.
46546      * @return {Roo.ContentPanel} this
46547      */
46548     load : function(){
46549         var um = this.el.getUpdateManager();
46550         um.update.apply(um, arguments);
46551         return this;
46552     },
46553
46554
46555     /**
46556      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
46557      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46558      * @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)
46559      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
46560      * @return {Roo.UpdateManager} The UpdateManager
46561      */
46562     setUrl : function(url, params, loadOnce){
46563         if(this.refreshDelegate){
46564             this.removeListener("activate", this.refreshDelegate);
46565         }
46566         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46567         this.on("activate", this.refreshDelegate);
46568         return this.el.getUpdateManager();
46569     },
46570     
46571     _handleRefresh : function(url, params, loadOnce){
46572         if(!loadOnce || !this.loaded){
46573             var updater = this.el.getUpdateManager();
46574             updater.update(url, params, this._setLoaded.createDelegate(this));
46575         }
46576     },
46577     
46578     _setLoaded : function(){
46579         this.loaded = true;
46580     }, 
46581     
46582     /**
46583      * Returns this panel's id
46584      * @return {String} 
46585      */
46586     getId : function(){
46587         return this.el.id;
46588     },
46589     
46590     /** 
46591      * Returns this panel's element - used by regiosn to add.
46592      * @return {Roo.Element} 
46593      */
46594     getEl : function(){
46595         return this.wrapEl || this.el;
46596     },
46597     
46598     adjustForComponents : function(width, height){
46599         if(this.resizeEl != this.el){
46600             width -= this.el.getFrameWidth('lr');
46601             height -= this.el.getFrameWidth('tb');
46602         }
46603         if(this.toolbar){
46604             var te = this.toolbar.getEl();
46605             height -= te.getHeight();
46606             te.setWidth(width);
46607         }
46608         if(this.adjustments){
46609             width += this.adjustments[0];
46610             height += this.adjustments[1];
46611         }
46612         return {"width": width, "height": height};
46613     },
46614     
46615     setSize : function(width, height){
46616         if(this.fitToFrame && !this.ignoreResize(width, height)){
46617             if(this.fitContainer && this.resizeEl != this.el){
46618                 this.el.setSize(width, height);
46619             }
46620             var size = this.adjustForComponents(width, height);
46621             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46622             this.fireEvent('resize', this, size.width, size.height);
46623         }
46624     },
46625     
46626     /**
46627      * Returns this panel's title
46628      * @return {String} 
46629      */
46630     getTitle : function(){
46631         return this.title;
46632     },
46633     
46634     /**
46635      * Set this panel's title
46636      * @param {String} title
46637      */
46638     setTitle : function(title){
46639         this.title = title;
46640         if(this.region){
46641             this.region.updatePanelTitle(this, title);
46642         }
46643     },
46644     
46645     /**
46646      * Returns true is this panel was configured to be closable
46647      * @return {Boolean} 
46648      */
46649     isClosable : function(){
46650         return this.closable;
46651     },
46652     
46653     beforeSlide : function(){
46654         this.el.clip();
46655         this.resizeEl.clip();
46656     },
46657     
46658     afterSlide : function(){
46659         this.el.unclip();
46660         this.resizeEl.unclip();
46661     },
46662     
46663     /**
46664      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46665      *   Will fail silently if the {@link #setUrl} method has not been called.
46666      *   This does not activate the panel, just updates its content.
46667      */
46668     refresh : function(){
46669         if(this.refreshDelegate){
46670            this.loaded = false;
46671            this.refreshDelegate();
46672         }
46673     },
46674     
46675     /**
46676      * Destroys this panel
46677      */
46678     destroy : function(){
46679         this.el.removeAllListeners();
46680         var tempEl = document.createElement("span");
46681         tempEl.appendChild(this.el.dom);
46682         tempEl.innerHTML = "";
46683         this.el.remove();
46684         this.el = null;
46685     },
46686     
46687     /**
46688      * form - if the content panel contains a form - this is a reference to it.
46689      * @type {Roo.form.Form}
46690      */
46691     form : false,
46692     /**
46693      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46694      *    This contains a reference to it.
46695      * @type {Roo.View}
46696      */
46697     view : false,
46698     
46699       /**
46700      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46701      * <pre><code>
46702
46703 layout.addxtype({
46704        xtype : 'Form',
46705        items: [ .... ]
46706    }
46707 );
46708
46709 </code></pre>
46710      * @param {Object} cfg Xtype definition of item to add.
46711      */
46712     
46713     addxtype : function(cfg) {
46714         // add form..
46715         if (cfg.xtype.match(/^Form$/)) {
46716             var el = this.el.createChild();
46717
46718             this.form = new  Roo.form.Form(cfg);
46719             
46720             
46721             if ( this.form.allItems.length) this.form.render(el.dom);
46722             return this.form;
46723         }
46724         // should only have one of theses..
46725         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46726             // views..
46727             cfg.el = this.el.appendChild(document.createElement("div"));
46728             // factory?
46729             
46730             var ret = new Roo.factory(cfg);
46731             ret.render && ret.render(false, ''); // render blank..
46732             this.view = ret;
46733             return ret;
46734         }
46735         return false;
46736     }
46737 });
46738
46739 /**
46740  * @class Roo.GridPanel
46741  * @extends Roo.ContentPanel
46742  * @constructor
46743  * Create a new GridPanel.
46744  * @param {Roo.grid.Grid} grid The grid for this panel
46745  * @param {String/Object} config A string to set only the panel's title, or a config object
46746  */
46747 Roo.GridPanel = function(grid, config){
46748     
46749   
46750     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46751         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46752         
46753     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46754     
46755     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46756     
46757     if(this.toolbar){
46758         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46759     }
46760     // xtype created footer. - not sure if will work as we normally have to render first..
46761     if (this.footer && !this.footer.el && this.footer.xtype) {
46762         
46763         this.footer.container = this.grid.getView().getFooterPanel(true);
46764         this.footer.dataSource = this.grid.dataSource;
46765         this.footer = Roo.factory(this.footer, Roo);
46766         
46767     }
46768     
46769     grid.monitorWindowResize = false; // turn off autosizing
46770     grid.autoHeight = false;
46771     grid.autoWidth = false;
46772     this.grid = grid;
46773     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46774 };
46775
46776 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46777     getId : function(){
46778         return this.grid.id;
46779     },
46780     
46781     /**
46782      * Returns the grid for this panel
46783      * @return {Roo.grid.Grid} 
46784      */
46785     getGrid : function(){
46786         return this.grid;    
46787     },
46788     
46789     setSize : function(width, height){
46790         if(!this.ignoreResize(width, height)){
46791             var grid = this.grid;
46792             var size = this.adjustForComponents(width, height);
46793             grid.getGridEl().setSize(size.width, size.height);
46794             grid.autoSize();
46795         }
46796     },
46797     
46798     beforeSlide : function(){
46799         this.grid.getView().scroller.clip();
46800     },
46801     
46802     afterSlide : function(){
46803         this.grid.getView().scroller.unclip();
46804     },
46805     
46806     destroy : function(){
46807         this.grid.destroy();
46808         delete this.grid;
46809         Roo.GridPanel.superclass.destroy.call(this); 
46810     }
46811 });
46812
46813
46814 /**
46815  * @class Roo.NestedLayoutPanel
46816  * @extends Roo.ContentPanel
46817  * @constructor
46818  * Create a new NestedLayoutPanel.
46819  * 
46820  * 
46821  * @param {Roo.BorderLayout} layout The layout for this panel
46822  * @param {String/Object} config A string to set only the title or a config object
46823  */
46824 Roo.NestedLayoutPanel = function(layout, config)
46825 {
46826     // construct with only one argument..
46827     /* FIXME - implement nicer consturctors
46828     if (layout.layout) {
46829         config = layout;
46830         layout = config.layout;
46831         delete config.layout;
46832     }
46833     if (layout.xtype && !layout.getEl) {
46834         // then layout needs constructing..
46835         layout = Roo.factory(layout, Roo);
46836     }
46837     */
46838     
46839     
46840     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46841     
46842     layout.monitorWindowResize = false; // turn off autosizing
46843     this.layout = layout;
46844     this.layout.getEl().addClass("x-layout-nested-layout");
46845     
46846     
46847     
46848     
46849 };
46850
46851 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46852
46853     setSize : function(width, height){
46854         if(!this.ignoreResize(width, height)){
46855             var size = this.adjustForComponents(width, height);
46856             var el = this.layout.getEl();
46857             el.setSize(size.width, size.height);
46858             var touch = el.dom.offsetWidth;
46859             this.layout.layout();
46860             // ie requires a double layout on the first pass
46861             if(Roo.isIE && !this.initialized){
46862                 this.initialized = true;
46863                 this.layout.layout();
46864             }
46865         }
46866     },
46867     
46868     // activate all subpanels if not currently active..
46869     
46870     setActiveState : function(active){
46871         this.active = active;
46872         if(!active){
46873             this.fireEvent("deactivate", this);
46874             return;
46875         }
46876         
46877         this.fireEvent("activate", this);
46878         // not sure if this should happen before or after..
46879         if (!this.layout) {
46880             return; // should not happen..
46881         }
46882         var reg = false;
46883         for (var r in this.layout.regions) {
46884             reg = this.layout.getRegion(r);
46885             if (reg.getActivePanel()) {
46886                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46887                 reg.setActivePanel(reg.getActivePanel());
46888                 continue;
46889             }
46890             if (!reg.panels.length) {
46891                 continue;
46892             }
46893             reg.showPanel(reg.getPanel(0));
46894         }
46895         
46896         
46897         
46898         
46899     },
46900     
46901     /**
46902      * Returns the nested BorderLayout for this panel
46903      * @return {Roo.BorderLayout} 
46904      */
46905     getLayout : function(){
46906         return this.layout;
46907     },
46908     
46909      /**
46910      * Adds a xtype elements to the layout of the nested panel
46911      * <pre><code>
46912
46913 panel.addxtype({
46914        xtype : 'ContentPanel',
46915        region: 'west',
46916        items: [ .... ]
46917    }
46918 );
46919
46920 panel.addxtype({
46921         xtype : 'NestedLayoutPanel',
46922         region: 'west',
46923         layout: {
46924            center: { },
46925            west: { }   
46926         },
46927         items : [ ... list of content panels or nested layout panels.. ]
46928    }
46929 );
46930 </code></pre>
46931      * @param {Object} cfg Xtype definition of item to add.
46932      */
46933     addxtype : function(cfg) {
46934         return this.layout.addxtype(cfg);
46935     
46936     }
46937 });
46938
46939 Roo.ScrollPanel = function(el, config, content){
46940     config = config || {};
46941     config.fitToFrame = true;
46942     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46943     
46944     this.el.dom.style.overflow = "hidden";
46945     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46946     this.el.removeClass("x-layout-inactive-content");
46947     this.el.on("mousewheel", this.onWheel, this);
46948
46949     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46950     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46951     up.unselectable(); down.unselectable();
46952     up.on("click", this.scrollUp, this);
46953     down.on("click", this.scrollDown, this);
46954     up.addClassOnOver("x-scroller-btn-over");
46955     down.addClassOnOver("x-scroller-btn-over");
46956     up.addClassOnClick("x-scroller-btn-click");
46957     down.addClassOnClick("x-scroller-btn-click");
46958     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46959
46960     this.resizeEl = this.el;
46961     this.el = wrap; this.up = up; this.down = down;
46962 };
46963
46964 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46965     increment : 100,
46966     wheelIncrement : 5,
46967     scrollUp : function(){
46968         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46969     },
46970
46971     scrollDown : function(){
46972         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46973     },
46974
46975     afterScroll : function(){
46976         var el = this.resizeEl;
46977         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46978         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46979         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46980     },
46981
46982     setSize : function(){
46983         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46984         this.afterScroll();
46985     },
46986
46987     onWheel : function(e){
46988         var d = e.getWheelDelta();
46989         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46990         this.afterScroll();
46991         e.stopEvent();
46992     },
46993
46994     setContent : function(content, loadScripts){
46995         this.resizeEl.update(content, loadScripts);
46996     }
46997
46998 });
46999
47000
47001
47002
47003
47004
47005
47006
47007
47008 /**
47009  * @class Roo.TreePanel
47010  * @extends Roo.ContentPanel
47011  * @constructor
47012  * Create a new TreePanel. - defaults to fit/scoll contents.
47013  * @param {String/Object} config A string to set only the panel's title, or a config object
47014  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47015  */
47016 Roo.TreePanel = function(config){
47017     var el = config.el;
47018     var tree = config.tree;
47019     delete config.tree; 
47020     delete config.el; // hopefull!
47021     
47022     // wrapper for IE7 strict & safari scroll issue
47023     
47024     var treeEl = el.createChild();
47025     config.resizeEl = treeEl;
47026     
47027     
47028     
47029     Roo.TreePanel.superclass.constructor.call(this, el, config);
47030  
47031  
47032     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47033     //console.log(tree);
47034     this.on('activate', function()
47035     {
47036         if (this.tree.rendered) {
47037             return;
47038         }
47039         //console.log('render tree');
47040         this.tree.render();
47041     });
47042     
47043     this.on('resize',  function (cp, w, h) {
47044             this.tree.innerCt.setWidth(w);
47045             this.tree.innerCt.setHeight(h);
47046             this.tree.innerCt.setStyle('overflow-y', 'auto');
47047     });
47048
47049         
47050     
47051 };
47052
47053 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47054     fitToFrame : true,
47055     autoScroll : true
47056 });
47057
47058
47059
47060
47061
47062
47063
47064
47065
47066
47067
47068 /*
47069  * Based on:
47070  * Ext JS Library 1.1.1
47071  * Copyright(c) 2006-2007, Ext JS, LLC.
47072  *
47073  * Originally Released Under LGPL - original licence link has changed is not relivant.
47074  *
47075  * Fork - LGPL
47076  * <script type="text/javascript">
47077  */
47078  
47079
47080 /**
47081  * @class Roo.ReaderLayout
47082  * @extends Roo.BorderLayout
47083  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47084  * center region containing two nested regions (a top one for a list view and one for item preview below),
47085  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47086  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47087  * expedites the setup of the overall layout and regions for this common application style.
47088  * Example:
47089  <pre><code>
47090 var reader = new Roo.ReaderLayout();
47091 var CP = Roo.ContentPanel;  // shortcut for adding
47092
47093 reader.beginUpdate();
47094 reader.add("north", new CP("north", "North"));
47095 reader.add("west", new CP("west", {title: "West"}));
47096 reader.add("east", new CP("east", {title: "East"}));
47097
47098 reader.regions.listView.add(new CP("listView", "List"));
47099 reader.regions.preview.add(new CP("preview", "Preview"));
47100 reader.endUpdate();
47101 </code></pre>
47102 * @constructor
47103 * Create a new ReaderLayout
47104 * @param {Object} config Configuration options
47105 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47106 * document.body if omitted)
47107 */
47108 Roo.ReaderLayout = function(config, renderTo){
47109     var c = config || {size:{}};
47110     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47111         north: c.north !== false ? Roo.apply({
47112             split:false,
47113             initialSize: 32,
47114             titlebar: false
47115         }, c.north) : false,
47116         west: c.west !== false ? Roo.apply({
47117             split:true,
47118             initialSize: 200,
47119             minSize: 175,
47120             maxSize: 400,
47121             titlebar: true,
47122             collapsible: true,
47123             animate: true,
47124             margins:{left:5,right:0,bottom:5,top:5},
47125             cmargins:{left:5,right:5,bottom:5,top:5}
47126         }, c.west) : false,
47127         east: c.east !== false ? Roo.apply({
47128             split:true,
47129             initialSize: 200,
47130             minSize: 175,
47131             maxSize: 400,
47132             titlebar: true,
47133             collapsible: true,
47134             animate: true,
47135             margins:{left:0,right:5,bottom:5,top:5},
47136             cmargins:{left:5,right:5,bottom:5,top:5}
47137         }, c.east) : false,
47138         center: Roo.apply({
47139             tabPosition: 'top',
47140             autoScroll:false,
47141             closeOnTab: true,
47142             titlebar:false,
47143             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47144         }, c.center)
47145     });
47146
47147     this.el.addClass('x-reader');
47148
47149     this.beginUpdate();
47150
47151     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47152         south: c.preview !== false ? Roo.apply({
47153             split:true,
47154             initialSize: 200,
47155             minSize: 100,
47156             autoScroll:true,
47157             collapsible:true,
47158             titlebar: true,
47159             cmargins:{top:5,left:0, right:0, bottom:0}
47160         }, c.preview) : false,
47161         center: Roo.apply({
47162             autoScroll:false,
47163             titlebar:false,
47164             minHeight:200
47165         }, c.listView)
47166     });
47167     this.add('center', new Roo.NestedLayoutPanel(inner,
47168             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47169
47170     this.endUpdate();
47171
47172     this.regions.preview = inner.getRegion('south');
47173     this.regions.listView = inner.getRegion('center');
47174 };
47175
47176 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47177  * Based on:
47178  * Ext JS Library 1.1.1
47179  * Copyright(c) 2006-2007, Ext JS, LLC.
47180  *
47181  * Originally Released Under LGPL - original licence link has changed is not relivant.
47182  *
47183  * Fork - LGPL
47184  * <script type="text/javascript">
47185  */
47186  
47187 /**
47188  * @class Roo.grid.Grid
47189  * @extends Roo.util.Observable
47190  * This class represents the primary interface of a component based grid control.
47191  * <br><br>Usage:<pre><code>
47192  var grid = new Roo.grid.Grid("my-container-id", {
47193      ds: myDataStore,
47194      cm: myColModel,
47195      selModel: mySelectionModel,
47196      autoSizeColumns: true,
47197      monitorWindowResize: false,
47198      trackMouseOver: true
47199  });
47200  // set any options
47201  grid.render();
47202  * </code></pre>
47203  * <b>Common Problems:</b><br/>
47204  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47205  * element will correct this<br/>
47206  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47207  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47208  * are unpredictable.<br/>
47209  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47210  * grid to calculate dimensions/offsets.<br/>
47211   * @constructor
47212  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47213  * The container MUST have some type of size defined for the grid to fill. The container will be
47214  * automatically set to position relative if it isn't already.
47215  * @param {Object} config A config object that sets properties on this grid.
47216  */
47217 Roo.grid.Grid = function(container, config){
47218         // initialize the container
47219         this.container = Roo.get(container);
47220         this.container.update("");
47221         this.container.setStyle("overflow", "hidden");
47222     this.container.addClass('x-grid-container');
47223
47224     this.id = this.container.id;
47225
47226     Roo.apply(this, config);
47227     // check and correct shorthanded configs
47228     if(this.ds){
47229         this.dataSource = this.ds;
47230         delete this.ds;
47231     }
47232     if(this.cm){
47233         this.colModel = this.cm;
47234         delete this.cm;
47235     }
47236     if(this.sm){
47237         this.selModel = this.sm;
47238         delete this.sm;
47239     }
47240
47241     if (this.selModel) {
47242         this.selModel = Roo.factory(this.selModel, Roo.grid);
47243         this.sm = this.selModel;
47244         this.sm.xmodule = this.xmodule || false;
47245     }
47246     if (typeof(this.colModel.config) == 'undefined') {
47247         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47248         this.cm = this.colModel;
47249         this.cm.xmodule = this.xmodule || false;
47250     }
47251     if (this.dataSource) {
47252         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47253         this.ds = this.dataSource;
47254         this.ds.xmodule = this.xmodule || false;
47255          
47256     }
47257     
47258     
47259     
47260     if(this.width){
47261         this.container.setWidth(this.width);
47262     }
47263
47264     if(this.height){
47265         this.container.setHeight(this.height);
47266     }
47267     /** @private */
47268         this.addEvents({
47269         // raw events
47270         /**
47271          * @event click
47272          * The raw click event for the entire grid.
47273          * @param {Roo.EventObject} e
47274          */
47275         "click" : true,
47276         /**
47277          * @event dblclick
47278          * The raw dblclick event for the entire grid.
47279          * @param {Roo.EventObject} e
47280          */
47281         "dblclick" : true,
47282         /**
47283          * @event contextmenu
47284          * The raw contextmenu event for the entire grid.
47285          * @param {Roo.EventObject} e
47286          */
47287         "contextmenu" : true,
47288         /**
47289          * @event mousedown
47290          * The raw mousedown event for the entire grid.
47291          * @param {Roo.EventObject} e
47292          */
47293         "mousedown" : true,
47294         /**
47295          * @event mouseup
47296          * The raw mouseup event for the entire grid.
47297          * @param {Roo.EventObject} e
47298          */
47299         "mouseup" : true,
47300         /**
47301          * @event mouseover
47302          * The raw mouseover event for the entire grid.
47303          * @param {Roo.EventObject} e
47304          */
47305         "mouseover" : true,
47306         /**
47307          * @event mouseout
47308          * The raw mouseout event for the entire grid.
47309          * @param {Roo.EventObject} e
47310          */
47311         "mouseout" : true,
47312         /**
47313          * @event keypress
47314          * The raw keypress event for the entire grid.
47315          * @param {Roo.EventObject} e
47316          */
47317         "keypress" : true,
47318         /**
47319          * @event keydown
47320          * The raw keydown event for the entire grid.
47321          * @param {Roo.EventObject} e
47322          */
47323         "keydown" : true,
47324
47325         // custom events
47326
47327         /**
47328          * @event cellclick
47329          * Fires when a cell is clicked
47330          * @param {Grid} this
47331          * @param {Number} rowIndex
47332          * @param {Number} columnIndex
47333          * @param {Roo.EventObject} e
47334          */
47335         "cellclick" : true,
47336         /**
47337          * @event celldblclick
47338          * Fires when a cell is double clicked
47339          * @param {Grid} this
47340          * @param {Number} rowIndex
47341          * @param {Number} columnIndex
47342          * @param {Roo.EventObject} e
47343          */
47344         "celldblclick" : true,
47345         /**
47346          * @event rowclick
47347          * Fires when a row is clicked
47348          * @param {Grid} this
47349          * @param {Number} rowIndex
47350          * @param {Roo.EventObject} e
47351          */
47352         "rowclick" : true,
47353         /**
47354          * @event rowdblclick
47355          * Fires when a row is double clicked
47356          * @param {Grid} this
47357          * @param {Number} rowIndex
47358          * @param {Roo.EventObject} e
47359          */
47360         "rowdblclick" : true,
47361         /**
47362          * @event headerclick
47363          * Fires when a header is clicked
47364          * @param {Grid} this
47365          * @param {Number} columnIndex
47366          * @param {Roo.EventObject} e
47367          */
47368         "headerclick" : true,
47369         /**
47370          * @event headerdblclick
47371          * Fires when a header cell is double clicked
47372          * @param {Grid} this
47373          * @param {Number} columnIndex
47374          * @param {Roo.EventObject} e
47375          */
47376         "headerdblclick" : true,
47377         /**
47378          * @event rowcontextmenu
47379          * Fires when a row is right clicked
47380          * @param {Grid} this
47381          * @param {Number} rowIndex
47382          * @param {Roo.EventObject} e
47383          */
47384         "rowcontextmenu" : true,
47385         /**
47386          * @event cellcontextmenu
47387          * Fires when a cell is right clicked
47388          * @param {Grid} this
47389          * @param {Number} rowIndex
47390          * @param {Number} cellIndex
47391          * @param {Roo.EventObject} e
47392          */
47393          "cellcontextmenu" : true,
47394         /**
47395          * @event headercontextmenu
47396          * Fires when a header is right clicked
47397          * @param {Grid} this
47398          * @param {Number} columnIndex
47399          * @param {Roo.EventObject} e
47400          */
47401         "headercontextmenu" : true,
47402         /**
47403          * @event bodyscroll
47404          * Fires when the body element is scrolled
47405          * @param {Number} scrollLeft
47406          * @param {Number} scrollTop
47407          */
47408         "bodyscroll" : true,
47409         /**
47410          * @event columnresize
47411          * Fires when the user resizes a column
47412          * @param {Number} columnIndex
47413          * @param {Number} newSize
47414          */
47415         "columnresize" : true,
47416         /**
47417          * @event columnmove
47418          * Fires when the user moves a column
47419          * @param {Number} oldIndex
47420          * @param {Number} newIndex
47421          */
47422         "columnmove" : true,
47423         /**
47424          * @event startdrag
47425          * Fires when row(s) start being dragged
47426          * @param {Grid} this
47427          * @param {Roo.GridDD} dd The drag drop object
47428          * @param {event} e The raw browser event
47429          */
47430         "startdrag" : true,
47431         /**
47432          * @event enddrag
47433          * Fires when a drag operation is complete
47434          * @param {Grid} this
47435          * @param {Roo.GridDD} dd The drag drop object
47436          * @param {event} e The raw browser event
47437          */
47438         "enddrag" : true,
47439         /**
47440          * @event dragdrop
47441          * Fires when dragged row(s) are dropped on a valid DD target
47442          * @param {Grid} this
47443          * @param {Roo.GridDD} dd The drag drop object
47444          * @param {String} targetId The target drag drop object
47445          * @param {event} e The raw browser event
47446          */
47447         "dragdrop" : true,
47448         /**
47449          * @event dragover
47450          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47451          * @param {Grid} this
47452          * @param {Roo.GridDD} dd The drag drop object
47453          * @param {String} targetId The target drag drop object
47454          * @param {event} e The raw browser event
47455          */
47456         "dragover" : true,
47457         /**
47458          * @event dragenter
47459          *  Fires when the dragged row(s) first cross another DD target while being dragged
47460          * @param {Grid} this
47461          * @param {Roo.GridDD} dd The drag drop object
47462          * @param {String} targetId The target drag drop object
47463          * @param {event} e The raw browser event
47464          */
47465         "dragenter" : true,
47466         /**
47467          * @event dragout
47468          * Fires when the dragged row(s) leave another DD target while being dragged
47469          * @param {Grid} this
47470          * @param {Roo.GridDD} dd The drag drop object
47471          * @param {String} targetId The target drag drop object
47472          * @param {event} e The raw browser event
47473          */
47474         "dragout" : true,
47475         /**
47476          * @event rowclass
47477          * Fires when a row is rendered, so you can change add a style to it.
47478          * @param {GridView} gridview   The grid view
47479          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47480          */
47481         'rowclass' : true,
47482
47483         /**
47484          * @event render
47485          * Fires when the grid is rendered
47486          * @param {Grid} grid
47487          */
47488         'render' : true
47489     });
47490
47491     Roo.grid.Grid.superclass.constructor.call(this);
47492 };
47493 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47494     
47495     /**
47496      * @cfg {String} ddGroup - drag drop group.
47497      */
47498
47499     /**
47500      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47501      */
47502     minColumnWidth : 25,
47503
47504     /**
47505      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47506      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47507      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47508      */
47509     autoSizeColumns : false,
47510
47511     /**
47512      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47513      */
47514     autoSizeHeaders : true,
47515
47516     /**
47517      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47518      */
47519     monitorWindowResize : true,
47520
47521     /**
47522      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47523      * rows measured to get a columns size. Default is 0 (all rows).
47524      */
47525     maxRowsToMeasure : 0,
47526
47527     /**
47528      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47529      */
47530     trackMouseOver : true,
47531
47532     /**
47533     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47534     */
47535     
47536     /**
47537     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47538     */
47539     enableDragDrop : false,
47540     
47541     /**
47542     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47543     */
47544     enableColumnMove : true,
47545     
47546     /**
47547     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47548     */
47549     enableColumnHide : true,
47550     
47551     /**
47552     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47553     */
47554     enableRowHeightSync : false,
47555     
47556     /**
47557     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47558     */
47559     stripeRows : true,
47560     
47561     /**
47562     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47563     */
47564     autoHeight : false,
47565
47566     /**
47567      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
47568      */
47569     autoExpandColumn : false,
47570
47571     /**
47572     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47573     * Default is 50.
47574     */
47575     autoExpandMin : 50,
47576
47577     /**
47578     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47579     */
47580     autoExpandMax : 1000,
47581
47582     /**
47583     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47584     */
47585     view : null,
47586
47587     /**
47588     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47589     */
47590     loadMask : false,
47591     /**
47592     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47593     */
47594     dropTarget: false,
47595     
47596    
47597     
47598     // private
47599     rendered : false,
47600
47601     /**
47602     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47603     * of a fixed width. Default is false.
47604     */
47605     /**
47606     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47607     */
47608     /**
47609      * Called once after all setup has been completed and the grid is ready to be rendered.
47610      * @return {Roo.grid.Grid} this
47611      */
47612     render : function()
47613     {
47614         var c = this.container;
47615         // try to detect autoHeight/width mode
47616         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47617             this.autoHeight = true;
47618         }
47619         var view = this.getView();
47620         view.init(this);
47621
47622         c.on("click", this.onClick, this);
47623         c.on("dblclick", this.onDblClick, this);
47624         c.on("contextmenu", this.onContextMenu, this);
47625         c.on("keydown", this.onKeyDown, this);
47626
47627         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47628
47629         this.getSelectionModel().init(this);
47630
47631         view.render();
47632
47633         if(this.loadMask){
47634             this.loadMask = new Roo.LoadMask(this.container,
47635                     Roo.apply({store:this.dataSource}, this.loadMask));
47636         }
47637         
47638         
47639         if (this.toolbar && this.toolbar.xtype) {
47640             this.toolbar.container = this.getView().getHeaderPanel(true);
47641             this.toolbar = new Roo.Toolbar(this.toolbar);
47642         }
47643         if (this.footer && this.footer.xtype) {
47644             this.footer.dataSource = this.getDataSource();
47645             this.footer.container = this.getView().getFooterPanel(true);
47646             this.footer = Roo.factory(this.footer, Roo);
47647         }
47648         if (this.dropTarget && this.dropTarget.xtype) {
47649             delete this.dropTarget.xtype;
47650             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47651         }
47652         
47653         
47654         this.rendered = true;
47655         this.fireEvent('render', this);
47656         return this;
47657     },
47658
47659         /**
47660          * Reconfigures the grid to use a different Store and Column Model.
47661          * The View will be bound to the new objects and refreshed.
47662          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47663          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47664          */
47665     reconfigure : function(dataSource, colModel){
47666         if(this.loadMask){
47667             this.loadMask.destroy();
47668             this.loadMask = new Roo.LoadMask(this.container,
47669                     Roo.apply({store:dataSource}, this.loadMask));
47670         }
47671         this.view.bind(dataSource, colModel);
47672         this.dataSource = dataSource;
47673         this.colModel = colModel;
47674         this.view.refresh(true);
47675     },
47676
47677     // private
47678     onKeyDown : function(e){
47679         this.fireEvent("keydown", e);
47680     },
47681
47682     /**
47683      * Destroy this grid.
47684      * @param {Boolean} removeEl True to remove the element
47685      */
47686     destroy : function(removeEl, keepListeners){
47687         if(this.loadMask){
47688             this.loadMask.destroy();
47689         }
47690         var c = this.container;
47691         c.removeAllListeners();
47692         this.view.destroy();
47693         this.colModel.purgeListeners();
47694         if(!keepListeners){
47695             this.purgeListeners();
47696         }
47697         c.update("");
47698         if(removeEl === true){
47699             c.remove();
47700         }
47701     },
47702
47703     // private
47704     processEvent : function(name, e){
47705         this.fireEvent(name, e);
47706         var t = e.getTarget();
47707         var v = this.view;
47708         var header = v.findHeaderIndex(t);
47709         if(header !== false){
47710             this.fireEvent("header" + name, this, header, e);
47711         }else{
47712             var row = v.findRowIndex(t);
47713             var cell = v.findCellIndex(t);
47714             if(row !== false){
47715                 this.fireEvent("row" + name, this, row, e);
47716                 if(cell !== false){
47717                     this.fireEvent("cell" + name, this, row, cell, e);
47718                 }
47719             }
47720         }
47721     },
47722
47723     // private
47724     onClick : function(e){
47725         this.processEvent("click", e);
47726     },
47727
47728     // private
47729     onContextMenu : function(e, t){
47730         this.processEvent("contextmenu", e);
47731     },
47732
47733     // private
47734     onDblClick : function(e){
47735         this.processEvent("dblclick", e);
47736     },
47737
47738     // private
47739     walkCells : function(row, col, step, fn, scope){
47740         var cm = this.colModel, clen = cm.getColumnCount();
47741         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47742         if(step < 0){
47743             if(col < 0){
47744                 row--;
47745                 first = false;
47746             }
47747             while(row >= 0){
47748                 if(!first){
47749                     col = clen-1;
47750                 }
47751                 first = false;
47752                 while(col >= 0){
47753                     if(fn.call(scope || this, row, col, cm) === true){
47754                         return [row, col];
47755                     }
47756                     col--;
47757                 }
47758                 row--;
47759             }
47760         } else {
47761             if(col >= clen){
47762                 row++;
47763                 first = false;
47764             }
47765             while(row < rlen){
47766                 if(!first){
47767                     col = 0;
47768                 }
47769                 first = false;
47770                 while(col < clen){
47771                     if(fn.call(scope || this, row, col, cm) === true){
47772                         return [row, col];
47773                     }
47774                     col++;
47775                 }
47776                 row++;
47777             }
47778         }
47779         return null;
47780     },
47781
47782     // private
47783     getSelections : function(){
47784         return this.selModel.getSelections();
47785     },
47786
47787     /**
47788      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47789      * but if manual update is required this method will initiate it.
47790      */
47791     autoSize : function(){
47792         if(this.rendered){
47793             this.view.layout();
47794             if(this.view.adjustForScroll){
47795                 this.view.adjustForScroll();
47796             }
47797         }
47798     },
47799
47800     /**
47801      * Returns the grid's underlying element.
47802      * @return {Element} The element
47803      */
47804     getGridEl : function(){
47805         return this.container;
47806     },
47807
47808     // private for compatibility, overridden by editor grid
47809     stopEditing : function(){},
47810
47811     /**
47812      * Returns the grid's SelectionModel.
47813      * @return {SelectionModel}
47814      */
47815     getSelectionModel : function(){
47816         if(!this.selModel){
47817             this.selModel = new Roo.grid.RowSelectionModel();
47818         }
47819         return this.selModel;
47820     },
47821
47822     /**
47823      * Returns the grid's DataSource.
47824      * @return {DataSource}
47825      */
47826     getDataSource : function(){
47827         return this.dataSource;
47828     },
47829
47830     /**
47831      * Returns the grid's ColumnModel.
47832      * @return {ColumnModel}
47833      */
47834     getColumnModel : function(){
47835         return this.colModel;
47836     },
47837
47838     /**
47839      * Returns the grid's GridView object.
47840      * @return {GridView}
47841      */
47842     getView : function(){
47843         if(!this.view){
47844             this.view = new Roo.grid.GridView(this.viewConfig);
47845         }
47846         return this.view;
47847     },
47848     /**
47849      * Called to get grid's drag proxy text, by default returns this.ddText.
47850      * @return {String}
47851      */
47852     getDragDropText : function(){
47853         var count = this.selModel.getCount();
47854         return String.format(this.ddText, count, count == 1 ? '' : 's');
47855     }
47856 });
47857 /**
47858  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47859  * %0 is replaced with the number of selected rows.
47860  * @type String
47861  */
47862 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47863  * Based on:
47864  * Ext JS Library 1.1.1
47865  * Copyright(c) 2006-2007, Ext JS, LLC.
47866  *
47867  * Originally Released Under LGPL - original licence link has changed is not relivant.
47868  *
47869  * Fork - LGPL
47870  * <script type="text/javascript">
47871  */
47872  
47873 Roo.grid.AbstractGridView = function(){
47874         this.grid = null;
47875         
47876         this.events = {
47877             "beforerowremoved" : true,
47878             "beforerowsinserted" : true,
47879             "beforerefresh" : true,
47880             "rowremoved" : true,
47881             "rowsinserted" : true,
47882             "rowupdated" : true,
47883             "refresh" : true
47884         };
47885     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47886 };
47887
47888 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47889     rowClass : "x-grid-row",
47890     cellClass : "x-grid-cell",
47891     tdClass : "x-grid-td",
47892     hdClass : "x-grid-hd",
47893     splitClass : "x-grid-hd-split",
47894     
47895         init: function(grid){
47896         this.grid = grid;
47897                 var cid = this.grid.getGridEl().id;
47898         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47899         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47900         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47901         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47902         },
47903         
47904         getColumnRenderers : function(){
47905         var renderers = [];
47906         var cm = this.grid.colModel;
47907         var colCount = cm.getColumnCount();
47908         for(var i = 0; i < colCount; i++){
47909             renderers[i] = cm.getRenderer(i);
47910         }
47911         return renderers;
47912     },
47913     
47914     getColumnIds : function(){
47915         var ids = [];
47916         var cm = this.grid.colModel;
47917         var colCount = cm.getColumnCount();
47918         for(var i = 0; i < colCount; i++){
47919             ids[i] = cm.getColumnId(i);
47920         }
47921         return ids;
47922     },
47923     
47924     getDataIndexes : function(){
47925         if(!this.indexMap){
47926             this.indexMap = this.buildIndexMap();
47927         }
47928         return this.indexMap.colToData;
47929     },
47930     
47931     getColumnIndexByDataIndex : function(dataIndex){
47932         if(!this.indexMap){
47933             this.indexMap = this.buildIndexMap();
47934         }
47935         return this.indexMap.dataToCol[dataIndex];
47936     },
47937     
47938     /**
47939      * Set a css style for a column dynamically. 
47940      * @param {Number} colIndex The index of the column
47941      * @param {String} name The css property name
47942      * @param {String} value The css value
47943      */
47944     setCSSStyle : function(colIndex, name, value){
47945         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47946         Roo.util.CSS.updateRule(selector, name, value);
47947     },
47948     
47949     generateRules : function(cm){
47950         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47951         Roo.util.CSS.removeStyleSheet(rulesId);
47952         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47953             var cid = cm.getColumnId(i);
47954             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47955                          this.tdSelector, cid, " {\n}\n",
47956                          this.hdSelector, cid, " {\n}\n",
47957                          this.splitSelector, cid, " {\n}\n");
47958         }
47959         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47960     }
47961 });/*
47962  * Based on:
47963  * Ext JS Library 1.1.1
47964  * Copyright(c) 2006-2007, Ext JS, LLC.
47965  *
47966  * Originally Released Under LGPL - original licence link has changed is not relivant.
47967  *
47968  * Fork - LGPL
47969  * <script type="text/javascript">
47970  */
47971
47972 // private
47973 // This is a support class used internally by the Grid components
47974 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47975     this.grid = grid;
47976     this.view = grid.getView();
47977     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47978     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47979     if(hd2){
47980         this.setHandleElId(Roo.id(hd));
47981         this.setOuterHandleElId(Roo.id(hd2));
47982     }
47983     this.scroll = false;
47984 };
47985 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47986     maxDragWidth: 120,
47987     getDragData : function(e){
47988         var t = Roo.lib.Event.getTarget(e);
47989         var h = this.view.findHeaderCell(t);
47990         if(h){
47991             return {ddel: h.firstChild, header:h};
47992         }
47993         return false;
47994     },
47995
47996     onInitDrag : function(e){
47997         this.view.headersDisabled = true;
47998         var clone = this.dragData.ddel.cloneNode(true);
47999         clone.id = Roo.id();
48000         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48001         this.proxy.update(clone);
48002         return true;
48003     },
48004
48005     afterValidDrop : function(){
48006         var v = this.view;
48007         setTimeout(function(){
48008             v.headersDisabled = false;
48009         }, 50);
48010     },
48011
48012     afterInvalidDrop : function(){
48013         var v = this.view;
48014         setTimeout(function(){
48015             v.headersDisabled = false;
48016         }, 50);
48017     }
48018 });
48019 /*
48020  * Based on:
48021  * Ext JS Library 1.1.1
48022  * Copyright(c) 2006-2007, Ext JS, LLC.
48023  *
48024  * Originally Released Under LGPL - original licence link has changed is not relivant.
48025  *
48026  * Fork - LGPL
48027  * <script type="text/javascript">
48028  */
48029 // private
48030 // This is a support class used internally by the Grid components
48031 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48032     this.grid = grid;
48033     this.view = grid.getView();
48034     // split the proxies so they don't interfere with mouse events
48035     this.proxyTop = Roo.DomHelper.append(document.body, {
48036         cls:"col-move-top", html:"&#160;"
48037     }, true);
48038     this.proxyBottom = Roo.DomHelper.append(document.body, {
48039         cls:"col-move-bottom", html:"&#160;"
48040     }, true);
48041     this.proxyTop.hide = this.proxyBottom.hide = function(){
48042         this.setLeftTop(-100,-100);
48043         this.setStyle("visibility", "hidden");
48044     };
48045     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48046     // temporarily disabled
48047     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48048     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48049 };
48050 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48051     proxyOffsets : [-4, -9],
48052     fly: Roo.Element.fly,
48053
48054     getTargetFromEvent : function(e){
48055         var t = Roo.lib.Event.getTarget(e);
48056         var cindex = this.view.findCellIndex(t);
48057         if(cindex !== false){
48058             return this.view.getHeaderCell(cindex);
48059         }
48060         return null;
48061     },
48062
48063     nextVisible : function(h){
48064         var v = this.view, cm = this.grid.colModel;
48065         h = h.nextSibling;
48066         while(h){
48067             if(!cm.isHidden(v.getCellIndex(h))){
48068                 return h;
48069             }
48070             h = h.nextSibling;
48071         }
48072         return null;
48073     },
48074
48075     prevVisible : function(h){
48076         var v = this.view, cm = this.grid.colModel;
48077         h = h.prevSibling;
48078         while(h){
48079             if(!cm.isHidden(v.getCellIndex(h))){
48080                 return h;
48081             }
48082             h = h.prevSibling;
48083         }
48084         return null;
48085     },
48086
48087     positionIndicator : function(h, n, e){
48088         var x = Roo.lib.Event.getPageX(e);
48089         var r = Roo.lib.Dom.getRegion(n.firstChild);
48090         var px, pt, py = r.top + this.proxyOffsets[1];
48091         if((r.right - x) <= (r.right-r.left)/2){
48092             px = r.right+this.view.borderWidth;
48093             pt = "after";
48094         }else{
48095             px = r.left;
48096             pt = "before";
48097         }
48098         var oldIndex = this.view.getCellIndex(h);
48099         var newIndex = this.view.getCellIndex(n);
48100
48101         if(this.grid.colModel.isFixed(newIndex)){
48102             return false;
48103         }
48104
48105         var locked = this.grid.colModel.isLocked(newIndex);
48106
48107         if(pt == "after"){
48108             newIndex++;
48109         }
48110         if(oldIndex < newIndex){
48111             newIndex--;
48112         }
48113         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48114             return false;
48115         }
48116         px +=  this.proxyOffsets[0];
48117         this.proxyTop.setLeftTop(px, py);
48118         this.proxyTop.show();
48119         if(!this.bottomOffset){
48120             this.bottomOffset = this.view.mainHd.getHeight();
48121         }
48122         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48123         this.proxyBottom.show();
48124         return pt;
48125     },
48126
48127     onNodeEnter : function(n, dd, e, data){
48128         if(data.header != n){
48129             this.positionIndicator(data.header, n, e);
48130         }
48131     },
48132
48133     onNodeOver : function(n, dd, e, data){
48134         var result = false;
48135         if(data.header != n){
48136             result = this.positionIndicator(data.header, n, e);
48137         }
48138         if(!result){
48139             this.proxyTop.hide();
48140             this.proxyBottom.hide();
48141         }
48142         return result ? this.dropAllowed : this.dropNotAllowed;
48143     },
48144
48145     onNodeOut : function(n, dd, e, data){
48146         this.proxyTop.hide();
48147         this.proxyBottom.hide();
48148     },
48149
48150     onNodeDrop : function(n, dd, e, data){
48151         var h = data.header;
48152         if(h != n){
48153             var cm = this.grid.colModel;
48154             var x = Roo.lib.Event.getPageX(e);
48155             var r = Roo.lib.Dom.getRegion(n.firstChild);
48156             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48157             var oldIndex = this.view.getCellIndex(h);
48158             var newIndex = this.view.getCellIndex(n);
48159             var locked = cm.isLocked(newIndex);
48160             if(pt == "after"){
48161                 newIndex++;
48162             }
48163             if(oldIndex < newIndex){
48164                 newIndex--;
48165             }
48166             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48167                 return false;
48168             }
48169             cm.setLocked(oldIndex, locked, true);
48170             cm.moveColumn(oldIndex, newIndex);
48171             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48172             return true;
48173         }
48174         return false;
48175     }
48176 });
48177 /*
48178  * Based on:
48179  * Ext JS Library 1.1.1
48180  * Copyright(c) 2006-2007, Ext JS, LLC.
48181  *
48182  * Originally Released Under LGPL - original licence link has changed is not relivant.
48183  *
48184  * Fork - LGPL
48185  * <script type="text/javascript">
48186  */
48187   
48188 /**
48189  * @class Roo.grid.GridView
48190  * @extends Roo.util.Observable
48191  *
48192  * @constructor
48193  * @param {Object} config
48194  */
48195 Roo.grid.GridView = function(config){
48196     Roo.grid.GridView.superclass.constructor.call(this);
48197     this.el = null;
48198
48199     Roo.apply(this, config);
48200 };
48201
48202 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48203
48204     /**
48205      * Override this function to apply custom css classes to rows during rendering
48206      * @param {Record} record The record
48207      * @param {Number} index
48208      * @method getRowClass
48209      */
48210     rowClass : "x-grid-row",
48211
48212     cellClass : "x-grid-col",
48213
48214     tdClass : "x-grid-td",
48215
48216     hdClass : "x-grid-hd",
48217
48218     splitClass : "x-grid-split",
48219
48220     sortClasses : ["sort-asc", "sort-desc"],
48221
48222     enableMoveAnim : false,
48223
48224     hlColor: "C3DAF9",
48225
48226     dh : Roo.DomHelper,
48227
48228     fly : Roo.Element.fly,
48229
48230     css : Roo.util.CSS,
48231
48232     borderWidth: 1,
48233
48234     splitOffset: 3,
48235
48236     scrollIncrement : 22,
48237
48238     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48239
48240     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48241
48242     bind : function(ds, cm){
48243         if(this.ds){
48244             this.ds.un("load", this.onLoad, this);
48245             this.ds.un("datachanged", this.onDataChange, this);
48246             this.ds.un("add", this.onAdd, this);
48247             this.ds.un("remove", this.onRemove, this);
48248             this.ds.un("update", this.onUpdate, this);
48249             this.ds.un("clear", this.onClear, this);
48250         }
48251         if(ds){
48252             ds.on("load", this.onLoad, this);
48253             ds.on("datachanged", this.onDataChange, this);
48254             ds.on("add", this.onAdd, this);
48255             ds.on("remove", this.onRemove, this);
48256             ds.on("update", this.onUpdate, this);
48257             ds.on("clear", this.onClear, this);
48258         }
48259         this.ds = ds;
48260
48261         if(this.cm){
48262             this.cm.un("widthchange", this.onColWidthChange, this);
48263             this.cm.un("headerchange", this.onHeaderChange, this);
48264             this.cm.un("hiddenchange", this.onHiddenChange, this);
48265             this.cm.un("columnmoved", this.onColumnMove, this);
48266             this.cm.un("columnlockchange", this.onColumnLock, this);
48267         }
48268         if(cm){
48269             this.generateRules(cm);
48270             cm.on("widthchange", this.onColWidthChange, this);
48271             cm.on("headerchange", this.onHeaderChange, this);
48272             cm.on("hiddenchange", this.onHiddenChange, this);
48273             cm.on("columnmoved", this.onColumnMove, this);
48274             cm.on("columnlockchange", this.onColumnLock, this);
48275         }
48276         this.cm = cm;
48277     },
48278
48279     init: function(grid){
48280         Roo.grid.GridView.superclass.init.call(this, grid);
48281
48282         this.bind(grid.dataSource, grid.colModel);
48283
48284         grid.on("headerclick", this.handleHeaderClick, this);
48285
48286         if(grid.trackMouseOver){
48287             grid.on("mouseover", this.onRowOver, this);
48288             grid.on("mouseout", this.onRowOut, this);
48289         }
48290         grid.cancelTextSelection = function(){};
48291         this.gridId = grid.id;
48292
48293         var tpls = this.templates || {};
48294
48295         if(!tpls.master){
48296             tpls.master = new Roo.Template(
48297                '<div class="x-grid" hidefocus="true">',
48298                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48299                   '<div class="x-grid-topbar"></div>',
48300                   '<div class="x-grid-scroller"><div></div></div>',
48301                   '<div class="x-grid-locked">',
48302                       '<div class="x-grid-header">{lockedHeader}</div>',
48303                       '<div class="x-grid-body">{lockedBody}</div>',
48304                   "</div>",
48305                   '<div class="x-grid-viewport">',
48306                       '<div class="x-grid-header">{header}</div>',
48307                       '<div class="x-grid-body">{body}</div>',
48308                   "</div>",
48309                   '<div class="x-grid-bottombar"></div>',
48310                  
48311                   '<div class="x-grid-resize-proxy">&#160;</div>',
48312                "</div>"
48313             );
48314             tpls.master.disableformats = true;
48315         }
48316
48317         if(!tpls.header){
48318             tpls.header = new Roo.Template(
48319                '<table border="0" cellspacing="0" cellpadding="0">',
48320                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48321                "</table>{splits}"
48322             );
48323             tpls.header.disableformats = true;
48324         }
48325         tpls.header.compile();
48326
48327         if(!tpls.hcell){
48328             tpls.hcell = new Roo.Template(
48329                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48330                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48331                 "</div></td>"
48332              );
48333              tpls.hcell.disableFormats = true;
48334         }
48335         tpls.hcell.compile();
48336
48337         if(!tpls.hsplit){
48338             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48339             tpls.hsplit.disableFormats = true;
48340         }
48341         tpls.hsplit.compile();
48342
48343         if(!tpls.body){
48344             tpls.body = new Roo.Template(
48345                '<table border="0" cellspacing="0" cellpadding="0">',
48346                "<tbody>{rows}</tbody>",
48347                "</table>"
48348             );
48349             tpls.body.disableFormats = true;
48350         }
48351         tpls.body.compile();
48352
48353         if(!tpls.row){
48354             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48355             tpls.row.disableFormats = true;
48356         }
48357         tpls.row.compile();
48358
48359         if(!tpls.cell){
48360             tpls.cell = new Roo.Template(
48361                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48362                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48363                 "</td>"
48364             );
48365             tpls.cell.disableFormats = true;
48366         }
48367         tpls.cell.compile();
48368
48369         this.templates = tpls;
48370     },
48371
48372     // remap these for backwards compat
48373     onColWidthChange : function(){
48374         this.updateColumns.apply(this, arguments);
48375     },
48376     onHeaderChange : function(){
48377         this.updateHeaders.apply(this, arguments);
48378     }, 
48379     onHiddenChange : function(){
48380         this.handleHiddenChange.apply(this, arguments);
48381     },
48382     onColumnMove : function(){
48383         this.handleColumnMove.apply(this, arguments);
48384     },
48385     onColumnLock : function(){
48386         this.handleLockChange.apply(this, arguments);
48387     },
48388
48389     onDataChange : function(){
48390         this.refresh();
48391         this.updateHeaderSortState();
48392     },
48393
48394     onClear : function(){
48395         this.refresh();
48396     },
48397
48398     onUpdate : function(ds, record){
48399         this.refreshRow(record);
48400     },
48401
48402     refreshRow : function(record){
48403         var ds = this.ds, index;
48404         if(typeof record == 'number'){
48405             index = record;
48406             record = ds.getAt(index);
48407         }else{
48408             index = ds.indexOf(record);
48409         }
48410         this.insertRows(ds, index, index, true);
48411         this.onRemove(ds, record, index+1, true);
48412         this.syncRowHeights(index, index);
48413         this.layout();
48414         this.fireEvent("rowupdated", this, index, record);
48415     },
48416
48417     onAdd : function(ds, records, index){
48418         this.insertRows(ds, index, index + (records.length-1));
48419     },
48420
48421     onRemove : function(ds, record, index, isUpdate){
48422         if(isUpdate !== true){
48423             this.fireEvent("beforerowremoved", this, index, record);
48424         }
48425         var bt = this.getBodyTable(), lt = this.getLockedTable();
48426         if(bt.rows[index]){
48427             bt.firstChild.removeChild(bt.rows[index]);
48428         }
48429         if(lt.rows[index]){
48430             lt.firstChild.removeChild(lt.rows[index]);
48431         }
48432         if(isUpdate !== true){
48433             this.stripeRows(index);
48434             this.syncRowHeights(index, index);
48435             this.layout();
48436             this.fireEvent("rowremoved", this, index, record);
48437         }
48438     },
48439
48440     onLoad : function(){
48441         this.scrollToTop();
48442     },
48443
48444     /**
48445      * Scrolls the grid to the top
48446      */
48447     scrollToTop : function(){
48448         if(this.scroller){
48449             this.scroller.dom.scrollTop = 0;
48450             this.syncScroll();
48451         }
48452     },
48453
48454     /**
48455      * Gets a panel in the header of the grid that can be used for toolbars etc.
48456      * After modifying the contents of this panel a call to grid.autoSize() may be
48457      * required to register any changes in size.
48458      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48459      * @return Roo.Element
48460      */
48461     getHeaderPanel : function(doShow){
48462         if(doShow){
48463             this.headerPanel.show();
48464         }
48465         return this.headerPanel;
48466     },
48467
48468     /**
48469      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48470      * After modifying the contents of this panel a call to grid.autoSize() may be
48471      * required to register any changes in size.
48472      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48473      * @return Roo.Element
48474      */
48475     getFooterPanel : function(doShow){
48476         if(doShow){
48477             this.footerPanel.show();
48478         }
48479         return this.footerPanel;
48480     },
48481
48482     initElements : function(){
48483         var E = Roo.Element;
48484         var el = this.grid.getGridEl().dom.firstChild;
48485         var cs = el.childNodes;
48486
48487         this.el = new E(el);
48488         
48489          this.focusEl = new E(el.firstChild);
48490         this.focusEl.swallowEvent("click", true);
48491         
48492         this.headerPanel = new E(cs[1]);
48493         this.headerPanel.enableDisplayMode("block");
48494
48495         this.scroller = new E(cs[2]);
48496         this.scrollSizer = new E(this.scroller.dom.firstChild);
48497
48498         this.lockedWrap = new E(cs[3]);
48499         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48500         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48501
48502         this.mainWrap = new E(cs[4]);
48503         this.mainHd = new E(this.mainWrap.dom.firstChild);
48504         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48505
48506         this.footerPanel = new E(cs[5]);
48507         this.footerPanel.enableDisplayMode("block");
48508
48509         this.resizeProxy = new E(cs[6]);
48510
48511         this.headerSelector = String.format(
48512            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48513            this.lockedHd.id, this.mainHd.id
48514         );
48515
48516         this.splitterSelector = String.format(
48517            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48518            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48519         );
48520     },
48521     idToCssName : function(s)
48522     {
48523         return s.replace(/[^a-z0-9]+/ig, '-');
48524     },
48525
48526     getHeaderCell : function(index){
48527         return Roo.DomQuery.select(this.headerSelector)[index];
48528     },
48529
48530     getHeaderCellMeasure : function(index){
48531         return this.getHeaderCell(index).firstChild;
48532     },
48533
48534     getHeaderCellText : function(index){
48535         return this.getHeaderCell(index).firstChild.firstChild;
48536     },
48537
48538     getLockedTable : function(){
48539         return this.lockedBody.dom.firstChild;
48540     },
48541
48542     getBodyTable : function(){
48543         return this.mainBody.dom.firstChild;
48544     },
48545
48546     getLockedRow : function(index){
48547         return this.getLockedTable().rows[index];
48548     },
48549
48550     getRow : function(index){
48551         return this.getBodyTable().rows[index];
48552     },
48553
48554     getRowComposite : function(index){
48555         if(!this.rowEl){
48556             this.rowEl = new Roo.CompositeElementLite();
48557         }
48558         var els = [], lrow, mrow;
48559         if(lrow = this.getLockedRow(index)){
48560             els.push(lrow);
48561         }
48562         if(mrow = this.getRow(index)){
48563             els.push(mrow);
48564         }
48565         this.rowEl.elements = els;
48566         return this.rowEl;
48567     },
48568     /**
48569      * Gets the 'td' of the cell
48570      * 
48571      * @param {Integer} rowIndex row to select
48572      * @param {Integer} colIndex column to select
48573      * 
48574      * @return {Object} 
48575      */
48576     getCell : function(rowIndex, colIndex){
48577         var locked = this.cm.getLockedCount();
48578         var source;
48579         if(colIndex < locked){
48580             source = this.lockedBody.dom.firstChild;
48581         }else{
48582             source = this.mainBody.dom.firstChild;
48583             colIndex -= locked;
48584         }
48585         return source.rows[rowIndex].childNodes[colIndex];
48586     },
48587
48588     getCellText : function(rowIndex, colIndex){
48589         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48590     },
48591
48592     getCellBox : function(cell){
48593         var b = this.fly(cell).getBox();
48594         if(Roo.isOpera){ // opera fails to report the Y
48595             b.y = cell.offsetTop + this.mainBody.getY();
48596         }
48597         return b;
48598     },
48599
48600     getCellIndex : function(cell){
48601         var id = String(cell.className).match(this.cellRE);
48602         if(id){
48603             return parseInt(id[1], 10);
48604         }
48605         return 0;
48606     },
48607
48608     findHeaderIndex : function(n){
48609         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48610         return r ? this.getCellIndex(r) : false;
48611     },
48612
48613     findHeaderCell : function(n){
48614         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48615         return r ? r : false;
48616     },
48617
48618     findRowIndex : function(n){
48619         if(!n){
48620             return false;
48621         }
48622         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48623         return r ? r.rowIndex : false;
48624     },
48625
48626     findCellIndex : function(node){
48627         var stop = this.el.dom;
48628         while(node && node != stop){
48629             if(this.findRE.test(node.className)){
48630                 return this.getCellIndex(node);
48631             }
48632             node = node.parentNode;
48633         }
48634         return false;
48635     },
48636
48637     getColumnId : function(index){
48638         return this.cm.getColumnId(index);
48639     },
48640
48641     getSplitters : function()
48642     {
48643         if(this.splitterSelector){
48644            return Roo.DomQuery.select(this.splitterSelector);
48645         }else{
48646             return null;
48647       }
48648     },
48649
48650     getSplitter : function(index){
48651         return this.getSplitters()[index];
48652     },
48653
48654     onRowOver : function(e, t){
48655         var row;
48656         if((row = this.findRowIndex(t)) !== false){
48657             this.getRowComposite(row).addClass("x-grid-row-over");
48658         }
48659     },
48660
48661     onRowOut : function(e, t){
48662         var row;
48663         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48664             this.getRowComposite(row).removeClass("x-grid-row-over");
48665         }
48666     },
48667
48668     renderHeaders : function(){
48669         var cm = this.cm;
48670         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48671         var cb = [], lb = [], sb = [], lsb = [], p = {};
48672         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48673             p.cellId = "x-grid-hd-0-" + i;
48674             p.splitId = "x-grid-csplit-0-" + i;
48675             p.id = cm.getColumnId(i);
48676             p.title = cm.getColumnTooltip(i) || "";
48677             p.value = cm.getColumnHeader(i) || "";
48678             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48679             if(!cm.isLocked(i)){
48680                 cb[cb.length] = ct.apply(p);
48681                 sb[sb.length] = st.apply(p);
48682             }else{
48683                 lb[lb.length] = ct.apply(p);
48684                 lsb[lsb.length] = st.apply(p);
48685             }
48686         }
48687         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48688                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48689     },
48690
48691     updateHeaders : function(){
48692         var html = this.renderHeaders();
48693         this.lockedHd.update(html[0]);
48694         this.mainHd.update(html[1]);
48695     },
48696
48697     /**
48698      * Focuses the specified row.
48699      * @param {Number} row The row index
48700      */
48701     focusRow : function(row)
48702     {
48703         //Roo.log('GridView.focusRow');
48704         var x = this.scroller.dom.scrollLeft;
48705         this.focusCell(row, 0, false);
48706         this.scroller.dom.scrollLeft = x;
48707     },
48708
48709     /**
48710      * Focuses the specified cell.
48711      * @param {Number} row The row index
48712      * @param {Number} col The column index
48713      * @param {Boolean} hscroll false to disable horizontal scrolling
48714      */
48715     focusCell : function(row, col, hscroll)
48716     {
48717         //Roo.log('GridView.focusCell');
48718         var el = this.ensureVisible(row, col, hscroll);
48719         this.focusEl.alignTo(el, "tl-tl");
48720         if(Roo.isGecko){
48721             this.focusEl.focus();
48722         }else{
48723             this.focusEl.focus.defer(1, this.focusEl);
48724         }
48725     },
48726
48727     /**
48728      * Scrolls the specified cell into view
48729      * @param {Number} row The row index
48730      * @param {Number} col The column index
48731      * @param {Boolean} hscroll false to disable horizontal scrolling
48732      */
48733     ensureVisible : function(row, col, hscroll)
48734     {
48735         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48736         //return null; //disable for testing.
48737         if(typeof row != "number"){
48738             row = row.rowIndex;
48739         }
48740         if(row < 0 && row >= this.ds.getCount()){
48741             return  null;
48742         }
48743         col = (col !== undefined ? col : 0);
48744         var cm = this.grid.colModel;
48745         while(cm.isHidden(col)){
48746             col++;
48747         }
48748
48749         var el = this.getCell(row, col);
48750         if(!el){
48751             return null;
48752         }
48753         var c = this.scroller.dom;
48754
48755         var ctop = parseInt(el.offsetTop, 10);
48756         var cleft = parseInt(el.offsetLeft, 10);
48757         var cbot = ctop + el.offsetHeight;
48758         var cright = cleft + el.offsetWidth;
48759         
48760         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48761         var stop = parseInt(c.scrollTop, 10);
48762         var sleft = parseInt(c.scrollLeft, 10);
48763         var sbot = stop + ch;
48764         var sright = sleft + c.clientWidth;
48765         /*
48766         Roo.log('GridView.ensureVisible:' +
48767                 ' ctop:' + ctop +
48768                 ' c.clientHeight:' + c.clientHeight +
48769                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48770                 ' stop:' + stop +
48771                 ' cbot:' + cbot +
48772                 ' sbot:' + sbot +
48773                 ' ch:' + ch  
48774                 );
48775         */
48776         if(ctop < stop){
48777              c.scrollTop = ctop;
48778             //Roo.log("set scrolltop to ctop DISABLE?");
48779         }else if(cbot > sbot){
48780             //Roo.log("set scrolltop to cbot-ch");
48781             c.scrollTop = cbot-ch;
48782         }
48783         
48784         if(hscroll !== false){
48785             if(cleft < sleft){
48786                 c.scrollLeft = cleft;
48787             }else if(cright > sright){
48788                 c.scrollLeft = cright-c.clientWidth;
48789             }
48790         }
48791          
48792         return el;
48793     },
48794
48795     updateColumns : function(){
48796         this.grid.stopEditing();
48797         var cm = this.grid.colModel, colIds = this.getColumnIds();
48798         //var totalWidth = cm.getTotalWidth();
48799         var pos = 0;
48800         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48801             //if(cm.isHidden(i)) continue;
48802             var w = cm.getColumnWidth(i);
48803             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48804             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48805         }
48806         this.updateSplitters();
48807     },
48808
48809     generateRules : function(cm){
48810         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48811         Roo.util.CSS.removeStyleSheet(rulesId);
48812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48813             var cid = cm.getColumnId(i);
48814             var align = '';
48815             if(cm.config[i].align){
48816                 align = 'text-align:'+cm.config[i].align+';';
48817             }
48818             var hidden = '';
48819             if(cm.isHidden(i)){
48820                 hidden = 'display:none;';
48821             }
48822             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48823             ruleBuf.push(
48824                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48825                     this.hdSelector, cid, " {\n", align, width, "}\n",
48826                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48827                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48828         }
48829         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48830     },
48831
48832     updateSplitters : function(){
48833         var cm = this.cm, s = this.getSplitters();
48834         if(s){ // splitters not created yet
48835             var pos = 0, locked = true;
48836             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48837                 if(cm.isHidden(i)) continue;
48838                 var w = cm.getColumnWidth(i); // make sure it's a number
48839                 if(!cm.isLocked(i) && locked){
48840                     pos = 0;
48841                     locked = false;
48842                 }
48843                 pos += w;
48844                 s[i].style.left = (pos-this.splitOffset) + "px";
48845             }
48846         }
48847     },
48848
48849     handleHiddenChange : function(colModel, colIndex, hidden){
48850         if(hidden){
48851             this.hideColumn(colIndex);
48852         }else{
48853             this.unhideColumn(colIndex);
48854         }
48855     },
48856
48857     hideColumn : function(colIndex){
48858         var cid = this.getColumnId(colIndex);
48859         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48860         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48861         if(Roo.isSafari){
48862             this.updateHeaders();
48863         }
48864         this.updateSplitters();
48865         this.layout();
48866     },
48867
48868     unhideColumn : function(colIndex){
48869         var cid = this.getColumnId(colIndex);
48870         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48871         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48872
48873         if(Roo.isSafari){
48874             this.updateHeaders();
48875         }
48876         this.updateSplitters();
48877         this.layout();
48878     },
48879
48880     insertRows : function(dm, firstRow, lastRow, isUpdate){
48881         if(firstRow == 0 && lastRow == dm.getCount()-1){
48882             this.refresh();
48883         }else{
48884             if(!isUpdate){
48885                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48886             }
48887             var s = this.getScrollState();
48888             var markup = this.renderRows(firstRow, lastRow);
48889             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48890             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48891             this.restoreScroll(s);
48892             if(!isUpdate){
48893                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48894                 this.syncRowHeights(firstRow, lastRow);
48895                 this.stripeRows(firstRow);
48896                 this.layout();
48897             }
48898         }
48899     },
48900
48901     bufferRows : function(markup, target, index){
48902         var before = null, trows = target.rows, tbody = target.tBodies[0];
48903         if(index < trows.length){
48904             before = trows[index];
48905         }
48906         var b = document.createElement("div");
48907         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48908         var rows = b.firstChild.rows;
48909         for(var i = 0, len = rows.length; i < len; i++){
48910             if(before){
48911                 tbody.insertBefore(rows[0], before);
48912             }else{
48913                 tbody.appendChild(rows[0]);
48914             }
48915         }
48916         b.innerHTML = "";
48917         b = null;
48918     },
48919
48920     deleteRows : function(dm, firstRow, lastRow){
48921         if(dm.getRowCount()<1){
48922             this.fireEvent("beforerefresh", this);
48923             this.mainBody.update("");
48924             this.lockedBody.update("");
48925             this.fireEvent("refresh", this);
48926         }else{
48927             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48928             var bt = this.getBodyTable();
48929             var tbody = bt.firstChild;
48930             var rows = bt.rows;
48931             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48932                 tbody.removeChild(rows[firstRow]);
48933             }
48934             this.stripeRows(firstRow);
48935             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48936         }
48937     },
48938
48939     updateRows : function(dataSource, firstRow, lastRow){
48940         var s = this.getScrollState();
48941         this.refresh();
48942         this.restoreScroll(s);
48943     },
48944
48945     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48946         if(!noRefresh){
48947            this.refresh();
48948         }
48949         this.updateHeaderSortState();
48950     },
48951
48952     getScrollState : function(){
48953         
48954         var sb = this.scroller.dom;
48955         return {left: sb.scrollLeft, top: sb.scrollTop};
48956     },
48957
48958     stripeRows : function(startRow){
48959         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48960             return;
48961         }
48962         startRow = startRow || 0;
48963         var rows = this.getBodyTable().rows;
48964         var lrows = this.getLockedTable().rows;
48965         var cls = ' x-grid-row-alt ';
48966         for(var i = startRow, len = rows.length; i < len; i++){
48967             var row = rows[i], lrow = lrows[i];
48968             var isAlt = ((i+1) % 2 == 0);
48969             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48970             if(isAlt == hasAlt){
48971                 continue;
48972             }
48973             if(isAlt){
48974                 row.className += " x-grid-row-alt";
48975             }else{
48976                 row.className = row.className.replace("x-grid-row-alt", "");
48977             }
48978             if(lrow){
48979                 lrow.className = row.className;
48980             }
48981         }
48982     },
48983
48984     restoreScroll : function(state){
48985         //Roo.log('GridView.restoreScroll');
48986         var sb = this.scroller.dom;
48987         sb.scrollLeft = state.left;
48988         sb.scrollTop = state.top;
48989         this.syncScroll();
48990     },
48991
48992     syncScroll : function(){
48993         //Roo.log('GridView.syncScroll');
48994         var sb = this.scroller.dom;
48995         var sh = this.mainHd.dom;
48996         var bs = this.mainBody.dom;
48997         var lv = this.lockedBody.dom;
48998         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48999         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49000     },
49001
49002     handleScroll : function(e){
49003         this.syncScroll();
49004         var sb = this.scroller.dom;
49005         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49006         e.stopEvent();
49007     },
49008
49009     handleWheel : function(e){
49010         var d = e.getWheelDelta();
49011         this.scroller.dom.scrollTop -= d*22;
49012         // set this here to prevent jumpy scrolling on large tables
49013         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49014         e.stopEvent();
49015     },
49016
49017     renderRows : function(startRow, endRow){
49018         // pull in all the crap needed to render rows
49019         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49020         var colCount = cm.getColumnCount();
49021
49022         if(ds.getCount() < 1){
49023             return ["", ""];
49024         }
49025
49026         // build a map for all the columns
49027         var cs = [];
49028         for(var i = 0; i < colCount; i++){
49029             var name = cm.getDataIndex(i);
49030             cs[i] = {
49031                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49032                 renderer : cm.getRenderer(i),
49033                 id : cm.getColumnId(i),
49034                 locked : cm.isLocked(i)
49035             };
49036         }
49037
49038         startRow = startRow || 0;
49039         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49040
49041         // records to render
49042         var rs = ds.getRange(startRow, endRow);
49043
49044         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49045     },
49046
49047     // As much as I hate to duplicate code, this was branched because FireFox really hates
49048     // [].join("") on strings. The performance difference was substantial enough to
49049     // branch this function
49050     doRender : Roo.isGecko ?
49051             function(cs, rs, ds, startRow, colCount, stripe){
49052                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49053                 // buffers
49054                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49055                 
49056                 var hasListener = this.grid.hasListener('rowclass');
49057                 var rowcfg = {};
49058                 for(var j = 0, len = rs.length; j < len; j++){
49059                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49060                     for(var i = 0; i < colCount; i++){
49061                         c = cs[i];
49062                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49063                         p.id = c.id;
49064                         p.css = p.attr = "";
49065                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49066                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49067                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49068                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49069                         }
49070                         var markup = ct.apply(p);
49071                         if(!c.locked){
49072                             cb+= markup;
49073                         }else{
49074                             lcb+= markup;
49075                         }
49076                     }
49077                     var alt = [];
49078                     if(stripe && ((rowIndex+1) % 2 == 0)){
49079                         alt.push("x-grid-row-alt")
49080                     }
49081                     if(r.dirty){
49082                         alt.push(  " x-grid-dirty-row");
49083                     }
49084                     rp.cells = lcb;
49085                     if(this.getRowClass){
49086                         alt.push(this.getRowClass(r, rowIndex));
49087                     }
49088                     if (hasListener) {
49089                         rowcfg = {
49090                              
49091                             record: r,
49092                             rowIndex : rowIndex,
49093                             rowClass : ''
49094                         }
49095                         this.grid.fireEvent('rowclass', this, rowcfg);
49096                         alt.push(rowcfg.rowClass);
49097                     }
49098                     rp.alt = alt.join(" ");
49099                     lbuf+= rt.apply(rp);
49100                     rp.cells = cb;
49101                     buf+=  rt.apply(rp);
49102                 }
49103                 return [lbuf, buf];
49104             } :
49105             function(cs, rs, ds, startRow, colCount, stripe){
49106                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49107                 // buffers
49108                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49109                 var hasListener = this.grid.hasListener('rowclass');
49110                 var rowcfg = {};
49111                 for(var j = 0, len = rs.length; j < len; j++){
49112                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49113                     for(var i = 0; i < colCount; i++){
49114                         c = cs[i];
49115                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49116                         p.id = c.id;
49117                         p.css = p.attr = "";
49118                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49119                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49120                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49121                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49122                         }
49123                         var markup = ct.apply(p);
49124                         if(!c.locked){
49125                             cb[cb.length] = markup;
49126                         }else{
49127                             lcb[lcb.length] = markup;
49128                         }
49129                     }
49130                     var alt = [];
49131                     if(stripe && ((rowIndex+1) % 2 == 0)){
49132                         alt.push( "x-grid-row-alt");
49133                     }
49134                     if(r.dirty){
49135                         alt.push(" x-grid-dirty-row");
49136                     }
49137                     rp.cells = lcb;
49138                     if(this.getRowClass){
49139                         alt.push( this.getRowClass(r, rowIndex));
49140                     }
49141                     if (hasListener) {
49142                         rowcfg = {
49143                              
49144                             record: r,
49145                             rowIndex : rowIndex,
49146                             rowClass : ''
49147                         }
49148                         this.grid.fireEvent('rowclass', this, rowcfg);
49149                         alt.push(rowcfg.rowClass);
49150                     }
49151                     rp.alt = alt.join(" ");
49152                     rp.cells = lcb.join("");
49153                     lbuf[lbuf.length] = rt.apply(rp);
49154                     rp.cells = cb.join("");
49155                     buf[buf.length] =  rt.apply(rp);
49156                 }
49157                 return [lbuf.join(""), buf.join("")];
49158             },
49159
49160     renderBody : function(){
49161         var markup = this.renderRows();
49162         var bt = this.templates.body;
49163         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49164     },
49165
49166     /**
49167      * Refreshes the grid
49168      * @param {Boolean} headersToo
49169      */
49170     refresh : function(headersToo){
49171         this.fireEvent("beforerefresh", this);
49172         this.grid.stopEditing();
49173         var result = this.renderBody();
49174         this.lockedBody.update(result[0]);
49175         this.mainBody.update(result[1]);
49176         if(headersToo === true){
49177             this.updateHeaders();
49178             this.updateColumns();
49179             this.updateSplitters();
49180             this.updateHeaderSortState();
49181         }
49182         this.syncRowHeights();
49183         this.layout();
49184         this.fireEvent("refresh", this);
49185     },
49186
49187     handleColumnMove : function(cm, oldIndex, newIndex){
49188         this.indexMap = null;
49189         var s = this.getScrollState();
49190         this.refresh(true);
49191         this.restoreScroll(s);
49192         this.afterMove(newIndex);
49193     },
49194
49195     afterMove : function(colIndex){
49196         if(this.enableMoveAnim && Roo.enableFx){
49197             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49198         }
49199         // if multisort - fix sortOrder, and reload..
49200         if (this.grid.dataSource.multiSort) {
49201             // the we can call sort again..
49202             var dm = this.grid.dataSource;
49203             var cm = this.grid.colModel;
49204             var so = [];
49205             for(var i = 0; i < cm.config.length; i++ ) {
49206                 
49207                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49208                     continue; // dont' bother, it's not in sort list or being set.
49209                 }
49210                 
49211                 so.push(cm.config[i].dataIndex);
49212             };
49213             dm.sortOrder = so;
49214             dm.load(dm.lastOptions);
49215             
49216             
49217         }
49218         
49219     },
49220
49221     updateCell : function(dm, rowIndex, dataIndex){
49222         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49223         if(typeof colIndex == "undefined"){ // not present in grid
49224             return;
49225         }
49226         var cm = this.grid.colModel;
49227         var cell = this.getCell(rowIndex, colIndex);
49228         var cellText = this.getCellText(rowIndex, colIndex);
49229
49230         var p = {
49231             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49232             id : cm.getColumnId(colIndex),
49233             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49234         };
49235         var renderer = cm.getRenderer(colIndex);
49236         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49237         if(typeof val == "undefined" || val === "") val = "&#160;";
49238         cellText.innerHTML = val;
49239         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49240         this.syncRowHeights(rowIndex, rowIndex);
49241     },
49242
49243     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49244         var maxWidth = 0;
49245         if(this.grid.autoSizeHeaders){
49246             var h = this.getHeaderCellMeasure(colIndex);
49247             maxWidth = Math.max(maxWidth, h.scrollWidth);
49248         }
49249         var tb, index;
49250         if(this.cm.isLocked(colIndex)){
49251             tb = this.getLockedTable();
49252             index = colIndex;
49253         }else{
49254             tb = this.getBodyTable();
49255             index = colIndex - this.cm.getLockedCount();
49256         }
49257         if(tb && tb.rows){
49258             var rows = tb.rows;
49259             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49260             for(var i = 0; i < stopIndex; i++){
49261                 var cell = rows[i].childNodes[index].firstChild;
49262                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49263             }
49264         }
49265         return maxWidth + /*margin for error in IE*/ 5;
49266     },
49267     /**
49268      * Autofit a column to its content.
49269      * @param {Number} colIndex
49270      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49271      */
49272      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49273          if(this.cm.isHidden(colIndex)){
49274              return; // can't calc a hidden column
49275          }
49276         if(forceMinSize){
49277             var cid = this.cm.getColumnId(colIndex);
49278             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49279            if(this.grid.autoSizeHeaders){
49280                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49281            }
49282         }
49283         var newWidth = this.calcColumnWidth(colIndex);
49284         this.cm.setColumnWidth(colIndex,
49285             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49286         if(!suppressEvent){
49287             this.grid.fireEvent("columnresize", colIndex, newWidth);
49288         }
49289     },
49290
49291     /**
49292      * Autofits all columns to their content and then expands to fit any extra space in the grid
49293      */
49294      autoSizeColumns : function(){
49295         var cm = this.grid.colModel;
49296         var colCount = cm.getColumnCount();
49297         for(var i = 0; i < colCount; i++){
49298             this.autoSizeColumn(i, true, true);
49299         }
49300         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49301             this.fitColumns();
49302         }else{
49303             this.updateColumns();
49304             this.layout();
49305         }
49306     },
49307
49308     /**
49309      * Autofits all columns to the grid's width proportionate with their current size
49310      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49311      */
49312     fitColumns : function(reserveScrollSpace){
49313         var cm = this.grid.colModel;
49314         var colCount = cm.getColumnCount();
49315         var cols = [];
49316         var width = 0;
49317         var i, w;
49318         for (i = 0; i < colCount; i++){
49319             if(!cm.isHidden(i) && !cm.isFixed(i)){
49320                 w = cm.getColumnWidth(i);
49321                 cols.push(i);
49322                 cols.push(w);
49323                 width += w;
49324             }
49325         }
49326         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49327         if(reserveScrollSpace){
49328             avail -= 17;
49329         }
49330         var frac = (avail - cm.getTotalWidth())/width;
49331         while (cols.length){
49332             w = cols.pop();
49333             i = cols.pop();
49334             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49335         }
49336         this.updateColumns();
49337         this.layout();
49338     },
49339
49340     onRowSelect : function(rowIndex){
49341         var row = this.getRowComposite(rowIndex);
49342         row.addClass("x-grid-row-selected");
49343     },
49344
49345     onRowDeselect : function(rowIndex){
49346         var row = this.getRowComposite(rowIndex);
49347         row.removeClass("x-grid-row-selected");
49348     },
49349
49350     onCellSelect : function(row, col){
49351         var cell = this.getCell(row, col);
49352         if(cell){
49353             Roo.fly(cell).addClass("x-grid-cell-selected");
49354         }
49355     },
49356
49357     onCellDeselect : function(row, col){
49358         var cell = this.getCell(row, col);
49359         if(cell){
49360             Roo.fly(cell).removeClass("x-grid-cell-selected");
49361         }
49362     },
49363
49364     updateHeaderSortState : function(){
49365         
49366         // sort state can be single { field: xxx, direction : yyy}
49367         // or   { xxx=>ASC , yyy : DESC ..... }
49368         
49369         var mstate = {};
49370         if (!this.ds.multiSort) { 
49371             var state = this.ds.getSortState();
49372             if(!state){
49373                 return;
49374             }
49375             mstate[state.field] = state.direction;
49376             // FIXME... - this is not used here.. but might be elsewhere..
49377             this.sortState = state;
49378             
49379         } else {
49380             mstate = this.ds.sortToggle;
49381         }
49382         //remove existing sort classes..
49383         
49384         var sc = this.sortClasses;
49385         var hds = this.el.select(this.headerSelector).removeClass(sc);
49386         
49387         for(var f in mstate) {
49388         
49389             var sortColumn = this.cm.findColumnIndex(f);
49390             
49391             if(sortColumn != -1){
49392                 var sortDir = mstate[f];        
49393                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49394             }
49395         }
49396         
49397          
49398         
49399     },
49400
49401
49402     handleHeaderClick : function(g, index){
49403         if(this.headersDisabled){
49404             return;
49405         }
49406         var dm = g.dataSource, cm = g.colModel;
49407         if(!cm.isSortable(index)){
49408             return;
49409         }
49410         g.stopEditing();
49411         
49412         if (dm.multiSort) {
49413             // update the sortOrder
49414             var so = [];
49415             for(var i = 0; i < cm.config.length; i++ ) {
49416                 
49417                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49418                     continue; // dont' bother, it's not in sort list or being set.
49419                 }
49420                 
49421                 so.push(cm.config[i].dataIndex);
49422             };
49423             dm.sortOrder = so;
49424         }
49425         
49426         
49427         dm.sort(cm.getDataIndex(index));
49428     },
49429
49430
49431     destroy : function(){
49432         if(this.colMenu){
49433             this.colMenu.removeAll();
49434             Roo.menu.MenuMgr.unregister(this.colMenu);
49435             this.colMenu.getEl().remove();
49436             delete this.colMenu;
49437         }
49438         if(this.hmenu){
49439             this.hmenu.removeAll();
49440             Roo.menu.MenuMgr.unregister(this.hmenu);
49441             this.hmenu.getEl().remove();
49442             delete this.hmenu;
49443         }
49444         if(this.grid.enableColumnMove){
49445             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49446             if(dds){
49447                 for(var dd in dds){
49448                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49449                         var elid = dds[dd].dragElId;
49450                         dds[dd].unreg();
49451                         Roo.get(elid).remove();
49452                     } else if(dds[dd].config.isTarget){
49453                         dds[dd].proxyTop.remove();
49454                         dds[dd].proxyBottom.remove();
49455                         dds[dd].unreg();
49456                     }
49457                     if(Roo.dd.DDM.locationCache[dd]){
49458                         delete Roo.dd.DDM.locationCache[dd];
49459                     }
49460                 }
49461                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49462             }
49463         }
49464         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49465         this.bind(null, null);
49466         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49467     },
49468
49469     handleLockChange : function(){
49470         this.refresh(true);
49471     },
49472
49473     onDenyColumnLock : function(){
49474
49475     },
49476
49477     onDenyColumnHide : function(){
49478
49479     },
49480
49481     handleHdMenuClick : function(item){
49482         var index = this.hdCtxIndex;
49483         var cm = this.cm, ds = this.ds;
49484         switch(item.id){
49485             case "asc":
49486                 ds.sort(cm.getDataIndex(index), "ASC");
49487                 break;
49488             case "desc":
49489                 ds.sort(cm.getDataIndex(index), "DESC");
49490                 break;
49491             case "lock":
49492                 var lc = cm.getLockedCount();
49493                 if(cm.getColumnCount(true) <= lc+1){
49494                     this.onDenyColumnLock();
49495                     return;
49496                 }
49497                 if(lc != index){
49498                     cm.setLocked(index, true, true);
49499                     cm.moveColumn(index, lc);
49500                     this.grid.fireEvent("columnmove", index, lc);
49501                 }else{
49502                     cm.setLocked(index, true);
49503                 }
49504             break;
49505             case "unlock":
49506                 var lc = cm.getLockedCount();
49507                 if((lc-1) != index){
49508                     cm.setLocked(index, false, true);
49509                     cm.moveColumn(index, lc-1);
49510                     this.grid.fireEvent("columnmove", index, lc-1);
49511                 }else{
49512                     cm.setLocked(index, false);
49513                 }
49514             break;
49515             default:
49516                 index = cm.getIndexById(item.id.substr(4));
49517                 if(index != -1){
49518                     if(item.checked && cm.getColumnCount(true) <= 1){
49519                         this.onDenyColumnHide();
49520                         return false;
49521                     }
49522                     cm.setHidden(index, item.checked);
49523                 }
49524         }
49525         return true;
49526     },
49527
49528     beforeColMenuShow : function(){
49529         var cm = this.cm,  colCount = cm.getColumnCount();
49530         this.colMenu.removeAll();
49531         for(var i = 0; i < colCount; i++){
49532             this.colMenu.add(new Roo.menu.CheckItem({
49533                 id: "col-"+cm.getColumnId(i),
49534                 text: cm.getColumnHeader(i),
49535                 checked: !cm.isHidden(i),
49536                 hideOnClick:false
49537             }));
49538         }
49539     },
49540
49541     handleHdCtx : function(g, index, e){
49542         e.stopEvent();
49543         var hd = this.getHeaderCell(index);
49544         this.hdCtxIndex = index;
49545         var ms = this.hmenu.items, cm = this.cm;
49546         ms.get("asc").setDisabled(!cm.isSortable(index));
49547         ms.get("desc").setDisabled(!cm.isSortable(index));
49548         if(this.grid.enableColLock !== false){
49549             ms.get("lock").setDisabled(cm.isLocked(index));
49550             ms.get("unlock").setDisabled(!cm.isLocked(index));
49551         }
49552         this.hmenu.show(hd, "tl-bl");
49553     },
49554
49555     handleHdOver : function(e){
49556         var hd = this.findHeaderCell(e.getTarget());
49557         if(hd && !this.headersDisabled){
49558             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49559                this.fly(hd).addClass("x-grid-hd-over");
49560             }
49561         }
49562     },
49563
49564     handleHdOut : function(e){
49565         var hd = this.findHeaderCell(e.getTarget());
49566         if(hd){
49567             this.fly(hd).removeClass("x-grid-hd-over");
49568         }
49569     },
49570
49571     handleSplitDblClick : function(e, t){
49572         var i = this.getCellIndex(t);
49573         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49574             this.autoSizeColumn(i, true);
49575             this.layout();
49576         }
49577     },
49578
49579     render : function(){
49580
49581         var cm = this.cm;
49582         var colCount = cm.getColumnCount();
49583
49584         if(this.grid.monitorWindowResize === true){
49585             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49586         }
49587         var header = this.renderHeaders();
49588         var body = this.templates.body.apply({rows:""});
49589         var html = this.templates.master.apply({
49590             lockedBody: body,
49591             body: body,
49592             lockedHeader: header[0],
49593             header: header[1]
49594         });
49595
49596         //this.updateColumns();
49597
49598         this.grid.getGridEl().dom.innerHTML = html;
49599
49600         this.initElements();
49601         
49602         // a kludge to fix the random scolling effect in webkit
49603         this.el.on("scroll", function() {
49604             this.el.dom.scrollTop=0; // hopefully not recursive..
49605         },this);
49606
49607         this.scroller.on("scroll", this.handleScroll, this);
49608         this.lockedBody.on("mousewheel", this.handleWheel, this);
49609         this.mainBody.on("mousewheel", this.handleWheel, this);
49610
49611         this.mainHd.on("mouseover", this.handleHdOver, this);
49612         this.mainHd.on("mouseout", this.handleHdOut, this);
49613         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49614                 {delegate: "."+this.splitClass});
49615
49616         this.lockedHd.on("mouseover", this.handleHdOver, this);
49617         this.lockedHd.on("mouseout", this.handleHdOut, this);
49618         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49619                 {delegate: "."+this.splitClass});
49620
49621         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49622             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49623         }
49624
49625         this.updateSplitters();
49626
49627         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49628             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49629             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49630         }
49631
49632         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49633             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49634             this.hmenu.add(
49635                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49636                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49637             );
49638             if(this.grid.enableColLock !== false){
49639                 this.hmenu.add('-',
49640                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49641                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49642                 );
49643             }
49644             if(this.grid.enableColumnHide !== false){
49645
49646                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49647                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49648                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49649
49650                 this.hmenu.add('-',
49651                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49652                 );
49653             }
49654             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49655
49656             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49657         }
49658
49659         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49660             this.dd = new Roo.grid.GridDragZone(this.grid, {
49661                 ddGroup : this.grid.ddGroup || 'GridDD'
49662             });
49663         }
49664
49665         /*
49666         for(var i = 0; i < colCount; i++){
49667             if(cm.isHidden(i)){
49668                 this.hideColumn(i);
49669             }
49670             if(cm.config[i].align){
49671                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49672                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49673             }
49674         }*/
49675         
49676         this.updateHeaderSortState();
49677
49678         this.beforeInitialResize();
49679         this.layout(true);
49680
49681         // two part rendering gives faster view to the user
49682         this.renderPhase2.defer(1, this);
49683     },
49684
49685     renderPhase2 : function(){
49686         // render the rows now
49687         this.refresh();
49688         if(this.grid.autoSizeColumns){
49689             this.autoSizeColumns();
49690         }
49691     },
49692
49693     beforeInitialResize : function(){
49694
49695     },
49696
49697     onColumnSplitterMoved : function(i, w){
49698         this.userResized = true;
49699         var cm = this.grid.colModel;
49700         cm.setColumnWidth(i, w, true);
49701         var cid = cm.getColumnId(i);
49702         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49703         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49704         this.updateSplitters();
49705         this.layout();
49706         this.grid.fireEvent("columnresize", i, w);
49707     },
49708
49709     syncRowHeights : function(startIndex, endIndex){
49710         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49711             startIndex = startIndex || 0;
49712             var mrows = this.getBodyTable().rows;
49713             var lrows = this.getLockedTable().rows;
49714             var len = mrows.length-1;
49715             endIndex = Math.min(endIndex || len, len);
49716             for(var i = startIndex; i <= endIndex; i++){
49717                 var m = mrows[i], l = lrows[i];
49718                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49719                 m.style.height = l.style.height = h + "px";
49720             }
49721         }
49722     },
49723
49724     layout : function(initialRender, is2ndPass){
49725         var g = this.grid;
49726         var auto = g.autoHeight;
49727         var scrollOffset = 16;
49728         var c = g.getGridEl(), cm = this.cm,
49729                 expandCol = g.autoExpandColumn,
49730                 gv = this;
49731         //c.beginMeasure();
49732
49733         if(!c.dom.offsetWidth){ // display:none?
49734             if(initialRender){
49735                 this.lockedWrap.show();
49736                 this.mainWrap.show();
49737             }
49738             return;
49739         }
49740
49741         var hasLock = this.cm.isLocked(0);
49742
49743         var tbh = this.headerPanel.getHeight();
49744         var bbh = this.footerPanel.getHeight();
49745
49746         if(auto){
49747             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49748             var newHeight = ch + c.getBorderWidth("tb");
49749             if(g.maxHeight){
49750                 newHeight = Math.min(g.maxHeight, newHeight);
49751             }
49752             c.setHeight(newHeight);
49753         }
49754
49755         if(g.autoWidth){
49756             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49757         }
49758
49759         var s = this.scroller;
49760
49761         var csize = c.getSize(true);
49762
49763         this.el.setSize(csize.width, csize.height);
49764
49765         this.headerPanel.setWidth(csize.width);
49766         this.footerPanel.setWidth(csize.width);
49767
49768         var hdHeight = this.mainHd.getHeight();
49769         var vw = csize.width;
49770         var vh = csize.height - (tbh + bbh);
49771
49772         s.setSize(vw, vh);
49773
49774         var bt = this.getBodyTable();
49775         var ltWidth = hasLock ?
49776                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49777
49778         var scrollHeight = bt.offsetHeight;
49779         var scrollWidth = ltWidth + bt.offsetWidth;
49780         var vscroll = false, hscroll = false;
49781
49782         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49783
49784         var lw = this.lockedWrap, mw = this.mainWrap;
49785         var lb = this.lockedBody, mb = this.mainBody;
49786
49787         setTimeout(function(){
49788             var t = s.dom.offsetTop;
49789             var w = s.dom.clientWidth,
49790                 h = s.dom.clientHeight;
49791
49792             lw.setTop(t);
49793             lw.setSize(ltWidth, h);
49794
49795             mw.setLeftTop(ltWidth, t);
49796             mw.setSize(w-ltWidth, h);
49797
49798             lb.setHeight(h-hdHeight);
49799             mb.setHeight(h-hdHeight);
49800
49801             if(is2ndPass !== true && !gv.userResized && expandCol){
49802                 // high speed resize without full column calculation
49803                 
49804                 var ci = cm.getIndexById(expandCol);
49805                 if (ci < 0) {
49806                     ci = cm.findColumnIndex(expandCol);
49807                 }
49808                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49809                 var expandId = cm.getColumnId(ci);
49810                 var  tw = cm.getTotalWidth(false);
49811                 var currentWidth = cm.getColumnWidth(ci);
49812                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49813                 if(currentWidth != cw){
49814                     cm.setColumnWidth(ci, cw, true);
49815                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49816                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49817                     gv.updateSplitters();
49818                     gv.layout(false, true);
49819                 }
49820             }
49821
49822             if(initialRender){
49823                 lw.show();
49824                 mw.show();
49825             }
49826             //c.endMeasure();
49827         }, 10);
49828     },
49829
49830     onWindowResize : function(){
49831         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49832             return;
49833         }
49834         this.layout();
49835     },
49836
49837     appendFooter : function(parentEl){
49838         return null;
49839     },
49840
49841     sortAscText : "Sort Ascending",
49842     sortDescText : "Sort Descending",
49843     lockText : "Lock Column",
49844     unlockText : "Unlock Column",
49845     columnsText : "Columns"
49846 });
49847
49848
49849 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49850     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49851     this.proxy.el.addClass('x-grid3-col-dd');
49852 };
49853
49854 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49855     handleMouseDown : function(e){
49856
49857     },
49858
49859     callHandleMouseDown : function(e){
49860         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49861     }
49862 });
49863 /*
49864  * Based on:
49865  * Ext JS Library 1.1.1
49866  * Copyright(c) 2006-2007, Ext JS, LLC.
49867  *
49868  * Originally Released Under LGPL - original licence link has changed is not relivant.
49869  *
49870  * Fork - LGPL
49871  * <script type="text/javascript">
49872  */
49873  
49874 // private
49875 // This is a support class used internally by the Grid components
49876 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49877     this.grid = grid;
49878     this.view = grid.getView();
49879     this.proxy = this.view.resizeProxy;
49880     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49881         "gridSplitters" + this.grid.getGridEl().id, {
49882         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49883     });
49884     this.setHandleElId(Roo.id(hd));
49885     this.setOuterHandleElId(Roo.id(hd2));
49886     this.scroll = false;
49887 };
49888 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49889     fly: Roo.Element.fly,
49890
49891     b4StartDrag : function(x, y){
49892         this.view.headersDisabled = true;
49893         this.proxy.setHeight(this.view.mainWrap.getHeight());
49894         var w = this.cm.getColumnWidth(this.cellIndex);
49895         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49896         this.resetConstraints();
49897         this.setXConstraint(minw, 1000);
49898         this.setYConstraint(0, 0);
49899         this.minX = x - minw;
49900         this.maxX = x + 1000;
49901         this.startPos = x;
49902         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49903     },
49904
49905
49906     handleMouseDown : function(e){
49907         ev = Roo.EventObject.setEvent(e);
49908         var t = this.fly(ev.getTarget());
49909         if(t.hasClass("x-grid-split")){
49910             this.cellIndex = this.view.getCellIndex(t.dom);
49911             this.split = t.dom;
49912             this.cm = this.grid.colModel;
49913             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49914                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49915             }
49916         }
49917     },
49918
49919     endDrag : function(e){
49920         this.view.headersDisabled = false;
49921         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49922         var diff = endX - this.startPos;
49923         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49924     },
49925
49926     autoOffset : function(){
49927         this.setDelta(0,0);
49928     }
49929 });/*
49930  * Based on:
49931  * Ext JS Library 1.1.1
49932  * Copyright(c) 2006-2007, Ext JS, LLC.
49933  *
49934  * Originally Released Under LGPL - original licence link has changed is not relivant.
49935  *
49936  * Fork - LGPL
49937  * <script type="text/javascript">
49938  */
49939  
49940 // private
49941 // This is a support class used internally by the Grid components
49942 Roo.grid.GridDragZone = function(grid, config){
49943     this.view = grid.getView();
49944     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49945     if(this.view.lockedBody){
49946         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49947         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49948     }
49949     this.scroll = false;
49950     this.grid = grid;
49951     this.ddel = document.createElement('div');
49952     this.ddel.className = 'x-grid-dd-wrap';
49953 };
49954
49955 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49956     ddGroup : "GridDD",
49957
49958     getDragData : function(e){
49959         var t = Roo.lib.Event.getTarget(e);
49960         var rowIndex = this.view.findRowIndex(t);
49961         if(rowIndex !== false){
49962             var sm = this.grid.selModel;
49963             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49964               //  sm.mouseDown(e, t);
49965             //}
49966             if (e.hasModifier()){
49967                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49968             }
49969             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49970         }
49971         return false;
49972     },
49973
49974     onInitDrag : function(e){
49975         var data = this.dragData;
49976         this.ddel.innerHTML = this.grid.getDragDropText();
49977         this.proxy.update(this.ddel);
49978         // fire start drag?
49979     },
49980
49981     afterRepair : function(){
49982         this.dragging = false;
49983     },
49984
49985     getRepairXY : function(e, data){
49986         return false;
49987     },
49988
49989     onEndDrag : function(data, e){
49990         // fire end drag?
49991     },
49992
49993     onValidDrop : function(dd, e, id){
49994         // fire drag drop?
49995         this.hideProxy();
49996     },
49997
49998     beforeInvalidDrop : function(e, id){
49999
50000     }
50001 });/*
50002  * Based on:
50003  * Ext JS Library 1.1.1
50004  * Copyright(c) 2006-2007, Ext JS, LLC.
50005  *
50006  * Originally Released Under LGPL - original licence link has changed is not relivant.
50007  *
50008  * Fork - LGPL
50009  * <script type="text/javascript">
50010  */
50011  
50012
50013 /**
50014  * @class Roo.grid.ColumnModel
50015  * @extends Roo.util.Observable
50016  * This is the default implementation of a ColumnModel used by the Grid. It defines
50017  * the columns in the grid.
50018  * <br>Usage:<br>
50019  <pre><code>
50020  var colModel = new Roo.grid.ColumnModel([
50021         {header: "Ticker", width: 60, sortable: true, locked: true},
50022         {header: "Company Name", width: 150, sortable: true},
50023         {header: "Market Cap.", width: 100, sortable: true},
50024         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50025         {header: "Employees", width: 100, sortable: true, resizable: false}
50026  ]);
50027  </code></pre>
50028  * <p>
50029  
50030  * The config options listed for this class are options which may appear in each
50031  * individual column definition.
50032  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50033  * @constructor
50034  * @param {Object} config An Array of column config objects. See this class's
50035  * config objects for details.
50036 */
50037 Roo.grid.ColumnModel = function(config){
50038         /**
50039      * The config passed into the constructor
50040      */
50041     this.config = config;
50042     this.lookup = {};
50043
50044     // if no id, create one
50045     // if the column does not have a dataIndex mapping,
50046     // map it to the order it is in the config
50047     for(var i = 0, len = config.length; i < len; i++){
50048         var c = config[i];
50049         if(typeof c.dataIndex == "undefined"){
50050             c.dataIndex = i;
50051         }
50052         if(typeof c.renderer == "string"){
50053             c.renderer = Roo.util.Format[c.renderer];
50054         }
50055         if(typeof c.id == "undefined"){
50056             c.id = Roo.id();
50057         }
50058         if(c.editor && c.editor.xtype){
50059             c.editor  = Roo.factory(c.editor, Roo.grid);
50060         }
50061         if(c.editor && c.editor.isFormField){
50062             c.editor = new Roo.grid.GridEditor(c.editor);
50063         }
50064         this.lookup[c.id] = c;
50065     }
50066
50067     /**
50068      * The width of columns which have no width specified (defaults to 100)
50069      * @type Number
50070      */
50071     this.defaultWidth = 100;
50072
50073     /**
50074      * Default sortable of columns which have no sortable specified (defaults to false)
50075      * @type Boolean
50076      */
50077     this.defaultSortable = false;
50078
50079     this.addEvents({
50080         /**
50081              * @event widthchange
50082              * Fires when the width of a column changes.
50083              * @param {ColumnModel} this
50084              * @param {Number} columnIndex The column index
50085              * @param {Number} newWidth The new width
50086              */
50087             "widthchange": true,
50088         /**
50089              * @event headerchange
50090              * Fires when the text of a header changes.
50091              * @param {ColumnModel} this
50092              * @param {Number} columnIndex The column index
50093              * @param {Number} newText The new header text
50094              */
50095             "headerchange": true,
50096         /**
50097              * @event hiddenchange
50098              * Fires when a column is hidden or "unhidden".
50099              * @param {ColumnModel} this
50100              * @param {Number} columnIndex The column index
50101              * @param {Boolean} hidden true if hidden, false otherwise
50102              */
50103             "hiddenchange": true,
50104             /**
50105          * @event columnmoved
50106          * Fires when a column is moved.
50107          * @param {ColumnModel} this
50108          * @param {Number} oldIndex
50109          * @param {Number} newIndex
50110          */
50111         "columnmoved" : true,
50112         /**
50113          * @event columlockchange
50114          * Fires when a column's locked state is changed
50115          * @param {ColumnModel} this
50116          * @param {Number} colIndex
50117          * @param {Boolean} locked true if locked
50118          */
50119         "columnlockchange" : true
50120     });
50121     Roo.grid.ColumnModel.superclass.constructor.call(this);
50122 };
50123 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50124     /**
50125      * @cfg {String} header The header text to display in the Grid view.
50126      */
50127     /**
50128      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50129      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50130      * specified, the column's index is used as an index into the Record's data Array.
50131      */
50132     /**
50133      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50134      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50135      */
50136     /**
50137      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50138      * Defaults to the value of the {@link #defaultSortable} property.
50139      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50140      */
50141     /**
50142      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50143      */
50144     /**
50145      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50146      */
50147     /**
50148      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50149      */
50150     /**
50151      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50152      */
50153     /**
50154      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50155      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50156      * default renderer uses the raw data value.
50157      */
50158        /**
50159      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50160      */
50161     /**
50162      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50163      */
50164
50165     /**
50166      * Returns the id of the column at the specified index.
50167      * @param {Number} index The column index
50168      * @return {String} the id
50169      */
50170     getColumnId : function(index){
50171         return this.config[index].id;
50172     },
50173
50174     /**
50175      * Returns the column for a specified id.
50176      * @param {String} id The column id
50177      * @return {Object} the column
50178      */
50179     getColumnById : function(id){
50180         return this.lookup[id];
50181     },
50182
50183     
50184     /**
50185      * Returns the column for a specified dataIndex.
50186      * @param {String} dataIndex The column dataIndex
50187      * @return {Object|Boolean} the column or false if not found
50188      */
50189     getColumnByDataIndex: function(dataIndex){
50190         var index = this.findColumnIndex(dataIndex);
50191         return index > -1 ? this.config[index] : false;
50192     },
50193     
50194     /**
50195      * Returns the index for a specified column id.
50196      * @param {String} id The column id
50197      * @return {Number} the index, or -1 if not found
50198      */
50199     getIndexById : function(id){
50200         for(var i = 0, len = this.config.length; i < len; i++){
50201             if(this.config[i].id == id){
50202                 return i;
50203             }
50204         }
50205         return -1;
50206     },
50207     
50208     /**
50209      * Returns the index for a specified column dataIndex.
50210      * @param {String} dataIndex The column dataIndex
50211      * @return {Number} the index, or -1 if not found
50212      */
50213     
50214     findColumnIndex : function(dataIndex){
50215         for(var i = 0, len = this.config.length; i < len; i++){
50216             if(this.config[i].dataIndex == dataIndex){
50217                 return i;
50218             }
50219         }
50220         return -1;
50221     },
50222     
50223     
50224     moveColumn : function(oldIndex, newIndex){
50225         var c = this.config[oldIndex];
50226         this.config.splice(oldIndex, 1);
50227         this.config.splice(newIndex, 0, c);
50228         this.dataMap = null;
50229         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50230     },
50231
50232     isLocked : function(colIndex){
50233         return this.config[colIndex].locked === true;
50234     },
50235
50236     setLocked : function(colIndex, value, suppressEvent){
50237         if(this.isLocked(colIndex) == value){
50238             return;
50239         }
50240         this.config[colIndex].locked = value;
50241         if(!suppressEvent){
50242             this.fireEvent("columnlockchange", this, colIndex, value);
50243         }
50244     },
50245
50246     getTotalLockedWidth : function(){
50247         var totalWidth = 0;
50248         for(var i = 0; i < this.config.length; i++){
50249             if(this.isLocked(i) && !this.isHidden(i)){
50250                 this.totalWidth += this.getColumnWidth(i);
50251             }
50252         }
50253         return totalWidth;
50254     },
50255
50256     getLockedCount : function(){
50257         for(var i = 0, len = this.config.length; i < len; i++){
50258             if(!this.isLocked(i)){
50259                 return i;
50260             }
50261         }
50262     },
50263
50264     /**
50265      * Returns the number of columns.
50266      * @return {Number}
50267      */
50268     getColumnCount : function(visibleOnly){
50269         if(visibleOnly === true){
50270             var c = 0;
50271             for(var i = 0, len = this.config.length; i < len; i++){
50272                 if(!this.isHidden(i)){
50273                     c++;
50274                 }
50275             }
50276             return c;
50277         }
50278         return this.config.length;
50279     },
50280
50281     /**
50282      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50283      * @param {Function} fn
50284      * @param {Object} scope (optional)
50285      * @return {Array} result
50286      */
50287     getColumnsBy : function(fn, scope){
50288         var r = [];
50289         for(var i = 0, len = this.config.length; i < len; i++){
50290             var c = this.config[i];
50291             if(fn.call(scope||this, c, i) === true){
50292                 r[r.length] = c;
50293             }
50294         }
50295         return r;
50296     },
50297
50298     /**
50299      * Returns true if the specified column is sortable.
50300      * @param {Number} col The column index
50301      * @return {Boolean}
50302      */
50303     isSortable : function(col){
50304         if(typeof this.config[col].sortable == "undefined"){
50305             return this.defaultSortable;
50306         }
50307         return this.config[col].sortable;
50308     },
50309
50310     /**
50311      * Returns the rendering (formatting) function defined for the column.
50312      * @param {Number} col The column index.
50313      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50314      */
50315     getRenderer : function(col){
50316         if(!this.config[col].renderer){
50317             return Roo.grid.ColumnModel.defaultRenderer;
50318         }
50319         return this.config[col].renderer;
50320     },
50321
50322     /**
50323      * Sets the rendering (formatting) function for a column.
50324      * @param {Number} col The column index
50325      * @param {Function} fn The function to use to process the cell's raw data
50326      * to return HTML markup for the grid view. The render function is called with
50327      * the following parameters:<ul>
50328      * <li>Data value.</li>
50329      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50330      * <li>css A CSS style string to apply to the table cell.</li>
50331      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50332      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50333      * <li>Row index</li>
50334      * <li>Column index</li>
50335      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50336      */
50337     setRenderer : function(col, fn){
50338         this.config[col].renderer = fn;
50339     },
50340
50341     /**
50342      * Returns the width for the specified column.
50343      * @param {Number} col The column index
50344      * @return {Number}
50345      */
50346     getColumnWidth : function(col){
50347         return this.config[col].width * 1 || this.defaultWidth;
50348     },
50349
50350     /**
50351      * Sets the width for a column.
50352      * @param {Number} col The column index
50353      * @param {Number} width The new width
50354      */
50355     setColumnWidth : function(col, width, suppressEvent){
50356         this.config[col].width = width;
50357         this.totalWidth = null;
50358         if(!suppressEvent){
50359              this.fireEvent("widthchange", this, col, width);
50360         }
50361     },
50362
50363     /**
50364      * Returns the total width of all columns.
50365      * @param {Boolean} includeHidden True to include hidden column widths
50366      * @return {Number}
50367      */
50368     getTotalWidth : function(includeHidden){
50369         if(!this.totalWidth){
50370             this.totalWidth = 0;
50371             for(var i = 0, len = this.config.length; i < len; i++){
50372                 if(includeHidden || !this.isHidden(i)){
50373                     this.totalWidth += this.getColumnWidth(i);
50374                 }
50375             }
50376         }
50377         return this.totalWidth;
50378     },
50379
50380     /**
50381      * Returns the header for the specified column.
50382      * @param {Number} col The column index
50383      * @return {String}
50384      */
50385     getColumnHeader : function(col){
50386         return this.config[col].header;
50387     },
50388
50389     /**
50390      * Sets the header for a column.
50391      * @param {Number} col The column index
50392      * @param {String} header The new header
50393      */
50394     setColumnHeader : function(col, header){
50395         this.config[col].header = header;
50396         this.fireEvent("headerchange", this, col, header);
50397     },
50398
50399     /**
50400      * Returns the tooltip for the specified column.
50401      * @param {Number} col The column index
50402      * @return {String}
50403      */
50404     getColumnTooltip : function(col){
50405             return this.config[col].tooltip;
50406     },
50407     /**
50408      * Sets the tooltip for a column.
50409      * @param {Number} col The column index
50410      * @param {String} tooltip The new tooltip
50411      */
50412     setColumnTooltip : function(col, tooltip){
50413             this.config[col].tooltip = tooltip;
50414     },
50415
50416     /**
50417      * Returns the dataIndex for the specified column.
50418      * @param {Number} col The column index
50419      * @return {Number}
50420      */
50421     getDataIndex : function(col){
50422         return this.config[col].dataIndex;
50423     },
50424
50425     /**
50426      * Sets the dataIndex for a column.
50427      * @param {Number} col The column index
50428      * @param {Number} dataIndex The new dataIndex
50429      */
50430     setDataIndex : function(col, dataIndex){
50431         this.config[col].dataIndex = dataIndex;
50432     },
50433
50434     
50435     
50436     /**
50437      * Returns true if the cell is editable.
50438      * @param {Number} colIndex The column index
50439      * @param {Number} rowIndex The row index
50440      * @return {Boolean}
50441      */
50442     isCellEditable : function(colIndex, rowIndex){
50443         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50444     },
50445
50446     /**
50447      * Returns the editor defined for the cell/column.
50448      * return false or null to disable editing.
50449      * @param {Number} colIndex The column index
50450      * @param {Number} rowIndex The row index
50451      * @return {Object}
50452      */
50453     getCellEditor : function(colIndex, rowIndex){
50454         return this.config[colIndex].editor;
50455     },
50456
50457     /**
50458      * Sets if a column is editable.
50459      * @param {Number} col The column index
50460      * @param {Boolean} editable True if the column is editable
50461      */
50462     setEditable : function(col, editable){
50463         this.config[col].editable = editable;
50464     },
50465
50466
50467     /**
50468      * Returns true if the column is hidden.
50469      * @param {Number} colIndex The column index
50470      * @return {Boolean}
50471      */
50472     isHidden : function(colIndex){
50473         return this.config[colIndex].hidden;
50474     },
50475
50476
50477     /**
50478      * Returns true if the column width cannot be changed
50479      */
50480     isFixed : function(colIndex){
50481         return this.config[colIndex].fixed;
50482     },
50483
50484     /**
50485      * Returns true if the column can be resized
50486      * @return {Boolean}
50487      */
50488     isResizable : function(colIndex){
50489         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50490     },
50491     /**
50492      * Sets if a column is hidden.
50493      * @param {Number} colIndex The column index
50494      * @param {Boolean} hidden True if the column is hidden
50495      */
50496     setHidden : function(colIndex, hidden){
50497         this.config[colIndex].hidden = hidden;
50498         this.totalWidth = null;
50499         this.fireEvent("hiddenchange", this, colIndex, hidden);
50500     },
50501
50502     /**
50503      * Sets the editor for a column.
50504      * @param {Number} col The column index
50505      * @param {Object} editor The editor object
50506      */
50507     setEditor : function(col, editor){
50508         this.config[col].editor = editor;
50509     }
50510 });
50511
50512 Roo.grid.ColumnModel.defaultRenderer = function(value){
50513         if(typeof value == "string" && value.length < 1){
50514             return "&#160;";
50515         }
50516         return value;
50517 };
50518
50519 // Alias for backwards compatibility
50520 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50521 /*
50522  * Based on:
50523  * Ext JS Library 1.1.1
50524  * Copyright(c) 2006-2007, Ext JS, LLC.
50525  *
50526  * Originally Released Under LGPL - original licence link has changed is not relivant.
50527  *
50528  * Fork - LGPL
50529  * <script type="text/javascript">
50530  */
50531
50532 /**
50533  * @class Roo.grid.AbstractSelectionModel
50534  * @extends Roo.util.Observable
50535  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50536  * implemented by descendant classes.  This class should not be directly instantiated.
50537  * @constructor
50538  */
50539 Roo.grid.AbstractSelectionModel = function(){
50540     this.locked = false;
50541     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50542 };
50543
50544 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50545     /** @ignore Called by the grid automatically. Do not call directly. */
50546     init : function(grid){
50547         this.grid = grid;
50548         this.initEvents();
50549     },
50550
50551     /**
50552      * Locks the selections.
50553      */
50554     lock : function(){
50555         this.locked = true;
50556     },
50557
50558     /**
50559      * Unlocks the selections.
50560      */
50561     unlock : function(){
50562         this.locked = false;
50563     },
50564
50565     /**
50566      * Returns true if the selections are locked.
50567      * @return {Boolean}
50568      */
50569     isLocked : function(){
50570         return this.locked;
50571     }
50572 });/*
50573  * Based on:
50574  * Ext JS Library 1.1.1
50575  * Copyright(c) 2006-2007, Ext JS, LLC.
50576  *
50577  * Originally Released Under LGPL - original licence link has changed is not relivant.
50578  *
50579  * Fork - LGPL
50580  * <script type="text/javascript">
50581  */
50582 /**
50583  * @extends Roo.grid.AbstractSelectionModel
50584  * @class Roo.grid.RowSelectionModel
50585  * The default SelectionModel used by {@link Roo.grid.Grid}.
50586  * It supports multiple selections and keyboard selection/navigation. 
50587  * @constructor
50588  * @param {Object} config
50589  */
50590 Roo.grid.RowSelectionModel = function(config){
50591     Roo.apply(this, config);
50592     this.selections = new Roo.util.MixedCollection(false, function(o){
50593         return o.id;
50594     });
50595
50596     this.last = false;
50597     this.lastActive = false;
50598
50599     this.addEvents({
50600         /**
50601              * @event selectionchange
50602              * Fires when the selection changes
50603              * @param {SelectionModel} this
50604              */
50605             "selectionchange" : true,
50606         /**
50607              * @event afterselectionchange
50608              * Fires after the selection changes (eg. by key press or clicking)
50609              * @param {SelectionModel} this
50610              */
50611             "afterselectionchange" : true,
50612         /**
50613              * @event beforerowselect
50614              * Fires when a row is selected being selected, return false to cancel.
50615              * @param {SelectionModel} this
50616              * @param {Number} rowIndex The selected index
50617              * @param {Boolean} keepExisting False if other selections will be cleared
50618              */
50619             "beforerowselect" : true,
50620         /**
50621              * @event rowselect
50622              * Fires when a row is selected.
50623              * @param {SelectionModel} this
50624              * @param {Number} rowIndex The selected index
50625              * @param {Roo.data.Record} r The record
50626              */
50627             "rowselect" : true,
50628         /**
50629              * @event rowdeselect
50630              * Fires when a row is deselected.
50631              * @param {SelectionModel} this
50632              * @param {Number} rowIndex The selected index
50633              */
50634         "rowdeselect" : true
50635     });
50636     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50637     this.locked = false;
50638 };
50639
50640 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50641     /**
50642      * @cfg {Boolean} singleSelect
50643      * True to allow selection of only one row at a time (defaults to false)
50644      */
50645     singleSelect : false,
50646
50647     // private
50648     initEvents : function(){
50649
50650         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50651             this.grid.on("mousedown", this.handleMouseDown, this);
50652         }else{ // allow click to work like normal
50653             this.grid.on("rowclick", this.handleDragableRowClick, this);
50654         }
50655
50656         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50657             "up" : function(e){
50658                 if(!e.shiftKey){
50659                     this.selectPrevious(e.shiftKey);
50660                 }else if(this.last !== false && this.lastActive !== false){
50661                     var last = this.last;
50662                     this.selectRange(this.last,  this.lastActive-1);
50663                     this.grid.getView().focusRow(this.lastActive);
50664                     if(last !== false){
50665                         this.last = last;
50666                     }
50667                 }else{
50668                     this.selectFirstRow();
50669                 }
50670                 this.fireEvent("afterselectionchange", this);
50671             },
50672             "down" : function(e){
50673                 if(!e.shiftKey){
50674                     this.selectNext(e.shiftKey);
50675                 }else if(this.last !== false && this.lastActive !== false){
50676                     var last = this.last;
50677                     this.selectRange(this.last,  this.lastActive+1);
50678                     this.grid.getView().focusRow(this.lastActive);
50679                     if(last !== false){
50680                         this.last = last;
50681                     }
50682                 }else{
50683                     this.selectFirstRow();
50684                 }
50685                 this.fireEvent("afterselectionchange", this);
50686             },
50687             scope: this
50688         });
50689
50690         var view = this.grid.view;
50691         view.on("refresh", this.onRefresh, this);
50692         view.on("rowupdated", this.onRowUpdated, this);
50693         view.on("rowremoved", this.onRemove, this);
50694     },
50695
50696     // private
50697     onRefresh : function(){
50698         var ds = this.grid.dataSource, i, v = this.grid.view;
50699         var s = this.selections;
50700         s.each(function(r){
50701             if((i = ds.indexOfId(r.id)) != -1){
50702                 v.onRowSelect(i);
50703             }else{
50704                 s.remove(r);
50705             }
50706         });
50707     },
50708
50709     // private
50710     onRemove : function(v, index, r){
50711         this.selections.remove(r);
50712     },
50713
50714     // private
50715     onRowUpdated : function(v, index, r){
50716         if(this.isSelected(r)){
50717             v.onRowSelect(index);
50718         }
50719     },
50720
50721     /**
50722      * Select records.
50723      * @param {Array} records The records to select
50724      * @param {Boolean} keepExisting (optional) True to keep existing selections
50725      */
50726     selectRecords : function(records, keepExisting){
50727         if(!keepExisting){
50728             this.clearSelections();
50729         }
50730         var ds = this.grid.dataSource;
50731         for(var i = 0, len = records.length; i < len; i++){
50732             this.selectRow(ds.indexOf(records[i]), true);
50733         }
50734     },
50735
50736     /**
50737      * Gets the number of selected rows.
50738      * @return {Number}
50739      */
50740     getCount : function(){
50741         return this.selections.length;
50742     },
50743
50744     /**
50745      * Selects the first row in the grid.
50746      */
50747     selectFirstRow : function(){
50748         this.selectRow(0);
50749     },
50750
50751     /**
50752      * Select the last row.
50753      * @param {Boolean} keepExisting (optional) True to keep existing selections
50754      */
50755     selectLastRow : function(keepExisting){
50756         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50757     },
50758
50759     /**
50760      * Selects the row immediately following the last selected row.
50761      * @param {Boolean} keepExisting (optional) True to keep existing selections
50762      */
50763     selectNext : function(keepExisting){
50764         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50765             this.selectRow(this.last+1, keepExisting);
50766             this.grid.getView().focusRow(this.last);
50767         }
50768     },
50769
50770     /**
50771      * Selects the row that precedes the last selected row.
50772      * @param {Boolean} keepExisting (optional) True to keep existing selections
50773      */
50774     selectPrevious : function(keepExisting){
50775         if(this.last){
50776             this.selectRow(this.last-1, keepExisting);
50777             this.grid.getView().focusRow(this.last);
50778         }
50779     },
50780
50781     /**
50782      * Returns the selected records
50783      * @return {Array} Array of selected records
50784      */
50785     getSelections : function(){
50786         return [].concat(this.selections.items);
50787     },
50788
50789     /**
50790      * Returns the first selected record.
50791      * @return {Record}
50792      */
50793     getSelected : function(){
50794         return this.selections.itemAt(0);
50795     },
50796
50797
50798     /**
50799      * Clears all selections.
50800      */
50801     clearSelections : function(fast){
50802         if(this.locked) return;
50803         if(fast !== true){
50804             var ds = this.grid.dataSource;
50805             var s = this.selections;
50806             s.each(function(r){
50807                 this.deselectRow(ds.indexOfId(r.id));
50808             }, this);
50809             s.clear();
50810         }else{
50811             this.selections.clear();
50812         }
50813         this.last = false;
50814     },
50815
50816
50817     /**
50818      * Selects all rows.
50819      */
50820     selectAll : function(){
50821         if(this.locked) return;
50822         this.selections.clear();
50823         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50824             this.selectRow(i, true);
50825         }
50826     },
50827
50828     /**
50829      * Returns True if there is a selection.
50830      * @return {Boolean}
50831      */
50832     hasSelection : function(){
50833         return this.selections.length > 0;
50834     },
50835
50836     /**
50837      * Returns True if the specified row is selected.
50838      * @param {Number/Record} record The record or index of the record to check
50839      * @return {Boolean}
50840      */
50841     isSelected : function(index){
50842         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50843         return (r && this.selections.key(r.id) ? true : false);
50844     },
50845
50846     /**
50847      * Returns True if the specified record id is selected.
50848      * @param {String} id The id of record to check
50849      * @return {Boolean}
50850      */
50851     isIdSelected : function(id){
50852         return (this.selections.key(id) ? true : false);
50853     },
50854
50855     // private
50856     handleMouseDown : function(e, t){
50857         var view = this.grid.getView(), rowIndex;
50858         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50859             return;
50860         };
50861         if(e.shiftKey && this.last !== false){
50862             var last = this.last;
50863             this.selectRange(last, rowIndex, e.ctrlKey);
50864             this.last = last; // reset the last
50865             view.focusRow(rowIndex);
50866         }else{
50867             var isSelected = this.isSelected(rowIndex);
50868             if(e.button !== 0 && isSelected){
50869                 view.focusRow(rowIndex);
50870             }else if(e.ctrlKey && isSelected){
50871                 this.deselectRow(rowIndex);
50872             }else if(!isSelected){
50873                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50874                 view.focusRow(rowIndex);
50875             }
50876         }
50877         this.fireEvent("afterselectionchange", this);
50878     },
50879     // private
50880     handleDragableRowClick :  function(grid, rowIndex, e) 
50881     {
50882         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50883             this.selectRow(rowIndex, false);
50884             grid.view.focusRow(rowIndex);
50885              this.fireEvent("afterselectionchange", this);
50886         }
50887     },
50888     
50889     /**
50890      * Selects multiple rows.
50891      * @param {Array} rows Array of the indexes of the row to select
50892      * @param {Boolean} keepExisting (optional) True to keep existing selections
50893      */
50894     selectRows : function(rows, keepExisting){
50895         if(!keepExisting){
50896             this.clearSelections();
50897         }
50898         for(var i = 0, len = rows.length; i < len; i++){
50899             this.selectRow(rows[i], true);
50900         }
50901     },
50902
50903     /**
50904      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50905      * @param {Number} startRow The index of the first row in the range
50906      * @param {Number} endRow The index of the last row in the range
50907      * @param {Boolean} keepExisting (optional) True to retain existing selections
50908      */
50909     selectRange : function(startRow, endRow, keepExisting){
50910         if(this.locked) return;
50911         if(!keepExisting){
50912             this.clearSelections();
50913         }
50914         if(startRow <= endRow){
50915             for(var i = startRow; i <= endRow; i++){
50916                 this.selectRow(i, true);
50917             }
50918         }else{
50919             for(var i = startRow; i >= endRow; i--){
50920                 this.selectRow(i, true);
50921             }
50922         }
50923     },
50924
50925     /**
50926      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50927      * @param {Number} startRow The index of the first row in the range
50928      * @param {Number} endRow The index of the last row in the range
50929      */
50930     deselectRange : function(startRow, endRow, preventViewNotify){
50931         if(this.locked) return;
50932         for(var i = startRow; i <= endRow; i++){
50933             this.deselectRow(i, preventViewNotify);
50934         }
50935     },
50936
50937     /**
50938      * Selects a row.
50939      * @param {Number} row The index of the row to select
50940      * @param {Boolean} keepExisting (optional) True to keep existing selections
50941      */
50942     selectRow : function(index, keepExisting, preventViewNotify){
50943         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50944         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50945             if(!keepExisting || this.singleSelect){
50946                 this.clearSelections();
50947             }
50948             var r = this.grid.dataSource.getAt(index);
50949             this.selections.add(r);
50950             this.last = this.lastActive = index;
50951             if(!preventViewNotify){
50952                 this.grid.getView().onRowSelect(index);
50953             }
50954             this.fireEvent("rowselect", this, index, r);
50955             this.fireEvent("selectionchange", this);
50956         }
50957     },
50958
50959     /**
50960      * Deselects a row.
50961      * @param {Number} row The index of the row to deselect
50962      */
50963     deselectRow : function(index, preventViewNotify){
50964         if(this.locked) return;
50965         if(this.last == index){
50966             this.last = false;
50967         }
50968         if(this.lastActive == index){
50969             this.lastActive = false;
50970         }
50971         var r = this.grid.dataSource.getAt(index);
50972         this.selections.remove(r);
50973         if(!preventViewNotify){
50974             this.grid.getView().onRowDeselect(index);
50975         }
50976         this.fireEvent("rowdeselect", this, index);
50977         this.fireEvent("selectionchange", this);
50978     },
50979
50980     // private
50981     restoreLast : function(){
50982         if(this._last){
50983             this.last = this._last;
50984         }
50985     },
50986
50987     // private
50988     acceptsNav : function(row, col, cm){
50989         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50990     },
50991
50992     // private
50993     onEditorKey : function(field, e){
50994         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50995         if(k == e.TAB){
50996             e.stopEvent();
50997             ed.completeEdit();
50998             if(e.shiftKey){
50999                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51000             }else{
51001                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51002             }
51003         }else if(k == e.ENTER && !e.ctrlKey){
51004             e.stopEvent();
51005             ed.completeEdit();
51006             if(e.shiftKey){
51007                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51008             }else{
51009                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51010             }
51011         }else if(k == e.ESC){
51012             ed.cancelEdit();
51013         }
51014         if(newCell){
51015             g.startEditing(newCell[0], newCell[1]);
51016         }
51017     }
51018 });/*
51019  * Based on:
51020  * Ext JS Library 1.1.1
51021  * Copyright(c) 2006-2007, Ext JS, LLC.
51022  *
51023  * Originally Released Under LGPL - original licence link has changed is not relivant.
51024  *
51025  * Fork - LGPL
51026  * <script type="text/javascript">
51027  */
51028 /**
51029  * @class Roo.grid.CellSelectionModel
51030  * @extends Roo.grid.AbstractSelectionModel
51031  * This class provides the basic implementation for cell selection in a grid.
51032  * @constructor
51033  * @param {Object} config The object containing the configuration of this model.
51034  */
51035 Roo.grid.CellSelectionModel = function(config){
51036     Roo.apply(this, config);
51037
51038     this.selection = null;
51039
51040     this.addEvents({
51041         /**
51042              * @event beforerowselect
51043              * Fires before a cell is selected.
51044              * @param {SelectionModel} this
51045              * @param {Number} rowIndex The selected row index
51046              * @param {Number} colIndex The selected cell index
51047              */
51048             "beforecellselect" : true,
51049         /**
51050              * @event cellselect
51051              * Fires when a cell is selected.
51052              * @param {SelectionModel} this
51053              * @param {Number} rowIndex The selected row index
51054              * @param {Number} colIndex The selected cell index
51055              */
51056             "cellselect" : true,
51057         /**
51058              * @event selectionchange
51059              * Fires when the active selection changes.
51060              * @param {SelectionModel} this
51061              * @param {Object} selection null for no selection or an object (o) with two properties
51062                 <ul>
51063                 <li>o.record: the record object for the row the selection is in</li>
51064                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51065                 </ul>
51066              */
51067             "selectionchange" : true
51068     });
51069     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51070 };
51071
51072 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51073
51074     /** @ignore */
51075     initEvents : function(){
51076         this.grid.on("mousedown", this.handleMouseDown, this);
51077         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51078         var view = this.grid.view;
51079         view.on("refresh", this.onViewChange, this);
51080         view.on("rowupdated", this.onRowUpdated, this);
51081         view.on("beforerowremoved", this.clearSelections, this);
51082         view.on("beforerowsinserted", this.clearSelections, this);
51083         if(this.grid.isEditor){
51084             this.grid.on("beforeedit", this.beforeEdit,  this);
51085         }
51086     },
51087
51088         //private
51089     beforeEdit : function(e){
51090         this.select(e.row, e.column, false, true, e.record);
51091     },
51092
51093         //private
51094     onRowUpdated : function(v, index, r){
51095         if(this.selection && this.selection.record == r){
51096             v.onCellSelect(index, this.selection.cell[1]);
51097         }
51098     },
51099
51100         //private
51101     onViewChange : function(){
51102         this.clearSelections(true);
51103     },
51104
51105         /**
51106          * Returns the currently selected cell,.
51107          * @return {Array} The selected cell (row, column) or null if none selected.
51108          */
51109     getSelectedCell : function(){
51110         return this.selection ? this.selection.cell : null;
51111     },
51112
51113     /**
51114      * Clears all selections.
51115      * @param {Boolean} true to prevent the gridview from being notified about the change.
51116      */
51117     clearSelections : function(preventNotify){
51118         var s = this.selection;
51119         if(s){
51120             if(preventNotify !== true){
51121                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51122             }
51123             this.selection = null;
51124             this.fireEvent("selectionchange", this, null);
51125         }
51126     },
51127
51128     /**
51129      * Returns true if there is a selection.
51130      * @return {Boolean}
51131      */
51132     hasSelection : function(){
51133         return this.selection ? true : false;
51134     },
51135
51136     /** @ignore */
51137     handleMouseDown : function(e, t){
51138         var v = this.grid.getView();
51139         if(this.isLocked()){
51140             return;
51141         };
51142         var row = v.findRowIndex(t);
51143         var cell = v.findCellIndex(t);
51144         if(row !== false && cell !== false){
51145             this.select(row, cell);
51146         }
51147     },
51148
51149     /**
51150      * Selects a cell.
51151      * @param {Number} rowIndex
51152      * @param {Number} collIndex
51153      */
51154     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51155         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51156             this.clearSelections();
51157             r = r || this.grid.dataSource.getAt(rowIndex);
51158             this.selection = {
51159                 record : r,
51160                 cell : [rowIndex, colIndex]
51161             };
51162             if(!preventViewNotify){
51163                 var v = this.grid.getView();
51164                 v.onCellSelect(rowIndex, colIndex);
51165                 if(preventFocus !== true){
51166                     v.focusCell(rowIndex, colIndex);
51167                 }
51168             }
51169             this.fireEvent("cellselect", this, rowIndex, colIndex);
51170             this.fireEvent("selectionchange", this, this.selection);
51171         }
51172     },
51173
51174         //private
51175     isSelectable : function(rowIndex, colIndex, cm){
51176         return !cm.isHidden(colIndex);
51177     },
51178
51179     /** @ignore */
51180     handleKeyDown : function(e){
51181         //Roo.log('Cell Sel Model handleKeyDown');
51182         if(!e.isNavKeyPress()){
51183             return;
51184         }
51185         var g = this.grid, s = this.selection;
51186         if(!s){
51187             e.stopEvent();
51188             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51189             if(cell){
51190                 this.select(cell[0], cell[1]);
51191             }
51192             return;
51193         }
51194         var sm = this;
51195         var walk = function(row, col, step){
51196             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51197         };
51198         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51199         var newCell;
51200
51201         switch(k){
51202             case e.TAB:
51203                 // handled by onEditorKey
51204                 if (g.isEditor && g.editing) {
51205                     return;
51206                 }
51207                 if(e.shiftKey){
51208                      newCell = walk(r, c-1, -1);
51209                 }else{
51210                      newCell = walk(r, c+1, 1);
51211                 }
51212              break;
51213              case e.DOWN:
51214                  newCell = walk(r+1, c, 1);
51215              break;
51216              case e.UP:
51217                  newCell = walk(r-1, c, -1);
51218              break;
51219              case e.RIGHT:
51220                  newCell = walk(r, c+1, 1);
51221              break;
51222              case e.LEFT:
51223                  newCell = walk(r, c-1, -1);
51224              break;
51225              case e.ENTER:
51226                  if(g.isEditor && !g.editing){
51227                     g.startEditing(r, c);
51228                     e.stopEvent();
51229                     return;
51230                 }
51231              break;
51232         };
51233         if(newCell){
51234             this.select(newCell[0], newCell[1]);
51235             e.stopEvent();
51236         }
51237     },
51238
51239     acceptsNav : function(row, col, cm){
51240         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51241     },
51242     /**
51243      * Selects a cell.
51244      * @param {Number} field (not used) - as it's normally used as a listener
51245      * @param {Number} e - event - fake it by using
51246      *
51247      * var e = Roo.EventObjectImpl.prototype;
51248      * e.keyCode = e.TAB
51249      *
51250      * 
51251      */
51252     onEditorKey : function(field, e){
51253         
51254         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51255         ///Roo.log('onEditorKey' + k);
51256         if (!ed) {
51257             
51258             
51259             
51260         }
51261         if(k == e.TAB){
51262             if(e.shiftKey){
51263                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51264             }else{
51265                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51266             }
51267             
51268             e.stopEvent();
51269             
51270         }else if(k == e.ENTER &&  !e.ctrlKey){
51271             ed.completeEdit();
51272             e.stopEvent();
51273             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51274         }else if(k == e.ESC){
51275             ed.cancelEdit();
51276         }
51277         
51278         
51279         if(newCell){
51280             //Roo.log('next cell after edit');
51281             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51282         }
51283     }
51284 });/*
51285  * Based on:
51286  * Ext JS Library 1.1.1
51287  * Copyright(c) 2006-2007, Ext JS, LLC.
51288  *
51289  * Originally Released Under LGPL - original licence link has changed is not relivant.
51290  *
51291  * Fork - LGPL
51292  * <script type="text/javascript">
51293  */
51294  
51295 /**
51296  * @class Roo.grid.EditorGrid
51297  * @extends Roo.grid.Grid
51298  * Class for creating and editable grid.
51299  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51300  * The container MUST have some type of size defined for the grid to fill. The container will be 
51301  * automatically set to position relative if it isn't already.
51302  * @param {Object} dataSource The data model to bind to
51303  * @param {Object} colModel The column model with info about this grid's columns
51304  */
51305 Roo.grid.EditorGrid = function(container, config){
51306     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51307     this.getGridEl().addClass("xedit-grid");
51308
51309     if(!this.selModel){
51310         this.selModel = new Roo.grid.CellSelectionModel();
51311     }
51312
51313     this.activeEditor = null;
51314
51315         this.addEvents({
51316             /**
51317              * @event beforeedit
51318              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51319              * <ul style="padding:5px;padding-left:16px;">
51320              * <li>grid - This grid</li>
51321              * <li>record - The record being edited</li>
51322              * <li>field - The field name being edited</li>
51323              * <li>value - The value for the field being edited.</li>
51324              * <li>row - The grid row index</li>
51325              * <li>column - The grid column index</li>
51326              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51327              * </ul>
51328              * @param {Object} e An edit event (see above for description)
51329              */
51330             "beforeedit" : true,
51331             /**
51332              * @event afteredit
51333              * Fires after a cell is edited. <br />
51334              * <ul style="padding:5px;padding-left:16px;">
51335              * <li>grid - This grid</li>
51336              * <li>record - The record being edited</li>
51337              * <li>field - The field name being edited</li>
51338              * <li>value - The value being set</li>
51339              * <li>originalValue - The original value for the field, before the edit.</li>
51340              * <li>row - The grid row index</li>
51341              * <li>column - The grid column index</li>
51342              * </ul>
51343              * @param {Object} e An edit event (see above for description)
51344              */
51345             "afteredit" : true,
51346             /**
51347              * @event validateedit
51348              * Fires after a cell is edited, but before the value is set in the record. 
51349          * You can use this to modify the value being set in the field, Return false
51350              * to cancel the change. The edit event object has the following properties <br />
51351              * <ul style="padding:5px;padding-left:16px;">
51352          * <li>editor - This editor</li>
51353              * <li>grid - This grid</li>
51354              * <li>record - The record being edited</li>
51355              * <li>field - The field name being edited</li>
51356              * <li>value - The value being set</li>
51357              * <li>originalValue - The original value for the field, before the edit.</li>
51358              * <li>row - The grid row index</li>
51359              * <li>column - The grid column index</li>
51360              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51361              * </ul>
51362              * @param {Object} e An edit event (see above for description)
51363              */
51364             "validateedit" : true
51365         });
51366     this.on("bodyscroll", this.stopEditing,  this);
51367     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51368 };
51369
51370 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51371     /**
51372      * @cfg {Number} clicksToEdit
51373      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51374      */
51375     clicksToEdit: 2,
51376
51377     // private
51378     isEditor : true,
51379     // private
51380     trackMouseOver: false, // causes very odd FF errors
51381
51382     onCellDblClick : function(g, row, col){
51383         this.startEditing(row, col);
51384     },
51385
51386     onEditComplete : function(ed, value, startValue){
51387         this.editing = false;
51388         this.activeEditor = null;
51389         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51390         var r = ed.record;
51391         var field = this.colModel.getDataIndex(ed.col);
51392         var e = {
51393             grid: this,
51394             record: r,
51395             field: field,
51396             originalValue: startValue,
51397             value: value,
51398             row: ed.row,
51399             column: ed.col,
51400             cancel:false,
51401             editor: ed
51402         };
51403         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51404         cell.show();
51405           
51406         if(String(value) !== String(startValue)){
51407             
51408             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51409                 r.set(field, e.value);
51410                 // if we are dealing with a combo box..
51411                 // then we also set the 'name' colum to be the displayField
51412                 if (ed.field.displayField && ed.field.name) {
51413                     r.set(ed.field.name, ed.field.el.dom.value);
51414                 }
51415                 
51416                 delete e.cancel; //?? why!!!
51417                 this.fireEvent("afteredit", e);
51418             }
51419         } else {
51420             this.fireEvent("afteredit", e); // always fire it!
51421         }
51422         this.view.focusCell(ed.row, ed.col);
51423     },
51424
51425     /**
51426      * Starts editing the specified for the specified row/column
51427      * @param {Number} rowIndex
51428      * @param {Number} colIndex
51429      */
51430     startEditing : function(row, col){
51431         this.stopEditing();
51432         if(this.colModel.isCellEditable(col, row)){
51433             this.view.ensureVisible(row, col, true);
51434           
51435             var r = this.dataSource.getAt(row);
51436             var field = this.colModel.getDataIndex(col);
51437             var cell = Roo.get(this.view.getCell(row,col));
51438             var e = {
51439                 grid: this,
51440                 record: r,
51441                 field: field,
51442                 value: r.data[field],
51443                 row: row,
51444                 column: col,
51445                 cancel:false 
51446             };
51447             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51448                 this.editing = true;
51449                 var ed = this.colModel.getCellEditor(col, row);
51450                 
51451                 if (!ed) {
51452                     return;
51453                 }
51454                 if(!ed.rendered){
51455                     ed.render(ed.parentEl || document.body);
51456                 }
51457                 ed.field.reset();
51458                
51459                 cell.hide();
51460                 
51461                 (function(){ // complex but required for focus issues in safari, ie and opera
51462                     ed.row = row;
51463                     ed.col = col;
51464                     ed.record = r;
51465                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51466                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51467                     this.activeEditor = ed;
51468                     var v = r.data[field];
51469                     ed.startEdit(this.view.getCell(row, col), v);
51470                     // combo's with 'displayField and name set
51471                     if (ed.field.displayField && ed.field.name) {
51472                         ed.field.el.dom.value = r.data[ed.field.name];
51473                     }
51474                     
51475                     
51476                 }).defer(50, this);
51477             }
51478         }
51479     },
51480         
51481     /**
51482      * Stops any active editing
51483      */
51484     stopEditing : function(){
51485         if(this.activeEditor){
51486             this.activeEditor.completeEdit();
51487         }
51488         this.activeEditor = null;
51489     }
51490 });/*
51491  * Based on:
51492  * Ext JS Library 1.1.1
51493  * Copyright(c) 2006-2007, Ext JS, LLC.
51494  *
51495  * Originally Released Under LGPL - original licence link has changed is not relivant.
51496  *
51497  * Fork - LGPL
51498  * <script type="text/javascript">
51499  */
51500
51501 // private - not really -- you end up using it !
51502 // This is a support class used internally by the Grid components
51503
51504 /**
51505  * @class Roo.grid.GridEditor
51506  * @extends Roo.Editor
51507  * Class for creating and editable grid elements.
51508  * @param {Object} config any settings (must include field)
51509  */
51510 Roo.grid.GridEditor = function(field, config){
51511     if (!config && field.field) {
51512         config = field;
51513         field = Roo.factory(config.field, Roo.form);
51514     }
51515     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51516     field.monitorTab = false;
51517 };
51518
51519 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51520     
51521     /**
51522      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51523      */
51524     
51525     alignment: "tl-tl",
51526     autoSize: "width",
51527     hideEl : false,
51528     cls: "x-small-editor x-grid-editor",
51529     shim:false,
51530     shadow:"frame"
51531 });/*
51532  * Based on:
51533  * Ext JS Library 1.1.1
51534  * Copyright(c) 2006-2007, Ext JS, LLC.
51535  *
51536  * Originally Released Under LGPL - original licence link has changed is not relivant.
51537  *
51538  * Fork - LGPL
51539  * <script type="text/javascript">
51540  */
51541   
51542
51543   
51544 Roo.grid.PropertyRecord = Roo.data.Record.create([
51545     {name:'name',type:'string'},  'value'
51546 ]);
51547
51548
51549 Roo.grid.PropertyStore = function(grid, source){
51550     this.grid = grid;
51551     this.store = new Roo.data.Store({
51552         recordType : Roo.grid.PropertyRecord
51553     });
51554     this.store.on('update', this.onUpdate,  this);
51555     if(source){
51556         this.setSource(source);
51557     }
51558     Roo.grid.PropertyStore.superclass.constructor.call(this);
51559 };
51560
51561
51562
51563 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51564     setSource : function(o){
51565         this.source = o;
51566         this.store.removeAll();
51567         var data = [];
51568         for(var k in o){
51569             if(this.isEditableValue(o[k])){
51570                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51571             }
51572         }
51573         this.store.loadRecords({records: data}, {}, true);
51574     },
51575
51576     onUpdate : function(ds, record, type){
51577         if(type == Roo.data.Record.EDIT){
51578             var v = record.data['value'];
51579             var oldValue = record.modified['value'];
51580             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51581                 this.source[record.id] = v;
51582                 record.commit();
51583                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51584             }else{
51585                 record.reject();
51586             }
51587         }
51588     },
51589
51590     getProperty : function(row){
51591        return this.store.getAt(row);
51592     },
51593
51594     isEditableValue: function(val){
51595         if(val && val instanceof Date){
51596             return true;
51597         }else if(typeof val == 'object' || typeof val == 'function'){
51598             return false;
51599         }
51600         return true;
51601     },
51602
51603     setValue : function(prop, value){
51604         this.source[prop] = value;
51605         this.store.getById(prop).set('value', value);
51606     },
51607
51608     getSource : function(){
51609         return this.source;
51610     }
51611 });
51612
51613 Roo.grid.PropertyColumnModel = function(grid, store){
51614     this.grid = grid;
51615     var g = Roo.grid;
51616     g.PropertyColumnModel.superclass.constructor.call(this, [
51617         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51618         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51619     ]);
51620     this.store = store;
51621     this.bselect = Roo.DomHelper.append(document.body, {
51622         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51623             {tag: 'option', value: 'true', html: 'true'},
51624             {tag: 'option', value: 'false', html: 'false'}
51625         ]
51626     });
51627     Roo.id(this.bselect);
51628     var f = Roo.form;
51629     this.editors = {
51630         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51631         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51632         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51633         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51634         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51635     };
51636     this.renderCellDelegate = this.renderCell.createDelegate(this);
51637     this.renderPropDelegate = this.renderProp.createDelegate(this);
51638 };
51639
51640 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51641     
51642     
51643     nameText : 'Name',
51644     valueText : 'Value',
51645     
51646     dateFormat : 'm/j/Y',
51647     
51648     
51649     renderDate : function(dateVal){
51650         return dateVal.dateFormat(this.dateFormat);
51651     },
51652
51653     renderBool : function(bVal){
51654         return bVal ? 'true' : 'false';
51655     },
51656
51657     isCellEditable : function(colIndex, rowIndex){
51658         return colIndex == 1;
51659     },
51660
51661     getRenderer : function(col){
51662         return col == 1 ?
51663             this.renderCellDelegate : this.renderPropDelegate;
51664     },
51665
51666     renderProp : function(v){
51667         return this.getPropertyName(v);
51668     },
51669
51670     renderCell : function(val){
51671         var rv = val;
51672         if(val instanceof Date){
51673             rv = this.renderDate(val);
51674         }else if(typeof val == 'boolean'){
51675             rv = this.renderBool(val);
51676         }
51677         return Roo.util.Format.htmlEncode(rv);
51678     },
51679
51680     getPropertyName : function(name){
51681         var pn = this.grid.propertyNames;
51682         return pn && pn[name] ? pn[name] : name;
51683     },
51684
51685     getCellEditor : function(colIndex, rowIndex){
51686         var p = this.store.getProperty(rowIndex);
51687         var n = p.data['name'], val = p.data['value'];
51688         
51689         if(typeof(this.grid.customEditors[n]) == 'string'){
51690             return this.editors[this.grid.customEditors[n]];
51691         }
51692         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51693             return this.grid.customEditors[n];
51694         }
51695         if(val instanceof Date){
51696             return this.editors['date'];
51697         }else if(typeof val == 'number'){
51698             return this.editors['number'];
51699         }else if(typeof val == 'boolean'){
51700             return this.editors['boolean'];
51701         }else{
51702             return this.editors['string'];
51703         }
51704     }
51705 });
51706
51707 /**
51708  * @class Roo.grid.PropertyGrid
51709  * @extends Roo.grid.EditorGrid
51710  * This class represents the  interface of a component based property grid control.
51711  * <br><br>Usage:<pre><code>
51712  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51713       
51714  });
51715  // set any options
51716  grid.render();
51717  * </code></pre>
51718   
51719  * @constructor
51720  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51721  * The container MUST have some type of size defined for the grid to fill. The container will be
51722  * automatically set to position relative if it isn't already.
51723  * @param {Object} config A config object that sets properties on this grid.
51724  */
51725 Roo.grid.PropertyGrid = function(container, config){
51726     config = config || {};
51727     var store = new Roo.grid.PropertyStore(this);
51728     this.store = store;
51729     var cm = new Roo.grid.PropertyColumnModel(this, store);
51730     store.store.sort('name', 'ASC');
51731     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51732         ds: store.store,
51733         cm: cm,
51734         enableColLock:false,
51735         enableColumnMove:false,
51736         stripeRows:false,
51737         trackMouseOver: false,
51738         clicksToEdit:1
51739     }, config));
51740     this.getGridEl().addClass('x-props-grid');
51741     this.lastEditRow = null;
51742     this.on('columnresize', this.onColumnResize, this);
51743     this.addEvents({
51744          /**
51745              * @event beforepropertychange
51746              * Fires before a property changes (return false to stop?)
51747              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51748              * @param {String} id Record Id
51749              * @param {String} newval New Value
51750          * @param {String} oldval Old Value
51751              */
51752         "beforepropertychange": true,
51753         /**
51754              * @event propertychange
51755              * Fires after a property changes
51756              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51757              * @param {String} id Record Id
51758              * @param {String} newval New Value
51759          * @param {String} oldval Old Value
51760              */
51761         "propertychange": true
51762     });
51763     this.customEditors = this.customEditors || {};
51764 };
51765 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51766     
51767      /**
51768      * @cfg {Object} customEditors map of colnames=> custom editors.
51769      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51770      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51771      * false disables editing of the field.
51772          */
51773     
51774       /**
51775      * @cfg {Object} propertyNames map of property Names to their displayed value
51776          */
51777     
51778     render : function(){
51779         Roo.grid.PropertyGrid.superclass.render.call(this);
51780         this.autoSize.defer(100, this);
51781     },
51782
51783     autoSize : function(){
51784         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51785         if(this.view){
51786             this.view.fitColumns();
51787         }
51788     },
51789
51790     onColumnResize : function(){
51791         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51792         this.autoSize();
51793     },
51794     /**
51795      * Sets the data for the Grid
51796      * accepts a Key => Value object of all the elements avaiable.
51797      * @param {Object} data  to appear in grid.
51798      */
51799     setSource : function(source){
51800         this.store.setSource(source);
51801         //this.autoSize();
51802     },
51803     /**
51804      * Gets all the data from the grid.
51805      * @return {Object} data  data stored in grid
51806      */
51807     getSource : function(){
51808         return this.store.getSource();
51809     }
51810 });/*
51811  * Based on:
51812  * Ext JS Library 1.1.1
51813  * Copyright(c) 2006-2007, Ext JS, LLC.
51814  *
51815  * Originally Released Under LGPL - original licence link has changed is not relivant.
51816  *
51817  * Fork - LGPL
51818  * <script type="text/javascript">
51819  */
51820  
51821 /**
51822  * @class Roo.LoadMask
51823  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51824  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51825  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51826  * element's UpdateManager load indicator and will be destroyed after the initial load.
51827  * @constructor
51828  * Create a new LoadMask
51829  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51830  * @param {Object} config The config object
51831  */
51832 Roo.LoadMask = function(el, config){
51833     this.el = Roo.get(el);
51834     Roo.apply(this, config);
51835     if(this.store){
51836         this.store.on('beforeload', this.onBeforeLoad, this);
51837         this.store.on('load', this.onLoad, this);
51838         this.store.on('loadexception', this.onLoad, this);
51839         this.removeMask = false;
51840     }else{
51841         var um = this.el.getUpdateManager();
51842         um.showLoadIndicator = false; // disable the default indicator
51843         um.on('beforeupdate', this.onBeforeLoad, this);
51844         um.on('update', this.onLoad, this);
51845         um.on('failure', this.onLoad, this);
51846         this.removeMask = true;
51847     }
51848 };
51849
51850 Roo.LoadMask.prototype = {
51851     /**
51852      * @cfg {Boolean} removeMask
51853      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51854      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51855      */
51856     /**
51857      * @cfg {String} msg
51858      * The text to display in a centered loading message box (defaults to 'Loading...')
51859      */
51860     msg : 'Loading...',
51861     /**
51862      * @cfg {String} msgCls
51863      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51864      */
51865     msgCls : 'x-mask-loading',
51866
51867     /**
51868      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51869      * @type Boolean
51870      */
51871     disabled: false,
51872
51873     /**
51874      * Disables the mask to prevent it from being displayed
51875      */
51876     disable : function(){
51877        this.disabled = true;
51878     },
51879
51880     /**
51881      * Enables the mask so that it can be displayed
51882      */
51883     enable : function(){
51884         this.disabled = false;
51885     },
51886
51887     // private
51888     onLoad : function(){
51889         this.el.unmask(this.removeMask);
51890     },
51891
51892     // private
51893     onBeforeLoad : function(){
51894         if(!this.disabled){
51895             this.el.mask(this.msg, this.msgCls);
51896         }
51897     },
51898
51899     // private
51900     destroy : function(){
51901         if(this.store){
51902             this.store.un('beforeload', this.onBeforeLoad, this);
51903             this.store.un('load', this.onLoad, this);
51904             this.store.un('loadexception', this.onLoad, this);
51905         }else{
51906             var um = this.el.getUpdateManager();
51907             um.un('beforeupdate', this.onBeforeLoad, this);
51908             um.un('update', this.onLoad, this);
51909             um.un('failure', this.onLoad, this);
51910         }
51911     }
51912 };/*
51913  * Based on:
51914  * Ext JS Library 1.1.1
51915  * Copyright(c) 2006-2007, Ext JS, LLC.
51916  *
51917  * Originally Released Under LGPL - original licence link has changed is not relivant.
51918  *
51919  * Fork - LGPL
51920  * <script type="text/javascript">
51921  */
51922 Roo.XTemplate = function(){
51923     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51924     var s = this.html;
51925
51926     s = ['<tpl>', s, '</tpl>'].join('');
51927
51928     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51929
51930     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51931     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51932     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51933     var m, id = 0;
51934     var tpls = [];
51935
51936     while(m = s.match(re)){
51937        var m2 = m[0].match(nameRe);
51938        var m3 = m[0].match(ifRe);
51939        var m4 = m[0].match(execRe);
51940        var exp = null, fn = null, exec = null;
51941        var name = m2 && m2[1] ? m2[1] : '';
51942        if(m3){
51943            exp = m3 && m3[1] ? m3[1] : null;
51944            if(exp){
51945                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51946            }
51947        }
51948        if(m4){
51949            exp = m4 && m4[1] ? m4[1] : null;
51950            if(exp){
51951                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51952            }
51953        }
51954        if(name){
51955            switch(name){
51956                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51957                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51958                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51959            }
51960        }
51961        tpls.push({
51962             id: id,
51963             target: name,
51964             exec: exec,
51965             test: fn,
51966             body: m[1]||''
51967         });
51968        s = s.replace(m[0], '{xtpl'+ id + '}');
51969        ++id;
51970     }
51971     for(var i = tpls.length-1; i >= 0; --i){
51972         this.compileTpl(tpls[i]);
51973     }
51974     this.master = tpls[tpls.length-1];
51975     this.tpls = tpls;
51976 };
51977 Roo.extend(Roo.XTemplate, Roo.Template, {
51978
51979     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51980
51981     applySubTemplate : function(id, values, parent){
51982         var t = this.tpls[id];
51983         if(t.test && !t.test.call(this, values, parent)){
51984             return '';
51985         }
51986         if(t.exec && t.exec.call(this, values, parent)){
51987             return '';
51988         }
51989         var vs = t.target ? t.target.call(this, values, parent) : values;
51990         parent = t.target ? values : parent;
51991         if(t.target && vs instanceof Array){
51992             var buf = [];
51993             for(var i = 0, len = vs.length; i < len; i++){
51994                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51995             }
51996             return buf.join('');
51997         }
51998         return t.compiled.call(this, vs, parent);
51999     },
52000
52001     compileTpl : function(tpl){
52002         var fm = Roo.util.Format;
52003         var useF = this.disableFormats !== true;
52004         var sep = Roo.isGecko ? "+" : ",";
52005         var fn = function(m, name, format, args){
52006             if(name.substr(0, 4) == 'xtpl'){
52007                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52008             }
52009             var v;
52010             if(name.indexOf('.') != -1){
52011                 v = name;
52012             }else{
52013                 v = "values['" + name + "']";
52014             }
52015             if(format && useF){
52016                 args = args ? ',' + args : "";
52017                 if(format.substr(0, 5) != "this."){
52018                     format = "fm." + format + '(';
52019                 }else{
52020                     format = 'this.call("'+ format.substr(5) + '", ';
52021                     args = ", values";
52022                 }
52023             }else{
52024                 args= ''; format = "("+v+" === undefined ? '' : ";
52025             }
52026             return "'"+ sep + format + v + args + ")"+sep+"'";
52027         };
52028         var body;
52029         // branched to use + in gecko and [].join() in others
52030         if(Roo.isGecko){
52031             body = "tpl.compiled = function(values, parent){ return '" +
52032                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52033                     "';};";
52034         }else{
52035             body = ["tpl.compiled = function(values, parent){ return ['"];
52036             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52037             body.push("'].join('');};");
52038             body = body.join('');
52039         }
52040         /** eval:var:zzzzzzz */
52041         eval(body);
52042         return this;
52043     },
52044
52045     applyTemplate : function(values){
52046         return this.master.compiled.call(this, values, {});
52047         var s = this.subs;
52048     },
52049
52050     apply : function(){
52051         return this.applyTemplate.apply(this, arguments);
52052     },
52053
52054     compile : function(){return this;}
52055 });
52056
52057 Roo.XTemplate.from = function(el){
52058     el = Roo.getDom(el);
52059     return new Roo.XTemplate(el.value || el.innerHTML);
52060 };/*
52061  * Original code for Roojs - LGPL
52062  * <script type="text/javascript">
52063  */
52064  
52065 /**
52066  * @class Roo.XComponent
52067  * A delayed Element creator...
52068  * Or a way to group chunks of interface together.
52069  * 
52070  * Mypart.xyx = new Roo.XComponent({
52071
52072     parent : 'Mypart.xyz', // empty == document.element.!!
52073     order : '001',
52074     name : 'xxxx'
52075     region : 'xxxx'
52076     disabled : function() {} 
52077      
52078     tree : function() { // return an tree of xtype declared components
52079         var MODULE = this;
52080         return 
52081         {
52082             xtype : 'NestedLayoutPanel',
52083             // technicall
52084         }
52085      ]
52086  *})
52087  *
52088  *
52089  * It can be used to build a big heiracy, with parent etc.
52090  * or you can just use this to render a single compoent to a dom element
52091  * MYPART.render(Roo.Element | String(id) | dom_element )
52092  * 
52093  * @extends Roo.util.Observable
52094  * @constructor
52095  * @param cfg {Object} configuration of component
52096  * 
52097  */
52098 Roo.XComponent = function(cfg) {
52099     Roo.apply(this, cfg);
52100     this.addEvents({ 
52101         /**
52102              * @event built
52103              * Fires when this the componnt is built
52104              * @param {Roo.XComponent} c the component
52105              */
52106         'built' : true,
52107         /**
52108              * @event buildcomplete
52109              * Fires on the top level element when all elements have been built
52110              * @param {Roo.XComponent} c the top level component.
52111          */
52112         'buildcomplete' : true
52113         
52114     });
52115     this.region = this.region || 'center'; // default..
52116     Roo.XComponent.register(this);
52117     this.modules = false;
52118     this.el = false; // where the layout goes..
52119     
52120     
52121 }
52122 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52123     /**
52124      * @property el
52125      * The created element (with Roo.factory())
52126      * @type {Roo.Layout}
52127      */
52128     el  : false,
52129     
52130     /**
52131      * @property el
52132      * for BC  - use el in new code
52133      * @type {Roo.Layout}
52134      */
52135     panel : false,
52136     
52137     /**
52138      * @property layout
52139      * for BC  - use el in new code
52140      * @type {Roo.Layout}
52141      */
52142     layout : false,
52143     
52144      /**
52145      * @cfg {Function|boolean} disabled
52146      * If this module is disabled by some rule, return true from the funtion
52147      */
52148     disabled : false,
52149     
52150     /**
52151      * @cfg {String} parent 
52152      * Name of parent element which it get xtype added to..
52153      */
52154     parent: false,
52155     
52156     /**
52157      * @cfg {String} order
52158      * Used to set the order in which elements are created (usefull for multiple tabs)
52159      */
52160     
52161     order : false,
52162     /**
52163      * @cfg {String} name
52164      * String to display while loading.
52165      */
52166     name : false,
52167     /**
52168      * @cfg {String} region
52169      * Region to render component to (defaults to center)
52170      */
52171     region : 'center',
52172     
52173     /**
52174      * @cfg {Array} items
52175      * A single item array - the first element is the root of the tree..
52176      * It's done this way to stay compatible with the Xtype system...
52177      */
52178     items : false,
52179     
52180     
52181      /**
52182      * render
52183      * render element to dom or tree
52184      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52185      */
52186     
52187     render : function(el)
52188     {
52189         
52190         el = el || false;
52191         var hp = this.parent ? 1 : 0;
52192         
52193         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52194             // if parent is a '#.....' string, then let's use that..
52195             var ename = this.parent.substr(1)
52196             this.parent = false;
52197             el = Roo.get(ename);
52198             if (!el) {
52199                 Roo.log("Warning - element can not be found :#" + ename );
52200                 return;
52201             }
52202         }
52203         
52204         
52205         if (!this.parent) {
52206             
52207             el = el ? Roo.get(el) : false;
52208             
52209             // it's a top level one..
52210             this.parent =  {
52211                 el : new Roo.BorderLayout(el || document.body, {
52212                 
52213                      center: {
52214                          titlebar: false,
52215                          autoScroll:false,
52216                          closeOnTab: true,
52217                          tabPosition: 'top',
52218                           //resizeTabs: true,
52219                          alwaysShowTabs: el && hp? false :  true,
52220                          hideTabs: el || !hp ? true :  false,
52221                          minTabWidth: 140
52222                      }
52223                  })
52224             }
52225         }
52226         
52227         
52228             
52229         var tree = this.tree();
52230         tree.region = tree.region || this.region;
52231         this.el = this.parent.el.addxtype(tree);
52232         this.fireEvent('built', this);
52233         
52234         this.panel = this.el;
52235         this.layout = this.panel.layout;    
52236          
52237     }
52238     
52239 });
52240
52241 Roo.apply(Roo.XComponent, {
52242     
52243     /**
52244      * @property  buildCompleted
52245      * True when the builder has completed building the interface.
52246      * @type Boolean
52247      */
52248     buildCompleted : false,
52249      
52250     /**
52251      * @property  topModule
52252      * the upper most module - uses document.element as it's constructor.
52253      * @type Object
52254      */
52255      
52256     topModule  : false,
52257       
52258     /**
52259      * @property  modules
52260      * array of modules to be created by registration system.
52261      * @type {Array} of Roo.XComponent
52262      */
52263     
52264     modules : [],
52265     /**
52266      * @property  elmodules
52267      * array of modules to be created by which use #ID 
52268      * @type {Array} of Roo.XComponent
52269      */
52270      
52271     elmodules : [],
52272
52273     
52274     /**
52275      * Register components to be built later.
52276      *
52277      * This solves the following issues
52278      * - Building is not done on page load, but after an authentication process has occured.
52279      * - Interface elements are registered on page load
52280      * - Parent Interface elements may not be loaded before child, so this handles that..
52281      * 
52282      *
52283      * example:
52284      * 
52285      * MyApp.register({
52286           order : '000001',
52287           module : 'Pman.Tab.projectMgr',
52288           region : 'center',
52289           parent : 'Pman.layout',
52290           disabled : false,  // or use a function..
52291         })
52292      
52293      * * @param {Object} details about module
52294      */
52295     register : function(obj) {
52296         this.modules.push(obj);
52297          
52298     },
52299     /**
52300      * convert a string to an object..
52301      * eg. 'AAA.BBB' -> finds AAA.BBB
52302
52303      */
52304     
52305     toObject : function(str)
52306     {
52307         if (!str || typeof(str) == 'object') {
52308             return str;
52309         }
52310         if (str.substring(0,1) == '#') {
52311             return str;
52312         }
52313
52314         var ar = str.split('.');
52315         var rt, o;
52316         rt = ar.shift();
52317             /** eval:var:o */
52318         try {
52319             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52320         } catch (e) {
52321             throw "Module not found : " + str;
52322         }
52323         
52324         if (o === false) {
52325             throw "Module not found : " + str;
52326         }
52327         Roo.each(ar, function(e) {
52328             if (typeof(o[e]) == 'undefined') {
52329                 throw "Module not found : " + str;
52330             }
52331             o = o[e];
52332         });
52333         
52334         return o;
52335         
52336     },
52337     
52338     
52339     /**
52340      * move modules into their correct place in the tree..
52341      * 
52342      */
52343     preBuild : function ()
52344     {
52345         var _t = this;
52346         Roo.each(this.modules , function (obj)
52347         {
52348             var opar = obj.parent;
52349             try { 
52350                 obj.parent = this.toObject(opar);
52351             } catch(e) {
52352                 Roo.log(e.toString());
52353                 return;
52354             }
52355             
52356             if (!obj.parent) {
52357                 this.topModule = obj;
52358                 return;
52359             }
52360             if (typeof(obj.parent) == 'string') {
52361                 this.elmodules.push(obj);
52362                 return;
52363             }
52364             if (obj.parent.constructor != Roo.XComponent) {
52365                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52366             }
52367             if (!obj.parent.modules) {
52368                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52369                     function(o) { return o.order + '' }
52370                 );
52371             }
52372             
52373             obj.parent.modules.add(obj);
52374         }, this);
52375     },
52376     
52377      /**
52378      * make a list of modules to build.
52379      * @return {Array} list of modules. 
52380      */ 
52381     
52382     buildOrder : function()
52383     {
52384         var _this = this;
52385         var cmp = function(a,b) {   
52386             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52387         };
52388         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52389             throw "No top level modules to build";
52390         }
52391         
52392         // make a flat list in order of modules to build.
52393         var mods = this.topModule ? [ this.topModule ] : [];
52394         Roo.each(this.elmodules,function(e) { mods.push(e) });
52395
52396         
52397         // add modules to their parents..
52398         var addMod = function(m) {
52399            // Roo.debug && Roo.log(m.modKey);
52400             
52401             mods.push(m);
52402             if (m.modules) {
52403                 m.modules.keySort('ASC',  cmp );
52404                 m.modules.each(addMod);
52405             }
52406             // not sure if this is used any more..
52407             if (m.finalize) {
52408                 m.finalize.name = m.name + " (clean up) ";
52409                 mods.push(m.finalize);
52410             }
52411             
52412         }
52413         if (this.topModule) { 
52414             this.topModule.modules.keySort('ASC',  cmp );
52415             this.topModule.modules.each(addMod);
52416         }
52417         return mods;
52418     },
52419     
52420      /**
52421      * Build the registered modules.
52422      * @param {Object} parent element.
52423      * @param {Function} optional method to call after module has been added.
52424      * 
52425      */ 
52426    
52427     build : function() 
52428     {
52429         
52430         this.preBuild();
52431         var mods = this.buildOrder();
52432       
52433         //this.allmods = mods;
52434         //Roo.debug && Roo.log(mods);
52435         //return;
52436         if (!mods.length) { // should not happen
52437             throw "NO modules!!!";
52438         }
52439         
52440         
52441         
52442         // flash it up as modal - so we store the mask!?
52443         Roo.MessageBox.show({ title: 'loading' });
52444         Roo.MessageBox.show({
52445            title: "Please wait...",
52446            msg: "Building Interface...",
52447            width:450,
52448            progress:true,
52449            closable:false,
52450            modal: false
52451           
52452         });
52453         var total = mods.length;
52454         
52455         var _this = this;
52456         var progressRun = function() {
52457             if (!mods.length) {
52458                 Roo.debug && Roo.log('hide?');
52459                 Roo.MessageBox.hide();
52460                 if (_this.topModule) { 
52461                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52462                 }
52463                 // THE END...
52464                 return false;   
52465             }
52466             
52467             var m = mods.shift();
52468             
52469             
52470             Roo.debug && Roo.log(m);
52471             // not sure if this is supported any more.. - modules that are are just function
52472             if (typeof(m) == 'function') { 
52473                 m.call(this);
52474                 return progressRun.defer(10, _this);
52475             } 
52476             
52477             
52478             
52479             Roo.MessageBox.updateProgress(
52480                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52481                     " of " + total + 
52482                     (m.name ? (' - ' + m.name) : '')
52483                     );
52484             
52485          
52486             // is the module disabled?
52487             var disabled = (typeof(m.disabled) == 'function') ?
52488                 m.disabled.call(m.module.disabled) : m.disabled;    
52489             
52490             
52491             if (disabled) {
52492                 return progressRun(); // we do not update the display!
52493             }
52494             
52495             // now build 
52496             
52497             m.render();
52498             // it's 10 on top level, and 1 on others??? why...
52499             return progressRun.defer(10, _this);
52500              
52501         }
52502         progressRun.defer(1, _this);
52503      
52504         
52505         
52506     }
52507     
52508      
52509    
52510     
52511     
52512 });
52513  //<script type="text/javascript">
52514
52515
52516 /**
52517  * @class Roo.Login
52518  * @extends Roo.LayoutDialog
52519  * A generic Login Dialog..... - only one needed in theory!?!?
52520  *
52521  * Fires XComponent builder on success...
52522  * 
52523  * Sends 
52524  *    username,password, lang = for login actions.
52525  *    check = 1 for periodic checking that sesion is valid.
52526  *    passwordRequest = email request password
52527  *    logout = 1 = to logout
52528  * 
52529  * Affects: (this id="????" elements)
52530  *   loading  (removed) (used to indicate application is loading)
52531  *   loading-mask (hides) (used to hide application when it's building loading)
52532  *   
52533  * 
52534  * Usage: 
52535  *    
52536  * 
52537  * Myapp.login = Roo.Login({
52538      url: xxxx,
52539    
52540      realm : 'Myapp', 
52541      
52542      
52543      method : 'POST',
52544      
52545      
52546      * 
52547  })
52548  * 
52549  * 
52550  * 
52551  **/
52552  
52553 Roo.Login = function(cfg)
52554 {
52555     this.addEvents({
52556         'refreshed' : true
52557     });
52558     
52559     Roo.apply(this,cfg);
52560     
52561     Roo.onReady(function() {
52562         this.onLoad();
52563     }, this);
52564     // call parent..
52565     
52566    
52567     Roo.Login.superclass.constructor.call(this, this);
52568     //this.addxtype(this.items[0]);
52569     
52570     
52571 }
52572
52573
52574 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52575     
52576     /**
52577      * @cfg {String} method
52578      * Method used to query for login details.
52579      */
52580     
52581     method : 'POST',
52582     /**
52583      * @cfg {String} url
52584      * URL to query login data. - eg. baseURL + '/Login.php'
52585      */
52586     url : '',
52587     
52588     /**
52589      * @property user
52590      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52591      * @type {Object} 
52592      */
52593     user : false,
52594     /**
52595      * @property checkFails
52596      * Number of times we have attempted to get authentication check, and failed.
52597      * @type {Number} 
52598      */
52599     checkFails : 0,
52600       /**
52601      * @property intervalID
52602      * The window interval that does the constant login checking.
52603      * @type {Number} 
52604      */
52605     intervalID : 0,
52606     
52607     
52608     onLoad : function() // called on page load...
52609     {
52610         // load 
52611          
52612         if (Roo.get('loading')) { // clear any loading indicator..
52613             Roo.get('loading').remove();
52614         }
52615         
52616         //this.switchLang('en'); // set the language to english..
52617        
52618         this.check({
52619             success:  function(response, opts)  {  // check successfull...
52620             
52621                 var res = this.processResponse(response);
52622                 this.checkFails =0;
52623                 if (!res.success) { // error!
52624                     this.checkFails = 5;
52625                     //console.log('call failure');
52626                     return this.failure(response,opts);
52627                 }
52628                 
52629                 if (!res.data.id) { // id=0 == login failure.
52630                     return this.show();
52631                 }
52632                 
52633                               
52634                         //console.log(success);
52635                 this.fillAuth(res.data);   
52636                 this.checkFails =0;
52637                 Roo.XComponent.build();
52638             },
52639             failure : this.show
52640         });
52641         
52642     }, 
52643     
52644     
52645     check: function(cfg) // called every so often to refresh cookie etc..
52646     {
52647         if (cfg.again) { // could be undefined..
52648             this.checkFails++;
52649         } else {
52650             this.checkFails = 0;
52651         }
52652         var _this = this;
52653         if (this.sending) {
52654             if ( this.checkFails > 4) {
52655                 Roo.MessageBox.alert("Error",  
52656                     "Error getting authentication status. - try reloading, or wait a while", function() {
52657                         _this.sending = false;
52658                     }); 
52659                 return;
52660             }
52661             cfg.again = true;
52662             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52663             return;
52664         }
52665         this.sending = true;
52666         
52667         Roo.Ajax.request({  
52668             url: this.url,
52669             params: {
52670                 getAuthUser: true
52671             },  
52672             method: this.method,
52673             success:  cfg.success || this.success,
52674             failure : cfg.failure || this.failure,
52675             scope : this,
52676             callCfg : cfg
52677               
52678         });  
52679     }, 
52680     
52681     
52682     logout: function()
52683     {
52684         window.onbeforeunload = function() { }; // false does not work for IE..
52685         this.user = false;
52686         var _this = this;
52687         
52688         Roo.Ajax.request({  
52689             url: this.url,
52690             params: {
52691                 logout: 1
52692             },  
52693             method: 'GET',
52694             failure : function() {
52695                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52696                     document.location = document.location.toString() + '?ts=' + Math.random();
52697                 });
52698                 
52699             },
52700             success : function() {
52701                 _this.user = false;
52702                 this.checkFails =0;
52703                 // fixme..
52704                 document.location = document.location.toString() + '?ts=' + Math.random();
52705             }
52706               
52707               
52708         }); 
52709     },
52710     
52711     processResponse : function (response)
52712     {
52713         var res = '';
52714         try {
52715             res = Roo.decode(response.responseText);
52716             // oops...
52717             if (typeof(res) != 'object') {
52718                 res = { success : false, errorMsg : res, errors : true };
52719             }
52720             if (typeof(res.success) == 'undefined') {
52721                 res.success = false;
52722             }
52723             
52724         } catch(e) {
52725             res = { success : false,  errorMsg : response.responseText, errors : true };
52726         }
52727         return res;
52728     },
52729     
52730     success : function(response, opts)  // check successfull...
52731     {  
52732         this.sending = false;
52733         var res = this.processResponse(response);
52734         if (!res.success) {
52735             return this.failure(response, opts);
52736         }
52737         if (!res.data || !res.data.id) {
52738             return this.failure(response,opts);
52739         }
52740         //console.log(res);
52741         this.fillAuth(res.data);
52742         
52743         this.checkFails =0;
52744         
52745     },
52746     
52747     
52748     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52749     {
52750         this.authUser = -1;
52751         this.sending = false;
52752         var res = this.processResponse(response);
52753         //console.log(res);
52754         if ( this.checkFails > 2) {
52755         
52756             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52757                 "Error getting authentication status. - try reloading"); 
52758             return;
52759         }
52760         opts.callCfg.again = true;
52761         this.check.defer(1000, this, [ opts.callCfg ]);
52762         return;  
52763     },
52764     
52765     
52766     
52767     fillAuth: function(au) {
52768         this.startAuthCheck();
52769         this.authUserId = au.id;
52770         this.authUser = au;
52771         this.lastChecked = new Date();
52772         this.fireEvent('refreshed', au);
52773         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52774         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52775         au.lang = au.lang || 'en';
52776         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52777         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52778         this.switchLang(au.lang );
52779         
52780      
52781         // open system... - -on setyp..
52782         if (this.authUserId  < 0) {
52783             Roo.MessageBox.alert("Warning", 
52784                 "This is an open system - please set up a admin user with a password.");  
52785         }
52786          
52787         //Pman.onload(); // which should do nothing if it's a re-auth result...
52788         
52789              
52790     },
52791     
52792     startAuthCheck : function() // starter for timeout checking..
52793     {
52794         if (this.intervalID) { // timer already in place...
52795             return false;
52796         }
52797         var _this = this;
52798         this.intervalID =  window.setInterval(function() {
52799               _this.check(false);
52800             }, 120000); // every 120 secs = 2mins..
52801         
52802         
52803     },
52804          
52805     
52806     switchLang : function (lang) 
52807     {
52808         _T = typeof(_T) == 'undefined' ? false : _T;
52809           if (!_T || !lang.length) {
52810             return;
52811         }
52812         
52813         if (!_T && lang != 'en') {
52814             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52815             return;
52816         }
52817         
52818         if (typeof(_T.en) == 'undefined') {
52819             _T.en = {};
52820             Roo.apply(_T.en, _T);
52821         }
52822         
52823         if (typeof(_T[lang]) == 'undefined') {
52824             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52825             return;
52826         }
52827         
52828         
52829         Roo.apply(_T, _T[lang]);
52830         // just need to set the text values for everything...
52831         var _this = this;
52832         /* this will not work ...
52833         if (this.form) { 
52834             
52835                
52836             function formLabel(name, val) {
52837                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52838             }
52839             
52840             formLabel('password', "Password"+':');
52841             formLabel('username', "Email Address"+':');
52842             formLabel('lang', "Language"+':');
52843             this.dialog.setTitle("Login");
52844             this.dialog.buttons[0].setText("Forgot Password");
52845             this.dialog.buttons[1].setText("Login");
52846         }
52847         */
52848         
52849         
52850     },
52851     
52852     
52853     title: "Login",
52854     modal: true,
52855     width:  350,
52856     //height: 230,
52857     height: 180,
52858     shadow: true,
52859     minWidth:200,
52860     minHeight:180,
52861     //proxyDrag: true,
52862     closable: false,
52863     draggable: false,
52864     collapsible: false,
52865     resizable: false,
52866     center: {  // needed??
52867         autoScroll:false,
52868         titlebar: false,
52869        // tabPosition: 'top',
52870         hideTabs: true,
52871         closeOnTab: true,
52872         alwaysShowTabs: false
52873     } ,
52874     listeners : {
52875         
52876         show  : function(dlg)
52877         {
52878             //console.log(this);
52879             this.form = this.layout.getRegion('center').activePanel.form;
52880             this.form.dialog = dlg;
52881             this.buttons[0].form = this.form;
52882             this.buttons[0].dialog = dlg;
52883             this.buttons[1].form = this.form;
52884             this.buttons[1].dialog = dlg;
52885            
52886            //this.resizeToLogo.defer(1000,this);
52887             // this is all related to resizing for logos..
52888             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52889            //// if (!sz) {
52890              //   this.resizeToLogo.defer(1000,this);
52891              //   return;
52892            // }
52893             //var w = Ext.lib.Dom.getViewWidth() - 100;
52894             //var h = Ext.lib.Dom.getViewHeight() - 100;
52895             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52896             //this.center();
52897             if (this.disabled) {
52898                 this.hide();
52899                 return;
52900             }
52901             
52902             if (this.user.id < 0) { // used for inital setup situations.
52903                 return;
52904             }
52905             
52906             if (this.intervalID) {
52907                 // remove the timer
52908                 window.clearInterval(this.intervalID);
52909                 this.intervalID = false;
52910             }
52911             
52912             
52913             if (Roo.get('loading')) {
52914                 Roo.get('loading').remove();
52915             }
52916             if (Roo.get('loading-mask')) {
52917                 Roo.get('loading-mask').hide();
52918             }
52919             
52920             //incomming._node = tnode;
52921             this.form.reset();
52922             //this.dialog.modal = !modal;
52923             //this.dialog.show();
52924             this.el.unmask(); 
52925             
52926             
52927             this.form.setValues({
52928                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52929                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52930             });
52931             
52932             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52933             if (this.form.findField('username').getValue().length > 0 ){
52934                 this.form.findField('password').focus();
52935             } else {
52936                this.form.findField('username').focus();
52937             }
52938     
52939         }
52940     },
52941     items : [
52942          {
52943        
52944             xtype : 'ContentPanel',
52945             xns : Roo,
52946             region: 'center',
52947             fitToFrame : true,
52948             
52949             items : [
52950     
52951                 {
52952                
52953                     xtype : 'Form',
52954                     xns : Roo.form,
52955                     labelWidth: 100,
52956                     style : 'margin: 10px;',
52957                     
52958                     listeners : {
52959                         actionfailed : function(f, act) {
52960                             // form can return { errors: .... }
52961                                 
52962                             //act.result.errors // invalid form element list...
52963                             //act.result.errorMsg// invalid form element list...
52964                             
52965                             this.dialog.el.unmask();
52966                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52967                                         "Login failed - communication error - try again.");
52968                                       
52969                         },
52970                         actioncomplete: function(re, act) {
52971                              
52972                             Roo.state.Manager.set(
52973                                 this.dialog.realm + '.username',  
52974                                     this.findField('username').getValue()
52975                             );
52976                             Roo.state.Manager.set(
52977                                 this.dialog.realm + '.lang',  
52978                                 this.findField('lang').getValue() 
52979                             );
52980                             
52981                             this.dialog.fillAuth(act.result.data);
52982                               
52983                             this.dialog.hide();
52984                             
52985                             if (Roo.get('loading-mask')) {
52986                                 Roo.get('loading-mask').show();
52987                             }
52988                             Roo.XComponent.build();
52989                             
52990                              
52991                             
52992                         }
52993                     },
52994                     items : [
52995                         {
52996                             xtype : 'TextField',
52997                             xns : Roo.form,
52998                             fieldLabel: "Email Address",
52999                             name: 'username',
53000                             width:200,
53001                             autoCreate : {tag: "input", type: "text", size: "20"}
53002                         },
53003                         {
53004                             xtype : 'TextField',
53005                             xns : Roo.form,
53006                             fieldLabel: "Password",
53007                             inputType: 'password',
53008                             name: 'password',
53009                             width:200,
53010                             autoCreate : {tag: "input", type: "text", size: "20"},
53011                             listeners : {
53012                                 specialkey : function(e,ev) {
53013                                     if (ev.keyCode == 13) {
53014                                         this.form.dialog.el.mask("Logging in");
53015                                         this.form.doAction('submit', {
53016                                             url: this.form.dialog.url,
53017                                             method: this.form.dialog.method
53018                                         });
53019                                     }
53020                                 }
53021                             }  
53022                         },
53023                         {
53024                             xtype : 'ComboBox',
53025                             xns : Roo.form,
53026                             fieldLabel: "Language",
53027                             name : 'langdisp',
53028                             store: {
53029                                 xtype : 'SimpleStore',
53030                                 fields: ['lang', 'ldisp'],
53031                                 data : [
53032                                     [ 'en', 'English' ],
53033                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53034                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53035                                 ]
53036                             },
53037                             
53038                             valueField : 'lang',
53039                             hiddenName:  'lang',
53040                             width: 200,
53041                             displayField:'ldisp',
53042                             typeAhead: false,
53043                             editable: false,
53044                             mode: 'local',
53045                             triggerAction: 'all',
53046                             emptyText:'Select a Language...',
53047                             selectOnFocus:true,
53048                             listeners : {
53049                                 select :  function(cb, rec, ix) {
53050                                     this.form.switchLang(rec.data.lang);
53051                                 }
53052                             }
53053                         
53054                         }
53055                     ]
53056                 }
53057                   
53058                 
53059             ]
53060         }
53061     ],
53062     buttons : [
53063         {
53064             xtype : 'Button',
53065             xns : 'Roo',
53066             text : "Forgot Password",
53067             listeners : {
53068                 click : function() {
53069                     //console.log(this);
53070                     var n = this.form.findField('username').getValue();
53071                     if (!n.length) {
53072                         Roo.MessageBox.alert("Error", "Fill in your email address");
53073                         return;
53074                     }
53075                     Roo.Ajax.request({
53076                         url: this.dialog.url,
53077                         params: {
53078                             passwordRequest: n
53079                         },
53080                         method: this.dialog.method,
53081                         success:  function(response, opts)  {  // check successfull...
53082                         
53083                             var res = this.dialog.processResponse(response);
53084                             if (!res.success) { // error!
53085                                Roo.MessageBox.alert("Error" ,
53086                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53087                                return;
53088                             }
53089                             Roo.MessageBox.alert("Notice" ,
53090                                 "Please check you email for the Password Reset message");
53091                         },
53092                         failure : function() {
53093                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53094                         }
53095                         
53096                     });
53097                 }
53098             }
53099         },
53100         {
53101             xtype : 'Button',
53102             xns : 'Roo',
53103             text : "Login",
53104             listeners : {
53105                 
53106                 click : function () {
53107                         
53108                     this.dialog.el.mask("Logging in");
53109                     this.form.doAction('submit', {
53110                             url: this.dialog.url,
53111                             method: this.dialog.method
53112                     });
53113                 }
53114             }
53115         }
53116     ]
53117   
53118   
53119 })
53120  
53121
53122
53123