roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * 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     
23751     multiSelect : false,
23752     /**
23753      * @cfg {Boolean} singleSelect Allow single selection
23754      */
23755     singleSelect:  false,
23756     
23757     /**
23758      * Returns the element this view is bound to.
23759      * @return {Roo.Element}
23760      */
23761     getEl : function(){
23762         return this.el;
23763     },
23764
23765     /**
23766      * Refreshes the view.
23767      */
23768     refresh : function(){
23769         var t = this.tpl;
23770         this.clearSelections();
23771         this.el.update("");
23772         var html = [];
23773         var records = this.store.getRange();
23774         if(records.length < 1){
23775             this.el.update(this.emptyText);
23776             return;
23777         }
23778         for(var i = 0, len = records.length; i < len; i++){
23779             var data = this.prepareData(records[i].data, i, records[i]);
23780             this.fireEvent("preparedata", this, data, i, records[i]);
23781             html[html.length] = t.apply(data);
23782         }
23783         this.el.update(html.join(""));
23784         this.nodes = this.el.dom.childNodes;
23785         this.updateIndexes(0);
23786     },
23787
23788     /**
23789      * Function to override to reformat the data that is sent to
23790      * the template for each node.
23791      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23792      * a JSON object for an UpdateManager bound view).
23793      */
23794     prepareData : function(data){
23795         return data;
23796     },
23797
23798     onUpdate : function(ds, record){
23799         this.clearSelections();
23800         var index = this.store.indexOf(record);
23801         var n = this.nodes[index];
23802         this.tpl.insertBefore(n, this.prepareData(record.data));
23803         n.parentNode.removeChild(n);
23804         this.updateIndexes(index, index);
23805     },
23806
23807     onAdd : function(ds, records, index){
23808         this.clearSelections();
23809         if(this.nodes.length == 0){
23810             this.refresh();
23811             return;
23812         }
23813         var n = this.nodes[index];
23814         for(var i = 0, len = records.length; i < len; i++){
23815             var d = this.prepareData(records[i].data);
23816             if(n){
23817                 this.tpl.insertBefore(n, d);
23818             }else{
23819                 this.tpl.append(this.el, d);
23820             }
23821         }
23822         this.updateIndexes(index);
23823     },
23824
23825     onRemove : function(ds, record, index){
23826         this.clearSelections();
23827         this.el.dom.removeChild(this.nodes[index]);
23828         this.updateIndexes(index);
23829     },
23830
23831     /**
23832      * Refresh an individual node.
23833      * @param {Number} index
23834      */
23835     refreshNode : function(index){
23836         this.onUpdate(this.store, this.store.getAt(index));
23837     },
23838
23839     updateIndexes : function(startIndex, endIndex){
23840         var ns = this.nodes;
23841         startIndex = startIndex || 0;
23842         endIndex = endIndex || ns.length - 1;
23843         for(var i = startIndex; i <= endIndex; i++){
23844             ns[i].nodeIndex = i;
23845         }
23846     },
23847
23848     /**
23849      * Changes the data store this view uses and refresh the view.
23850      * @param {Store} store
23851      */
23852     setStore : function(store, initial){
23853         if(!initial && this.store){
23854             this.store.un("datachanged", this.refresh);
23855             this.store.un("add", this.onAdd);
23856             this.store.un("remove", this.onRemove);
23857             this.store.un("update", this.onUpdate);
23858             this.store.un("clear", this.refresh);
23859         }
23860         if(store){
23861           
23862             store.on("datachanged", this.refresh, this);
23863             store.on("add", this.onAdd, this);
23864             store.on("remove", this.onRemove, this);
23865             store.on("update", this.onUpdate, this);
23866             store.on("clear", this.refresh, this);
23867         }
23868         
23869         if(store){
23870             this.refresh();
23871         }
23872     },
23873
23874     /**
23875      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23876      * @param {HTMLElement} node
23877      * @return {HTMLElement} The template node
23878      */
23879     findItemFromChild : function(node){
23880         var el = this.el.dom;
23881         if(!node || node.parentNode == el){
23882                     return node;
23883             }
23884             var p = node.parentNode;
23885             while(p && p != el){
23886             if(p.parentNode == el){
23887                 return p;
23888             }
23889             p = p.parentNode;
23890         }
23891             return null;
23892     },
23893
23894     /** @ignore */
23895     onClick : function(e){
23896         var item = this.findItemFromChild(e.getTarget());
23897         if(item){
23898             var index = this.indexOf(item);
23899             if(this.onItemClick(item, index, e) !== false){
23900                 this.fireEvent("click", this, index, item, e);
23901             }
23902         }else{
23903             this.clearSelections();
23904         }
23905     },
23906
23907     /** @ignore */
23908     onContextMenu : function(e){
23909         var item = this.findItemFromChild(e.getTarget());
23910         if(item){
23911             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23912         }
23913     },
23914
23915     /** @ignore */
23916     onDblClick : function(e){
23917         var item = this.findItemFromChild(e.getTarget());
23918         if(item){
23919             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23920         }
23921     },
23922
23923     onItemClick : function(item, index, e){
23924         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23925             return false;
23926         }
23927         if(this.multiSelect || this.singleSelect){
23928             if(this.multiSelect && e.shiftKey && this.lastSelection){
23929                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23930             }else{
23931                 this.select(item, this.multiSelect && e.ctrlKey);
23932                 this.lastSelection = item;
23933             }
23934             e.preventDefault();
23935         }
23936         return true;
23937     },
23938
23939     /**
23940      * Get the number of selected nodes.
23941      * @return {Number}
23942      */
23943     getSelectionCount : function(){
23944         return this.selections.length;
23945     },
23946
23947     /**
23948      * Get the currently selected nodes.
23949      * @return {Array} An array of HTMLElements
23950      */
23951     getSelectedNodes : function(){
23952         return this.selections;
23953     },
23954
23955     /**
23956      * Get the indexes of the selected nodes.
23957      * @return {Array}
23958      */
23959     getSelectedIndexes : function(){
23960         var indexes = [], s = this.selections;
23961         for(var i = 0, len = s.length; i < len; i++){
23962             indexes.push(s[i].nodeIndex);
23963         }
23964         return indexes;
23965     },
23966
23967     /**
23968      * Clear all selections
23969      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23970      */
23971     clearSelections : function(suppressEvent){
23972         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23973             this.cmp.elements = this.selections;
23974             this.cmp.removeClass(this.selectedClass);
23975             this.selections = [];
23976             if(!suppressEvent){
23977                 this.fireEvent("selectionchange", this, this.selections);
23978             }
23979         }
23980     },
23981
23982     /**
23983      * Returns true if the passed node is selected
23984      * @param {HTMLElement/Number} node The node or node index
23985      * @return {Boolean}
23986      */
23987     isSelected : function(node){
23988         var s = this.selections;
23989         if(s.length < 1){
23990             return false;
23991         }
23992         node = this.getNode(node);
23993         return s.indexOf(node) !== -1;
23994     },
23995
23996     /**
23997      * Selects nodes.
23998      * @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
23999      * @param {Boolean} keepExisting (optional) true to keep existing selections
24000      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24001      */
24002     select : function(nodeInfo, keepExisting, suppressEvent){
24003         if(nodeInfo instanceof Array){
24004             if(!keepExisting){
24005                 this.clearSelections(true);
24006             }
24007             for(var i = 0, len = nodeInfo.length; i < len; i++){
24008                 this.select(nodeInfo[i], true, true);
24009             }
24010         } else{
24011             var node = this.getNode(nodeInfo);
24012             if(node && !this.isSelected(node)){
24013                 if(!keepExisting){
24014                     this.clearSelections(true);
24015                 }
24016                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24017                     Roo.fly(node).addClass(this.selectedClass);
24018                     this.selections.push(node);
24019                     if(!suppressEvent){
24020                         this.fireEvent("selectionchange", this, this.selections);
24021                     }
24022                 }
24023             }
24024         }
24025     },
24026
24027     /**
24028      * Gets a template node.
24029      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24030      * @return {HTMLElement} The node or null if it wasn't found
24031      */
24032     getNode : function(nodeInfo){
24033         if(typeof nodeInfo == "string"){
24034             return document.getElementById(nodeInfo);
24035         }else if(typeof nodeInfo == "number"){
24036             return this.nodes[nodeInfo];
24037         }
24038         return nodeInfo;
24039     },
24040
24041     /**
24042      * Gets a range template nodes.
24043      * @param {Number} startIndex
24044      * @param {Number} endIndex
24045      * @return {Array} An array of nodes
24046      */
24047     getNodes : function(start, end){
24048         var ns = this.nodes;
24049         start = start || 0;
24050         end = typeof end == "undefined" ? ns.length - 1 : end;
24051         var nodes = [];
24052         if(start <= end){
24053             for(var i = start; i <= end; i++){
24054                 nodes.push(ns[i]);
24055             }
24056         } else{
24057             for(var i = start; i >= end; i--){
24058                 nodes.push(ns[i]);
24059             }
24060         }
24061         return nodes;
24062     },
24063
24064     /**
24065      * Finds the index of the passed node
24066      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24067      * @return {Number} The index of the node or -1
24068      */
24069     indexOf : function(node){
24070         node = this.getNode(node);
24071         if(typeof node.nodeIndex == "number"){
24072             return node.nodeIndex;
24073         }
24074         var ns = this.nodes;
24075         for(var i = 0, len = ns.length; i < len; i++){
24076             if(ns[i] == node){
24077                 return i;
24078             }
24079         }
24080         return -1;
24081     }
24082 });
24083 /*
24084  * Based on:
24085  * Ext JS Library 1.1.1
24086  * Copyright(c) 2006-2007, Ext JS, LLC.
24087  *
24088  * Originally Released Under LGPL - original licence link has changed is not relivant.
24089  *
24090  * Fork - LGPL
24091  * <script type="text/javascript">
24092  */
24093
24094 /**
24095  * @class Roo.JsonView
24096  * @extends Roo.View
24097  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24098 <pre><code>
24099 var view = new Roo.JsonView({
24100     container: "my-element",
24101     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24102     multiSelect: true, 
24103     jsonRoot: "data" 
24104 });
24105
24106 // listen for node click?
24107 view.on("click", function(vw, index, node, e){
24108     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24109 });
24110
24111 // direct load of JSON data
24112 view.load("foobar.php");
24113
24114 // Example from my blog list
24115 var tpl = new Roo.Template(
24116     '&lt;div class="entry"&gt;' +
24117     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24118     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24119     "&lt;/div&gt;&lt;hr /&gt;"
24120 );
24121
24122 var moreView = new Roo.JsonView({
24123     container :  "entry-list", 
24124     template : tpl,
24125     jsonRoot: "posts"
24126 });
24127 moreView.on("beforerender", this.sortEntries, this);
24128 moreView.load({
24129     url: "/blog/get-posts.php",
24130     params: "allposts=true",
24131     text: "Loading Blog Entries..."
24132 });
24133 </code></pre>
24134
24135 * Note: old code is supported with arguments : (container, template, config)
24136
24137
24138  * @constructor
24139  * Create a new JsonView
24140  * 
24141  * @param {Object} config The config object
24142  * 
24143  */
24144 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24145     
24146     
24147     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24148
24149     var um = this.el.getUpdateManager();
24150     um.setRenderer(this);
24151     um.on("update", this.onLoad, this);
24152     um.on("failure", this.onLoadException, this);
24153
24154     /**
24155      * @event beforerender
24156      * Fires before rendering of the downloaded JSON data.
24157      * @param {Roo.JsonView} this
24158      * @param {Object} data The JSON data loaded
24159      */
24160     /**
24161      * @event load
24162      * Fires when data is loaded.
24163      * @param {Roo.JsonView} this
24164      * @param {Object} data The JSON data loaded
24165      * @param {Object} response The raw Connect response object
24166      */
24167     /**
24168      * @event loadexception
24169      * Fires when loading fails.
24170      * @param {Roo.JsonView} this
24171      * @param {Object} response The raw Connect response object
24172      */
24173     this.addEvents({
24174         'beforerender' : true,
24175         'load' : true,
24176         'loadexception' : true
24177     });
24178 };
24179 Roo.extend(Roo.JsonView, Roo.View, {
24180     /**
24181      * @type {String} The root property in the loaded JSON object that contains the data
24182      */
24183     jsonRoot : "",
24184
24185     /**
24186      * Refreshes the view.
24187      */
24188     refresh : function(){
24189         this.clearSelections();
24190         this.el.update("");
24191         var html = [];
24192         var o = this.jsonData;
24193         if(o && o.length > 0){
24194             for(var i = 0, len = o.length; i < len; i++){
24195                 var data = this.prepareData(o[i], i, o);
24196                 html[html.length] = this.tpl.apply(data);
24197             }
24198         }else{
24199             html.push(this.emptyText);
24200         }
24201         this.el.update(html.join(""));
24202         this.nodes = this.el.dom.childNodes;
24203         this.updateIndexes(0);
24204     },
24205
24206     /**
24207      * 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.
24208      * @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:
24209      <pre><code>
24210      view.load({
24211          url: "your-url.php",
24212          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24213          callback: yourFunction,
24214          scope: yourObject, //(optional scope)
24215          discardUrl: false,
24216          nocache: false,
24217          text: "Loading...",
24218          timeout: 30,
24219          scripts: false
24220      });
24221      </code></pre>
24222      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24223      * 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.
24224      * @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}
24225      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24226      * @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.
24227      */
24228     load : function(){
24229         var um = this.el.getUpdateManager();
24230         um.update.apply(um, arguments);
24231     },
24232
24233     render : function(el, response){
24234         this.clearSelections();
24235         this.el.update("");
24236         var o;
24237         try{
24238             o = Roo.util.JSON.decode(response.responseText);
24239             if(this.jsonRoot){
24240                 
24241                 o = o[this.jsonRoot];
24242             }
24243         } catch(e){
24244         }
24245         /**
24246          * The current JSON data or null
24247          */
24248         this.jsonData = o;
24249         this.beforeRender();
24250         this.refresh();
24251     },
24252
24253 /**
24254  * Get the number of records in the current JSON dataset
24255  * @return {Number}
24256  */
24257     getCount : function(){
24258         return this.jsonData ? this.jsonData.length : 0;
24259     },
24260
24261 /**
24262  * Returns the JSON object for the specified node(s)
24263  * @param {HTMLElement/Array} node The node or an array of nodes
24264  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24265  * you get the JSON object for the node
24266  */
24267     getNodeData : function(node){
24268         if(node instanceof Array){
24269             var data = [];
24270             for(var i = 0, len = node.length; i < len; i++){
24271                 data.push(this.getNodeData(node[i]));
24272             }
24273             return data;
24274         }
24275         return this.jsonData[this.indexOf(node)] || null;
24276     },
24277
24278     beforeRender : function(){
24279         this.snapshot = this.jsonData;
24280         if(this.sortInfo){
24281             this.sort.apply(this, this.sortInfo);
24282         }
24283         this.fireEvent("beforerender", this, this.jsonData);
24284     },
24285
24286     onLoad : function(el, o){
24287         this.fireEvent("load", this, this.jsonData, o);
24288     },
24289
24290     onLoadException : function(el, o){
24291         this.fireEvent("loadexception", this, o);
24292     },
24293
24294 /**
24295  * Filter the data by a specific property.
24296  * @param {String} property A property on your JSON objects
24297  * @param {String/RegExp} value Either string that the property values
24298  * should start with, or a RegExp to test against the property
24299  */
24300     filter : function(property, value){
24301         if(this.jsonData){
24302             var data = [];
24303             var ss = this.snapshot;
24304             if(typeof value == "string"){
24305                 var vlen = value.length;
24306                 if(vlen == 0){
24307                     this.clearFilter();
24308                     return;
24309                 }
24310                 value = value.toLowerCase();
24311                 for(var i = 0, len = ss.length; i < len; i++){
24312                     var o = ss[i];
24313                     if(o[property].substr(0, vlen).toLowerCase() == value){
24314                         data.push(o);
24315                     }
24316                 }
24317             } else if(value.exec){ // regex?
24318                 for(var i = 0, len = ss.length; i < len; i++){
24319                     var o = ss[i];
24320                     if(value.test(o[property])){
24321                         data.push(o);
24322                     }
24323                 }
24324             } else{
24325                 return;
24326             }
24327             this.jsonData = data;
24328             this.refresh();
24329         }
24330     },
24331
24332 /**
24333  * Filter by a function. The passed function will be called with each
24334  * object in the current dataset. If the function returns true the value is kept,
24335  * otherwise it is filtered.
24336  * @param {Function} fn
24337  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24338  */
24339     filterBy : function(fn, scope){
24340         if(this.jsonData){
24341             var data = [];
24342             var ss = this.snapshot;
24343             for(var i = 0, len = ss.length; i < len; i++){
24344                 var o = ss[i];
24345                 if(fn.call(scope || this, o)){
24346                     data.push(o);
24347                 }
24348             }
24349             this.jsonData = data;
24350             this.refresh();
24351         }
24352     },
24353
24354 /**
24355  * Clears the current filter.
24356  */
24357     clearFilter : function(){
24358         if(this.snapshot && this.jsonData != this.snapshot){
24359             this.jsonData = this.snapshot;
24360             this.refresh();
24361         }
24362     },
24363
24364
24365 /**
24366  * Sorts the data for this view and refreshes it.
24367  * @param {String} property A property on your JSON objects to sort on
24368  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24369  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24370  */
24371     sort : function(property, dir, sortType){
24372         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24373         if(this.jsonData){
24374             var p = property;
24375             var dsc = dir && dir.toLowerCase() == "desc";
24376             var f = function(o1, o2){
24377                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24378                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24379                 ;
24380                 if(v1 < v2){
24381                     return dsc ? +1 : -1;
24382                 } else if(v1 > v2){
24383                     return dsc ? -1 : +1;
24384                 } else{
24385                     return 0;
24386                 }
24387             };
24388             this.jsonData.sort(f);
24389             this.refresh();
24390             if(this.jsonData != this.snapshot){
24391                 this.snapshot.sort(f);
24392             }
24393         }
24394     }
24395 });/*
24396  * Based on:
24397  * Ext JS Library 1.1.1
24398  * Copyright(c) 2006-2007, Ext JS, LLC.
24399  *
24400  * Originally Released Under LGPL - original licence link has changed is not relivant.
24401  *
24402  * Fork - LGPL
24403  * <script type="text/javascript">
24404  */
24405  
24406
24407 /**
24408  * @class Roo.ColorPalette
24409  * @extends Roo.Component
24410  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24411  * Here's an example of typical usage:
24412  * <pre><code>
24413 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24414 cp.render('my-div');
24415
24416 cp.on('select', function(palette, selColor){
24417     // do something with selColor
24418 });
24419 </code></pre>
24420  * @constructor
24421  * Create a new ColorPalette
24422  * @param {Object} config The config object
24423  */
24424 Roo.ColorPalette = function(config){
24425     Roo.ColorPalette.superclass.constructor.call(this, config);
24426     this.addEvents({
24427         /**
24428              * @event select
24429              * Fires when a color is selected
24430              * @param {ColorPalette} this
24431              * @param {String} color The 6-digit color hex code (without the # symbol)
24432              */
24433         select: true
24434     });
24435
24436     if(this.handler){
24437         this.on("select", this.handler, this.scope, true);
24438     }
24439 };
24440 Roo.extend(Roo.ColorPalette, Roo.Component, {
24441     /**
24442      * @cfg {String} itemCls
24443      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24444      */
24445     itemCls : "x-color-palette",
24446     /**
24447      * @cfg {String} value
24448      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24449      * the hex codes are case-sensitive.
24450      */
24451     value : null,
24452     clickEvent:'click',
24453     // private
24454     ctype: "Roo.ColorPalette",
24455
24456     /**
24457      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24458      */
24459     allowReselect : false,
24460
24461     /**
24462      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24463      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24464      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24465      * of colors with the width setting until the box is symmetrical.</p>
24466      * <p>You can override individual colors if needed:</p>
24467      * <pre><code>
24468 var cp = new Roo.ColorPalette();
24469 cp.colors[0] = "FF0000";  // change the first box to red
24470 </code></pre>
24471
24472 Or you can provide a custom array of your own for complete control:
24473 <pre><code>
24474 var cp = new Roo.ColorPalette();
24475 cp.colors = ["000000", "993300", "333300"];
24476 </code></pre>
24477      * @type Array
24478      */
24479     colors : [
24480         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24481         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24482         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24483         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24484         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24485     ],
24486
24487     // private
24488     onRender : function(container, position){
24489         var t = new Roo.MasterTemplate(
24490             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24491         );
24492         var c = this.colors;
24493         for(var i = 0, len = c.length; i < len; i++){
24494             t.add([c[i]]);
24495         }
24496         var el = document.createElement("div");
24497         el.className = this.itemCls;
24498         t.overwrite(el);
24499         container.dom.insertBefore(el, position);
24500         this.el = Roo.get(el);
24501         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24502         if(this.clickEvent != 'click'){
24503             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24504         }
24505     },
24506
24507     // private
24508     afterRender : function(){
24509         Roo.ColorPalette.superclass.afterRender.call(this);
24510         if(this.value){
24511             var s = this.value;
24512             this.value = null;
24513             this.select(s);
24514         }
24515     },
24516
24517     // private
24518     handleClick : function(e, t){
24519         e.preventDefault();
24520         if(!this.disabled){
24521             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24522             this.select(c.toUpperCase());
24523         }
24524     },
24525
24526     /**
24527      * Selects the specified color in the palette (fires the select event)
24528      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24529      */
24530     select : function(color){
24531         color = color.replace("#", "");
24532         if(color != this.value || this.allowReselect){
24533             var el = this.el;
24534             if(this.value){
24535                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24536             }
24537             el.child("a.color-"+color).addClass("x-color-palette-sel");
24538             this.value = color;
24539             this.fireEvent("select", this, color);
24540         }
24541     }
24542 });/*
24543  * Based on:
24544  * Ext JS Library 1.1.1
24545  * Copyright(c) 2006-2007, Ext JS, LLC.
24546  *
24547  * Originally Released Under LGPL - original licence link has changed is not relivant.
24548  *
24549  * Fork - LGPL
24550  * <script type="text/javascript">
24551  */
24552  
24553 /**
24554  * @class Roo.DatePicker
24555  * @extends Roo.Component
24556  * Simple date picker class.
24557  * @constructor
24558  * Create a new DatePicker
24559  * @param {Object} config The config object
24560  */
24561 Roo.DatePicker = function(config){
24562     Roo.DatePicker.superclass.constructor.call(this, config);
24563
24564     this.value = config && config.value ?
24565                  config.value.clearTime() : new Date().clearTime();
24566
24567     this.addEvents({
24568         /**
24569              * @event select
24570              * Fires when a date is selected
24571              * @param {DatePicker} this
24572              * @param {Date} date The selected date
24573              */
24574         select: true
24575     });
24576
24577     if(this.handler){
24578         this.on("select", this.handler,  this.scope || this);
24579     }
24580     // build the disabledDatesRE
24581     if(!this.disabledDatesRE && this.disabledDates){
24582         var dd = this.disabledDates;
24583         var re = "(?:";
24584         for(var i = 0; i < dd.length; i++){
24585             re += dd[i];
24586             if(i != dd.length-1) re += "|";
24587         }
24588         this.disabledDatesRE = new RegExp(re + ")");
24589     }
24590 };
24591
24592 Roo.extend(Roo.DatePicker, Roo.Component, {
24593     /**
24594      * @cfg {String} todayText
24595      * The text to display on the button that selects the current date (defaults to "Today")
24596      */
24597     todayText : "Today",
24598     /**
24599      * @cfg {String} okText
24600      * The text to display on the ok button
24601      */
24602     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24603     /**
24604      * @cfg {String} cancelText
24605      * The text to display on the cancel button
24606      */
24607     cancelText : "Cancel",
24608     /**
24609      * @cfg {String} todayTip
24610      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24611      */
24612     todayTip : "{0} (Spacebar)",
24613     /**
24614      * @cfg {Date} minDate
24615      * Minimum allowable date (JavaScript date object, defaults to null)
24616      */
24617     minDate : null,
24618     /**
24619      * @cfg {Date} maxDate
24620      * Maximum allowable date (JavaScript date object, defaults to null)
24621      */
24622     maxDate : null,
24623     /**
24624      * @cfg {String} minText
24625      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24626      */
24627     minText : "This date is before the minimum date",
24628     /**
24629      * @cfg {String} maxText
24630      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24631      */
24632     maxText : "This date is after the maximum date",
24633     /**
24634      * @cfg {String} format
24635      * The default date format string which can be overriden for localization support.  The format must be
24636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24637      */
24638     format : "m/d/y",
24639     /**
24640      * @cfg {Array} disabledDays
24641      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24642      */
24643     disabledDays : null,
24644     /**
24645      * @cfg {String} disabledDaysText
24646      * The tooltip to display when the date falls on a disabled day (defaults to "")
24647      */
24648     disabledDaysText : "",
24649     /**
24650      * @cfg {RegExp} disabledDatesRE
24651      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24652      */
24653     disabledDatesRE : null,
24654     /**
24655      * @cfg {String} disabledDatesText
24656      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24657      */
24658     disabledDatesText : "",
24659     /**
24660      * @cfg {Boolean} constrainToViewport
24661      * True to constrain the date picker to the viewport (defaults to true)
24662      */
24663     constrainToViewport : true,
24664     /**
24665      * @cfg {Array} monthNames
24666      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24667      */
24668     monthNames : Date.monthNames,
24669     /**
24670      * @cfg {Array} dayNames
24671      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24672      */
24673     dayNames : Date.dayNames,
24674     /**
24675      * @cfg {String} nextText
24676      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24677      */
24678     nextText: 'Next Month (Control+Right)',
24679     /**
24680      * @cfg {String} prevText
24681      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24682      */
24683     prevText: 'Previous Month (Control+Left)',
24684     /**
24685      * @cfg {String} monthYearText
24686      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24687      */
24688     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24689     /**
24690      * @cfg {Number} startDay
24691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24692      */
24693     startDay : 0,
24694     /**
24695      * @cfg {Bool} showClear
24696      * Show a clear button (usefull for date form elements that can be blank.)
24697      */
24698     
24699     showClear: false,
24700     
24701     /**
24702      * Sets the value of the date field
24703      * @param {Date} value The date to set
24704      */
24705     setValue : function(value){
24706         var old = this.value;
24707         this.value = value.clearTime(true);
24708         if(this.el){
24709             this.update(this.value);
24710         }
24711     },
24712
24713     /**
24714      * Gets the current selected value of the date field
24715      * @return {Date} The selected date
24716      */
24717     getValue : function(){
24718         return this.value;
24719     },
24720
24721     // private
24722     focus : function(){
24723         if(this.el){
24724             this.update(this.activeDate);
24725         }
24726     },
24727
24728     // private
24729     onRender : function(container, position){
24730         var m = [
24731              '<table cellspacing="0">',
24732                 '<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>',
24733                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24734         var dn = this.dayNames;
24735         for(var i = 0; i < 7; i++){
24736             var d = this.startDay+i;
24737             if(d > 6){
24738                 d = d-7;
24739             }
24740             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24741         }
24742         m[m.length] = "</tr></thead><tbody><tr>";
24743         for(var i = 0; i < 42; i++) {
24744             if(i % 7 == 0 && i != 0){
24745                 m[m.length] = "</tr><tr>";
24746             }
24747             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24748         }
24749         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24750             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24751
24752         var el = document.createElement("div");
24753         el.className = "x-date-picker";
24754         el.innerHTML = m.join("");
24755
24756         container.dom.insertBefore(el, position);
24757
24758         this.el = Roo.get(el);
24759         this.eventEl = Roo.get(el.firstChild);
24760
24761         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24762             handler: this.showPrevMonth,
24763             scope: this,
24764             preventDefault:true,
24765             stopDefault:true
24766         });
24767
24768         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24769             handler: this.showNextMonth,
24770             scope: this,
24771             preventDefault:true,
24772             stopDefault:true
24773         });
24774
24775         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24776
24777         this.monthPicker = this.el.down('div.x-date-mp');
24778         this.monthPicker.enableDisplayMode('block');
24779         
24780         var kn = new Roo.KeyNav(this.eventEl, {
24781             "left" : function(e){
24782                 e.ctrlKey ?
24783                     this.showPrevMonth() :
24784                     this.update(this.activeDate.add("d", -1));
24785             },
24786
24787             "right" : function(e){
24788                 e.ctrlKey ?
24789                     this.showNextMonth() :
24790                     this.update(this.activeDate.add("d", 1));
24791             },
24792
24793             "up" : function(e){
24794                 e.ctrlKey ?
24795                     this.showNextYear() :
24796                     this.update(this.activeDate.add("d", -7));
24797             },
24798
24799             "down" : function(e){
24800                 e.ctrlKey ?
24801                     this.showPrevYear() :
24802                     this.update(this.activeDate.add("d", 7));
24803             },
24804
24805             "pageUp" : function(e){
24806                 this.showNextMonth();
24807             },
24808
24809             "pageDown" : function(e){
24810                 this.showPrevMonth();
24811             },
24812
24813             "enter" : function(e){
24814                 e.stopPropagation();
24815                 return true;
24816             },
24817
24818             scope : this
24819         });
24820
24821         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24822
24823         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24824
24825         this.el.unselectable();
24826         
24827         this.cells = this.el.select("table.x-date-inner tbody td");
24828         this.textNodes = this.el.query("table.x-date-inner tbody span");
24829
24830         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24831             text: "&#160;",
24832             tooltip: this.monthYearText
24833         });
24834
24835         this.mbtn.on('click', this.showMonthPicker, this);
24836         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24837
24838
24839         var today = (new Date()).dateFormat(this.format);
24840         
24841         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24842         if (this.showClear) {
24843             baseTb.add( new Roo.Toolbar.Fill());
24844         }
24845         baseTb.add({
24846             text: String.format(this.todayText, today),
24847             tooltip: String.format(this.todayTip, today),
24848             handler: this.selectToday,
24849             scope: this
24850         });
24851         
24852         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24853             
24854         //});
24855         if (this.showClear) {
24856             
24857             baseTb.add( new Roo.Toolbar.Fill());
24858             baseTb.add({
24859                 text: '&#160;',
24860                 cls: 'x-btn-icon x-btn-clear',
24861                 handler: function() {
24862                     //this.value = '';
24863                     this.fireEvent("select", this, '');
24864                 },
24865                 scope: this
24866             });
24867         }
24868         
24869         
24870         if(Roo.isIE){
24871             this.el.repaint();
24872         }
24873         this.update(this.value);
24874     },
24875
24876     createMonthPicker : function(){
24877         if(!this.monthPicker.dom.firstChild){
24878             var buf = ['<table border="0" cellspacing="0">'];
24879             for(var i = 0; i < 6; i++){
24880                 buf.push(
24881                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24882                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24883                     i == 0 ?
24884                     '<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>' :
24885                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24886                 );
24887             }
24888             buf.push(
24889                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24890                     this.okText,
24891                     '</button><button type="button" class="x-date-mp-cancel">',
24892                     this.cancelText,
24893                     '</button></td></tr>',
24894                 '</table>'
24895             );
24896             this.monthPicker.update(buf.join(''));
24897             this.monthPicker.on('click', this.onMonthClick, this);
24898             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24899
24900             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24901             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24902
24903             this.mpMonths.each(function(m, a, i){
24904                 i += 1;
24905                 if((i%2) == 0){
24906                     m.dom.xmonth = 5 + Math.round(i * .5);
24907                 }else{
24908                     m.dom.xmonth = Math.round((i-1) * .5);
24909                 }
24910             });
24911         }
24912     },
24913
24914     showMonthPicker : function(){
24915         this.createMonthPicker();
24916         var size = this.el.getSize();
24917         this.monthPicker.setSize(size);
24918         this.monthPicker.child('table').setSize(size);
24919
24920         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24921         this.updateMPMonth(this.mpSelMonth);
24922         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24923         this.updateMPYear(this.mpSelYear);
24924
24925         this.monthPicker.slideIn('t', {duration:.2});
24926     },
24927
24928     updateMPYear : function(y){
24929         this.mpyear = y;
24930         var ys = this.mpYears.elements;
24931         for(var i = 1; i <= 10; i++){
24932             var td = ys[i-1], y2;
24933             if((i%2) == 0){
24934                 y2 = y + Math.round(i * .5);
24935                 td.firstChild.innerHTML = y2;
24936                 td.xyear = y2;
24937             }else{
24938                 y2 = y - (5-Math.round(i * .5));
24939                 td.firstChild.innerHTML = y2;
24940                 td.xyear = y2;
24941             }
24942             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24943         }
24944     },
24945
24946     updateMPMonth : function(sm){
24947         this.mpMonths.each(function(m, a, i){
24948             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24949         });
24950     },
24951
24952     selectMPMonth: function(m){
24953         
24954     },
24955
24956     onMonthClick : function(e, t){
24957         e.stopEvent();
24958         var el = new Roo.Element(t), pn;
24959         if(el.is('button.x-date-mp-cancel')){
24960             this.hideMonthPicker();
24961         }
24962         else if(el.is('button.x-date-mp-ok')){
24963             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24964             this.hideMonthPicker();
24965         }
24966         else if(pn = el.up('td.x-date-mp-month', 2)){
24967             this.mpMonths.removeClass('x-date-mp-sel');
24968             pn.addClass('x-date-mp-sel');
24969             this.mpSelMonth = pn.dom.xmonth;
24970         }
24971         else if(pn = el.up('td.x-date-mp-year', 2)){
24972             this.mpYears.removeClass('x-date-mp-sel');
24973             pn.addClass('x-date-mp-sel');
24974             this.mpSelYear = pn.dom.xyear;
24975         }
24976         else if(el.is('a.x-date-mp-prev')){
24977             this.updateMPYear(this.mpyear-10);
24978         }
24979         else if(el.is('a.x-date-mp-next')){
24980             this.updateMPYear(this.mpyear+10);
24981         }
24982     },
24983
24984     onMonthDblClick : function(e, t){
24985         e.stopEvent();
24986         var el = new Roo.Element(t), pn;
24987         if(pn = el.up('td.x-date-mp-month', 2)){
24988             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24989             this.hideMonthPicker();
24990         }
24991         else if(pn = el.up('td.x-date-mp-year', 2)){
24992             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24993             this.hideMonthPicker();
24994         }
24995     },
24996
24997     hideMonthPicker : function(disableAnim){
24998         if(this.monthPicker){
24999             if(disableAnim === true){
25000                 this.monthPicker.hide();
25001             }else{
25002                 this.monthPicker.slideOut('t', {duration:.2});
25003             }
25004         }
25005     },
25006
25007     // private
25008     showPrevMonth : function(e){
25009         this.update(this.activeDate.add("mo", -1));
25010     },
25011
25012     // private
25013     showNextMonth : function(e){
25014         this.update(this.activeDate.add("mo", 1));
25015     },
25016
25017     // private
25018     showPrevYear : function(){
25019         this.update(this.activeDate.add("y", -1));
25020     },
25021
25022     // private
25023     showNextYear : function(){
25024         this.update(this.activeDate.add("y", 1));
25025     },
25026
25027     // private
25028     handleMouseWheel : function(e){
25029         var delta = e.getWheelDelta();
25030         if(delta > 0){
25031             this.showPrevMonth();
25032             e.stopEvent();
25033         } else if(delta < 0){
25034             this.showNextMonth();
25035             e.stopEvent();
25036         }
25037     },
25038
25039     // private
25040     handleDateClick : function(e, t){
25041         e.stopEvent();
25042         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25043             this.setValue(new Date(t.dateValue));
25044             this.fireEvent("select", this, this.value);
25045         }
25046     },
25047
25048     // private
25049     selectToday : function(){
25050         this.setValue(new Date().clearTime());
25051         this.fireEvent("select", this, this.value);
25052     },
25053
25054     // private
25055     update : function(date){
25056         var vd = this.activeDate;
25057         this.activeDate = date;
25058         if(vd && this.el){
25059             var t = date.getTime();
25060             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25061                 this.cells.removeClass("x-date-selected");
25062                 this.cells.each(function(c){
25063                    if(c.dom.firstChild.dateValue == t){
25064                        c.addClass("x-date-selected");
25065                        setTimeout(function(){
25066                             try{c.dom.firstChild.focus();}catch(e){}
25067                        }, 50);
25068                        return false;
25069                    }
25070                 });
25071                 return;
25072             }
25073         }
25074         var days = date.getDaysInMonth();
25075         var firstOfMonth = date.getFirstDateOfMonth();
25076         var startingPos = firstOfMonth.getDay()-this.startDay;
25077
25078         if(startingPos <= this.startDay){
25079             startingPos += 7;
25080         }
25081
25082         var pm = date.add("mo", -1);
25083         var prevStart = pm.getDaysInMonth()-startingPos;
25084
25085         var cells = this.cells.elements;
25086         var textEls = this.textNodes;
25087         days += startingPos;
25088
25089         // convert everything to numbers so it's fast
25090         var day = 86400000;
25091         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25092         var today = new Date().clearTime().getTime();
25093         var sel = date.clearTime().getTime();
25094         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25095         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25096         var ddMatch = this.disabledDatesRE;
25097         var ddText = this.disabledDatesText;
25098         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25099         var ddaysText = this.disabledDaysText;
25100         var format = this.format;
25101
25102         var setCellClass = function(cal, cell){
25103             cell.title = "";
25104             var t = d.getTime();
25105             cell.firstChild.dateValue = t;
25106             if(t == today){
25107                 cell.className += " x-date-today";
25108                 cell.title = cal.todayText;
25109             }
25110             if(t == sel){
25111                 cell.className += " x-date-selected";
25112                 setTimeout(function(){
25113                     try{cell.firstChild.focus();}catch(e){}
25114                 }, 50);
25115             }
25116             // disabling
25117             if(t < min) {
25118                 cell.className = " x-date-disabled";
25119                 cell.title = cal.minText;
25120                 return;
25121             }
25122             if(t > max) {
25123                 cell.className = " x-date-disabled";
25124                 cell.title = cal.maxText;
25125                 return;
25126             }
25127             if(ddays){
25128                 if(ddays.indexOf(d.getDay()) != -1){
25129                     cell.title = ddaysText;
25130                     cell.className = " x-date-disabled";
25131                 }
25132             }
25133             if(ddMatch && format){
25134                 var fvalue = d.dateFormat(format);
25135                 if(ddMatch.test(fvalue)){
25136                     cell.title = ddText.replace("%0", fvalue);
25137                     cell.className = " x-date-disabled";
25138                 }
25139             }
25140         };
25141
25142         var i = 0;
25143         for(; i < startingPos; i++) {
25144             textEls[i].innerHTML = (++prevStart);
25145             d.setDate(d.getDate()+1);
25146             cells[i].className = "x-date-prevday";
25147             setCellClass(this, cells[i]);
25148         }
25149         for(; i < days; i++){
25150             intDay = i - startingPos + 1;
25151             textEls[i].innerHTML = (intDay);
25152             d.setDate(d.getDate()+1);
25153             cells[i].className = "x-date-active";
25154             setCellClass(this, cells[i]);
25155         }
25156         var extraDays = 0;
25157         for(; i < 42; i++) {
25158              textEls[i].innerHTML = (++extraDays);
25159              d.setDate(d.getDate()+1);
25160              cells[i].className = "x-date-nextday";
25161              setCellClass(this, cells[i]);
25162         }
25163
25164         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25165
25166         if(!this.internalRender){
25167             var main = this.el.dom.firstChild;
25168             var w = main.offsetWidth;
25169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25170             Roo.fly(main).setWidth(w);
25171             this.internalRender = true;
25172             // opera does not respect the auto grow header center column
25173             // then, after it gets a width opera refuses to recalculate
25174             // without a second pass
25175             if(Roo.isOpera && !this.secondPass){
25176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25177                 this.secondPass = true;
25178                 this.update.defer(10, this, [date]);
25179             }
25180         }
25181     }
25182 });        /*
25183  * Based on:
25184  * Ext JS Library 1.1.1
25185  * Copyright(c) 2006-2007, Ext JS, LLC.
25186  *
25187  * Originally Released Under LGPL - original licence link has changed is not relivant.
25188  *
25189  * Fork - LGPL
25190  * <script type="text/javascript">
25191  */
25192 /**
25193  * @class Roo.TabPanel
25194  * @extends Roo.util.Observable
25195  * A lightweight tab container.
25196  * <br><br>
25197  * Usage:
25198  * <pre><code>
25199 // basic tabs 1, built from existing content
25200 var tabs = new Roo.TabPanel("tabs1");
25201 tabs.addTab("script", "View Script");
25202 tabs.addTab("markup", "View Markup");
25203 tabs.activate("script");
25204
25205 // more advanced tabs, built from javascript
25206 var jtabs = new Roo.TabPanel("jtabs");
25207 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25208
25209 // set up the UpdateManager
25210 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25211 var updater = tab2.getUpdateManager();
25212 updater.setDefaultUrl("ajax1.htm");
25213 tab2.on('activate', updater.refresh, updater, true);
25214
25215 // Use setUrl for Ajax loading
25216 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25217 tab3.setUrl("ajax2.htm", null, true);
25218
25219 // Disabled tab
25220 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25221 tab4.disable();
25222
25223 jtabs.activate("jtabs-1");
25224  * </code></pre>
25225  * @constructor
25226  * Create a new TabPanel.
25227  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25228  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25229  */
25230 Roo.TabPanel = function(container, config){
25231     /**
25232     * The container element for this TabPanel.
25233     * @type Roo.Element
25234     */
25235     this.el = Roo.get(container, true);
25236     if(config){
25237         if(typeof config == "boolean"){
25238             this.tabPosition = config ? "bottom" : "top";
25239         }else{
25240             Roo.apply(this, config);
25241         }
25242     }
25243     if(this.tabPosition == "bottom"){
25244         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25245         this.el.addClass("x-tabs-bottom");
25246     }
25247     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25248     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25249     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25250     if(Roo.isIE){
25251         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25252     }
25253     if(this.tabPosition != "bottom"){
25254         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25255          * @type Roo.Element
25256          */
25257         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25258         this.el.addClass("x-tabs-top");
25259     }
25260     this.items = [];
25261
25262     this.bodyEl.setStyle("position", "relative");
25263
25264     this.active = null;
25265     this.activateDelegate = this.activate.createDelegate(this);
25266
25267     this.addEvents({
25268         /**
25269          * @event tabchange
25270          * Fires when the active tab changes
25271          * @param {Roo.TabPanel} this
25272          * @param {Roo.TabPanelItem} activePanel The new active tab
25273          */
25274         "tabchange": true,
25275         /**
25276          * @event beforetabchange
25277          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25278          * @param {Roo.TabPanel} this
25279          * @param {Object} e Set cancel to true on this object to cancel the tab change
25280          * @param {Roo.TabPanelItem} tab The tab being changed to
25281          */
25282         "beforetabchange" : true
25283     });
25284
25285     Roo.EventManager.onWindowResize(this.onResize, this);
25286     this.cpad = this.el.getPadding("lr");
25287     this.hiddenCount = 0;
25288
25289
25290     // toolbar on the tabbar support...
25291     if (this.toolbar) {
25292         var tcfg = this.toolbar;
25293         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25294         this.toolbar = new Roo.Toolbar(tcfg);
25295         if (Roo.isSafari) {
25296             var tbl = tcfg.container.child('table', true);
25297             tbl.setAttribute('width', '100%');
25298         }
25299         
25300     }
25301    
25302
25303
25304     Roo.TabPanel.superclass.constructor.call(this);
25305 };
25306
25307 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25308     /*
25309      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25310      */
25311     tabPosition : "top",
25312     /*
25313      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25314      */
25315     currentTabWidth : 0,
25316     /*
25317      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25318      */
25319     minTabWidth : 40,
25320     /*
25321      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25322      */
25323     maxTabWidth : 250,
25324     /*
25325      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25326      */
25327     preferredTabWidth : 175,
25328     /*
25329      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25330      */
25331     resizeTabs : false,
25332     /*
25333      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25334      */
25335     monitorResize : true,
25336     /*
25337      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25338      */
25339     toolbar : false,
25340
25341     /**
25342      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25343      * @param {String} id The id of the div to use <b>or create</b>
25344      * @param {String} text The text for the tab
25345      * @param {String} content (optional) Content to put in the TabPanelItem body
25346      * @param {Boolean} closable (optional) True to create a close icon on the tab
25347      * @return {Roo.TabPanelItem} The created TabPanelItem
25348      */
25349     addTab : function(id, text, content, closable){
25350         var item = new Roo.TabPanelItem(this, id, text, closable);
25351         this.addTabItem(item);
25352         if(content){
25353             item.setContent(content);
25354         }
25355         return item;
25356     },
25357
25358     /**
25359      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25360      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25361      * @return {Roo.TabPanelItem}
25362      */
25363     getTab : function(id){
25364         return this.items[id];
25365     },
25366
25367     /**
25368      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25369      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25370      */
25371     hideTab : function(id){
25372         var t = this.items[id];
25373         if(!t.isHidden()){
25374            t.setHidden(true);
25375            this.hiddenCount++;
25376            this.autoSizeTabs();
25377         }
25378     },
25379
25380     /**
25381      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25382      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25383      */
25384     unhideTab : function(id){
25385         var t = this.items[id];
25386         if(t.isHidden()){
25387            t.setHidden(false);
25388            this.hiddenCount--;
25389            this.autoSizeTabs();
25390         }
25391     },
25392
25393     /**
25394      * Adds an existing {@link Roo.TabPanelItem}.
25395      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25396      */
25397     addTabItem : function(item){
25398         this.items[item.id] = item;
25399         this.items.push(item);
25400         if(this.resizeTabs){
25401            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25402            this.autoSizeTabs();
25403         }else{
25404             item.autoSize();
25405         }
25406     },
25407
25408     /**
25409      * Removes a {@link Roo.TabPanelItem}.
25410      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25411      */
25412     removeTab : function(id){
25413         var items = this.items;
25414         var tab = items[id];
25415         if(!tab) { return; }
25416         var index = items.indexOf(tab);
25417         if(this.active == tab && items.length > 1){
25418             var newTab = this.getNextAvailable(index);
25419             if(newTab) {
25420                 newTab.activate();
25421             }
25422         }
25423         this.stripEl.dom.removeChild(tab.pnode.dom);
25424         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25425             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25426         }
25427         items.splice(index, 1);
25428         delete this.items[tab.id];
25429         tab.fireEvent("close", tab);
25430         tab.purgeListeners();
25431         this.autoSizeTabs();
25432     },
25433
25434     getNextAvailable : function(start){
25435         var items = this.items;
25436         var index = start;
25437         // look for a next tab that will slide over to
25438         // replace the one being removed
25439         while(index < items.length){
25440             var item = items[++index];
25441             if(item && !item.isHidden()){
25442                 return item;
25443             }
25444         }
25445         // if one isn't found select the previous tab (on the left)
25446         index = start;
25447         while(index >= 0){
25448             var item = items[--index];
25449             if(item && !item.isHidden()){
25450                 return item;
25451             }
25452         }
25453         return null;
25454     },
25455
25456     /**
25457      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25458      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25459      */
25460     disableTab : function(id){
25461         var tab = this.items[id];
25462         if(tab && this.active != tab){
25463             tab.disable();
25464         }
25465     },
25466
25467     /**
25468      * Enables a {@link Roo.TabPanelItem} that is disabled.
25469      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25470      */
25471     enableTab : function(id){
25472         var tab = this.items[id];
25473         tab.enable();
25474     },
25475
25476     /**
25477      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25478      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25479      * @return {Roo.TabPanelItem} The TabPanelItem.
25480      */
25481     activate : function(id){
25482         var tab = this.items[id];
25483         if(!tab){
25484             return null;
25485         }
25486         if(tab == this.active || tab.disabled){
25487             return tab;
25488         }
25489         var e = {};
25490         this.fireEvent("beforetabchange", this, e, tab);
25491         if(e.cancel !== true && !tab.disabled){
25492             if(this.active){
25493                 this.active.hide();
25494             }
25495             this.active = this.items[id];
25496             this.active.show();
25497             this.fireEvent("tabchange", this, this.active);
25498         }
25499         return tab;
25500     },
25501
25502     /**
25503      * Gets the active {@link Roo.TabPanelItem}.
25504      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25505      */
25506     getActiveTab : function(){
25507         return this.active;
25508     },
25509
25510     /**
25511      * Updates the tab body element to fit the height of the container element
25512      * for overflow scrolling
25513      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25514      */
25515     syncHeight : function(targetHeight){
25516         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25517         var bm = this.bodyEl.getMargins();
25518         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25519         this.bodyEl.setHeight(newHeight);
25520         return newHeight;
25521     },
25522
25523     onResize : function(){
25524         if(this.monitorResize){
25525             this.autoSizeTabs();
25526         }
25527     },
25528
25529     /**
25530      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25531      */
25532     beginUpdate : function(){
25533         this.updating = true;
25534     },
25535
25536     /**
25537      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25538      */
25539     endUpdate : function(){
25540         this.updating = false;
25541         this.autoSizeTabs();
25542     },
25543
25544     /**
25545      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25546      */
25547     autoSizeTabs : function(){
25548         var count = this.items.length;
25549         var vcount = count - this.hiddenCount;
25550         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25551         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25552         var availWidth = Math.floor(w / vcount);
25553         var b = this.stripBody;
25554         if(b.getWidth() > w){
25555             var tabs = this.items;
25556             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25557             if(availWidth < this.minTabWidth){
25558                 /*if(!this.sleft){    // incomplete scrolling code
25559                     this.createScrollButtons();
25560                 }
25561                 this.showScroll();
25562                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25563             }
25564         }else{
25565             if(this.currentTabWidth < this.preferredTabWidth){
25566                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25567             }
25568         }
25569     },
25570
25571     /**
25572      * Returns the number of tabs in this TabPanel.
25573      * @return {Number}
25574      */
25575      getCount : function(){
25576          return this.items.length;
25577      },
25578
25579     /**
25580      * Resizes all the tabs to the passed width
25581      * @param {Number} The new width
25582      */
25583     setTabWidth : function(width){
25584         this.currentTabWidth = width;
25585         for(var i = 0, len = this.items.length; i < len; i++) {
25586                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25587         }
25588     },
25589
25590     /**
25591      * Destroys this TabPanel
25592      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25593      */
25594     destroy : function(removeEl){
25595         Roo.EventManager.removeResizeListener(this.onResize, this);
25596         for(var i = 0, len = this.items.length; i < len; i++){
25597             this.items[i].purgeListeners();
25598         }
25599         if(removeEl === true){
25600             this.el.update("");
25601             this.el.remove();
25602         }
25603     }
25604 });
25605
25606 /**
25607  * @class Roo.TabPanelItem
25608  * @extends Roo.util.Observable
25609  * Represents an individual item (tab plus body) in a TabPanel.
25610  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25611  * @param {String} id The id of this TabPanelItem
25612  * @param {String} text The text for the tab of this TabPanelItem
25613  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25614  */
25615 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25616     /**
25617      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25618      * @type Roo.TabPanel
25619      */
25620     this.tabPanel = tabPanel;
25621     /**
25622      * The id for this TabPanelItem
25623      * @type String
25624      */
25625     this.id = id;
25626     /** @private */
25627     this.disabled = false;
25628     /** @private */
25629     this.text = text;
25630     /** @private */
25631     this.loaded = false;
25632     this.closable = closable;
25633
25634     /**
25635      * The body element for this TabPanelItem.
25636      * @type Roo.Element
25637      */
25638     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25639     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25640     this.bodyEl.setStyle("display", "block");
25641     this.bodyEl.setStyle("zoom", "1");
25642     this.hideAction();
25643
25644     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25645     /** @private */
25646     this.el = Roo.get(els.el, true);
25647     this.inner = Roo.get(els.inner, true);
25648     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25649     this.pnode = Roo.get(els.el.parentNode, true);
25650     this.el.on("mousedown", this.onTabMouseDown, this);
25651     this.el.on("click", this.onTabClick, this);
25652     /** @private */
25653     if(closable){
25654         var c = Roo.get(els.close, true);
25655         c.dom.title = this.closeText;
25656         c.addClassOnOver("close-over");
25657         c.on("click", this.closeClick, this);
25658      }
25659
25660     this.addEvents({
25661          /**
25662          * @event activate
25663          * Fires when this tab becomes the active tab.
25664          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25665          * @param {Roo.TabPanelItem} this
25666          */
25667         "activate": true,
25668         /**
25669          * @event beforeclose
25670          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25671          * @param {Roo.TabPanelItem} this
25672          * @param {Object} e Set cancel to true on this object to cancel the close.
25673          */
25674         "beforeclose": true,
25675         /**
25676          * @event close
25677          * Fires when this tab is closed.
25678          * @param {Roo.TabPanelItem} this
25679          */
25680          "close": true,
25681         /**
25682          * @event deactivate
25683          * Fires when this tab is no longer the active tab.
25684          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25685          * @param {Roo.TabPanelItem} this
25686          */
25687          "deactivate" : true
25688     });
25689     this.hidden = false;
25690
25691     Roo.TabPanelItem.superclass.constructor.call(this);
25692 };
25693
25694 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25695     purgeListeners : function(){
25696        Roo.util.Observable.prototype.purgeListeners.call(this);
25697        this.el.removeAllListeners();
25698     },
25699     /**
25700      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25701      */
25702     show : function(){
25703         this.pnode.addClass("on");
25704         this.showAction();
25705         if(Roo.isOpera){
25706             this.tabPanel.stripWrap.repaint();
25707         }
25708         this.fireEvent("activate", this.tabPanel, this);
25709     },
25710
25711     /**
25712      * Returns true if this tab is the active tab.
25713      * @return {Boolean}
25714      */
25715     isActive : function(){
25716         return this.tabPanel.getActiveTab() == this;
25717     },
25718
25719     /**
25720      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25721      */
25722     hide : function(){
25723         this.pnode.removeClass("on");
25724         this.hideAction();
25725         this.fireEvent("deactivate", this.tabPanel, this);
25726     },
25727
25728     hideAction : function(){
25729         this.bodyEl.hide();
25730         this.bodyEl.setStyle("position", "absolute");
25731         this.bodyEl.setLeft("-20000px");
25732         this.bodyEl.setTop("-20000px");
25733     },
25734
25735     showAction : function(){
25736         this.bodyEl.setStyle("position", "relative");
25737         this.bodyEl.setTop("");
25738         this.bodyEl.setLeft("");
25739         this.bodyEl.show();
25740     },
25741
25742     /**
25743      * Set the tooltip for the tab.
25744      * @param {String} tooltip The tab's tooltip
25745      */
25746     setTooltip : function(text){
25747         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25748             this.textEl.dom.qtip = text;
25749             this.textEl.dom.removeAttribute('title');
25750         }else{
25751             this.textEl.dom.title = text;
25752         }
25753     },
25754
25755     onTabClick : function(e){
25756         e.preventDefault();
25757         this.tabPanel.activate(this.id);
25758     },
25759
25760     onTabMouseDown : function(e){
25761         e.preventDefault();
25762         this.tabPanel.activate(this.id);
25763     },
25764
25765     getWidth : function(){
25766         return this.inner.getWidth();
25767     },
25768
25769     setWidth : function(width){
25770         var iwidth = width - this.pnode.getPadding("lr");
25771         this.inner.setWidth(iwidth);
25772         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25773         this.pnode.setWidth(width);
25774     },
25775
25776     /**
25777      * Show or hide the tab
25778      * @param {Boolean} hidden True to hide or false to show.
25779      */
25780     setHidden : function(hidden){
25781         this.hidden = hidden;
25782         this.pnode.setStyle("display", hidden ? "none" : "");
25783     },
25784
25785     /**
25786      * Returns true if this tab is "hidden"
25787      * @return {Boolean}
25788      */
25789     isHidden : function(){
25790         return this.hidden;
25791     },
25792
25793     /**
25794      * Returns the text for this tab
25795      * @return {String}
25796      */
25797     getText : function(){
25798         return this.text;
25799     },
25800
25801     autoSize : function(){
25802         //this.el.beginMeasure();
25803         this.textEl.setWidth(1);
25804         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25805         //this.el.endMeasure();
25806     },
25807
25808     /**
25809      * Sets the text for the tab (Note: this also sets the tooltip text)
25810      * @param {String} text The tab's text and tooltip
25811      */
25812     setText : function(text){
25813         this.text = text;
25814         this.textEl.update(text);
25815         this.setTooltip(text);
25816         if(!this.tabPanel.resizeTabs){
25817             this.autoSize();
25818         }
25819     },
25820     /**
25821      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25822      */
25823     activate : function(){
25824         this.tabPanel.activate(this.id);
25825     },
25826
25827     /**
25828      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25829      */
25830     disable : function(){
25831         if(this.tabPanel.active != this){
25832             this.disabled = true;
25833             this.pnode.addClass("disabled");
25834         }
25835     },
25836
25837     /**
25838      * Enables this TabPanelItem if it was previously disabled.
25839      */
25840     enable : function(){
25841         this.disabled = false;
25842         this.pnode.removeClass("disabled");
25843     },
25844
25845     /**
25846      * Sets the content for this TabPanelItem.
25847      * @param {String} content The content
25848      * @param {Boolean} loadScripts true to look for and load scripts
25849      */
25850     setContent : function(content, loadScripts){
25851         this.bodyEl.update(content, loadScripts);
25852     },
25853
25854     /**
25855      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25856      * @return {Roo.UpdateManager} The UpdateManager
25857      */
25858     getUpdateManager : function(){
25859         return this.bodyEl.getUpdateManager();
25860     },
25861
25862     /**
25863      * Set a URL to be used to load the content for this TabPanelItem.
25864      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25865      * @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)
25866      * @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)
25867      * @return {Roo.UpdateManager} The UpdateManager
25868      */
25869     setUrl : function(url, params, loadOnce){
25870         if(this.refreshDelegate){
25871             this.un('activate', this.refreshDelegate);
25872         }
25873         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25874         this.on("activate", this.refreshDelegate);
25875         return this.bodyEl.getUpdateManager();
25876     },
25877
25878     /** @private */
25879     _handleRefresh : function(url, params, loadOnce){
25880         if(!loadOnce || !this.loaded){
25881             var updater = this.bodyEl.getUpdateManager();
25882             updater.update(url, params, this._setLoaded.createDelegate(this));
25883         }
25884     },
25885
25886     /**
25887      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25888      *   Will fail silently if the setUrl method has not been called.
25889      *   This does not activate the panel, just updates its content.
25890      */
25891     refresh : function(){
25892         if(this.refreshDelegate){
25893            this.loaded = false;
25894            this.refreshDelegate();
25895         }
25896     },
25897
25898     /** @private */
25899     _setLoaded : function(){
25900         this.loaded = true;
25901     },
25902
25903     /** @private */
25904     closeClick : function(e){
25905         var o = {};
25906         e.stopEvent();
25907         this.fireEvent("beforeclose", this, o);
25908         if(o.cancel !== true){
25909             this.tabPanel.removeTab(this.id);
25910         }
25911     },
25912     /**
25913      * The text displayed in the tooltip for the close icon.
25914      * @type String
25915      */
25916     closeText : "Close this tab"
25917 });
25918
25919 /** @private */
25920 Roo.TabPanel.prototype.createStrip = function(container){
25921     var strip = document.createElement("div");
25922     strip.className = "x-tabs-wrap";
25923     container.appendChild(strip);
25924     return strip;
25925 };
25926 /** @private */
25927 Roo.TabPanel.prototype.createStripList = function(strip){
25928     // div wrapper for retard IE
25929     // returns the "tr" element.
25930     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25931         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25932         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25933     return strip.firstChild.firstChild.firstChild.firstChild;
25934 };
25935 /** @private */
25936 Roo.TabPanel.prototype.createBody = function(container){
25937     var body = document.createElement("div");
25938     Roo.id(body, "tab-body");
25939     Roo.fly(body).addClass("x-tabs-body");
25940     container.appendChild(body);
25941     return body;
25942 };
25943 /** @private */
25944 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25945     var body = Roo.getDom(id);
25946     if(!body){
25947         body = document.createElement("div");
25948         body.id = id;
25949     }
25950     Roo.fly(body).addClass("x-tabs-item-body");
25951     bodyEl.insertBefore(body, bodyEl.firstChild);
25952     return body;
25953 };
25954 /** @private */
25955 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25956     var td = document.createElement("td");
25957     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25958     //stripEl.appendChild(td);
25959     if(closable){
25960         td.className = "x-tabs-closable";
25961         if(!this.closeTpl){
25962             this.closeTpl = new Roo.Template(
25963                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25964                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25965                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25966             );
25967         }
25968         var el = this.closeTpl.overwrite(td, {"text": text});
25969         var close = el.getElementsByTagName("div")[0];
25970         var inner = el.getElementsByTagName("em")[0];
25971         return {"el": el, "close": close, "inner": inner};
25972     } else {
25973         if(!this.tabTpl){
25974             this.tabTpl = new Roo.Template(
25975                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25976                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25977             );
25978         }
25979         var el = this.tabTpl.overwrite(td, {"text": text});
25980         var inner = el.getElementsByTagName("em")[0];
25981         return {"el": el, "inner": inner};
25982     }
25983 };/*
25984  * Based on:
25985  * Ext JS Library 1.1.1
25986  * Copyright(c) 2006-2007, Ext JS, LLC.
25987  *
25988  * Originally Released Under LGPL - original licence link has changed is not relivant.
25989  *
25990  * Fork - LGPL
25991  * <script type="text/javascript">
25992  */
25993
25994 /**
25995  * @class Roo.Button
25996  * @extends Roo.util.Observable
25997  * Simple Button class
25998  * @cfg {String} text The button text
25999  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26000  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26001  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26002  * @cfg {Object} scope The scope of the handler
26003  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26004  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26005  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26006  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26007  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26008  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26009    applies if enableToggle = true)
26010  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26011  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26012   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26013  * @constructor
26014  * Create a new button
26015  * @param {Object} config The config object
26016  */
26017 Roo.Button = function(renderTo, config)
26018 {
26019     if (!config) {
26020         config = renderTo;
26021         renderTo = config.renderTo || false;
26022     }
26023     
26024     Roo.apply(this, config);
26025     this.addEvents({
26026         /**
26027              * @event click
26028              * Fires when this button is clicked
26029              * @param {Button} this
26030              * @param {EventObject} e The click event
26031              */
26032             "click" : true,
26033         /**
26034              * @event toggle
26035              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26036              * @param {Button} this
26037              * @param {Boolean} pressed
26038              */
26039             "toggle" : true,
26040         /**
26041              * @event mouseover
26042              * Fires when the mouse hovers over the button
26043              * @param {Button} this
26044              * @param {Event} e The event object
26045              */
26046         'mouseover' : true,
26047         /**
26048              * @event mouseout
26049              * Fires when the mouse exits the button
26050              * @param {Button} this
26051              * @param {Event} e The event object
26052              */
26053         'mouseout': true,
26054          /**
26055              * @event render
26056              * Fires when the button is rendered
26057              * @param {Button} this
26058              */
26059         'render': true
26060     });
26061     if(this.menu){
26062         this.menu = Roo.menu.MenuMgr.get(this.menu);
26063     }
26064     // register listeners first!!  - so render can be captured..
26065     Roo.util.Observable.call(this);
26066     if(renderTo){
26067         this.render(renderTo);
26068     }
26069     
26070   
26071 };
26072
26073 Roo.extend(Roo.Button, Roo.util.Observable, {
26074     /**
26075      * 
26076      */
26077     
26078     /**
26079      * Read-only. True if this button is hidden
26080      * @type Boolean
26081      */
26082     hidden : false,
26083     /**
26084      * Read-only. True if this button is disabled
26085      * @type Boolean
26086      */
26087     disabled : false,
26088     /**
26089      * Read-only. True if this button is pressed (only if enableToggle = true)
26090      * @type Boolean
26091      */
26092     pressed : false,
26093
26094     /**
26095      * @cfg {Number} tabIndex 
26096      * The DOM tabIndex for this button (defaults to undefined)
26097      */
26098     tabIndex : undefined,
26099
26100     /**
26101      * @cfg {Boolean} enableToggle
26102      * True to enable pressed/not pressed toggling (defaults to false)
26103      */
26104     enableToggle: false,
26105     /**
26106      * @cfg {Mixed} menu
26107      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26108      */
26109     menu : undefined,
26110     /**
26111      * @cfg {String} menuAlign
26112      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26113      */
26114     menuAlign : "tl-bl?",
26115
26116     /**
26117      * @cfg {String} iconCls
26118      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26119      */
26120     iconCls : undefined,
26121     /**
26122      * @cfg {String} type
26123      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26124      */
26125     type : 'button',
26126
26127     // private
26128     menuClassTarget: 'tr',
26129
26130     /**
26131      * @cfg {String} clickEvent
26132      * The type of event to map to the button's event handler (defaults to 'click')
26133      */
26134     clickEvent : 'click',
26135
26136     /**
26137      * @cfg {Boolean} handleMouseEvents
26138      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26139      */
26140     handleMouseEvents : true,
26141
26142     /**
26143      * @cfg {String} tooltipType
26144      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26145      */
26146     tooltipType : 'qtip',
26147
26148     /**
26149      * @cfg {String} cls
26150      * A CSS class to apply to the button's main element.
26151      */
26152     
26153     /**
26154      * @cfg {Roo.Template} template (Optional)
26155      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26156      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26157      * require code modifications if required elements (e.g. a button) aren't present.
26158      */
26159
26160     // private
26161     render : function(renderTo){
26162         var btn;
26163         if(this.hideParent){
26164             this.parentEl = Roo.get(renderTo);
26165         }
26166         if(!this.dhconfig){
26167             if(!this.template){
26168                 if(!Roo.Button.buttonTemplate){
26169                     // hideous table template
26170                     Roo.Button.buttonTemplate = new Roo.Template(
26171                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26172                         '<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>',
26173                         "</tr></tbody></table>");
26174                 }
26175                 this.template = Roo.Button.buttonTemplate;
26176             }
26177             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26178             var btnEl = btn.child("button:first");
26179             btnEl.on('focus', this.onFocus, this);
26180             btnEl.on('blur', this.onBlur, this);
26181             if(this.cls){
26182                 btn.addClass(this.cls);
26183             }
26184             if(this.icon){
26185                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26186             }
26187             if(this.iconCls){
26188                 btnEl.addClass(this.iconCls);
26189                 if(!this.cls){
26190                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26191                 }
26192             }
26193             if(this.tabIndex !== undefined){
26194                 btnEl.dom.tabIndex = this.tabIndex;
26195             }
26196             if(this.tooltip){
26197                 if(typeof this.tooltip == 'object'){
26198                     Roo.QuickTips.tips(Roo.apply({
26199                           target: btnEl.id
26200                     }, this.tooltip));
26201                 } else {
26202                     btnEl.dom[this.tooltipType] = this.tooltip;
26203                 }
26204             }
26205         }else{
26206             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26207         }
26208         this.el = btn;
26209         if(this.id){
26210             this.el.dom.id = this.el.id = this.id;
26211         }
26212         if(this.menu){
26213             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26214             this.menu.on("show", this.onMenuShow, this);
26215             this.menu.on("hide", this.onMenuHide, this);
26216         }
26217         btn.addClass("x-btn");
26218         if(Roo.isIE && !Roo.isIE7){
26219             this.autoWidth.defer(1, this);
26220         }else{
26221             this.autoWidth();
26222         }
26223         if(this.handleMouseEvents){
26224             btn.on("mouseover", this.onMouseOver, this);
26225             btn.on("mouseout", this.onMouseOut, this);
26226             btn.on("mousedown", this.onMouseDown, this);
26227         }
26228         btn.on(this.clickEvent, this.onClick, this);
26229         //btn.on("mouseup", this.onMouseUp, this);
26230         if(this.hidden){
26231             this.hide();
26232         }
26233         if(this.disabled){
26234             this.disable();
26235         }
26236         Roo.ButtonToggleMgr.register(this);
26237         if(this.pressed){
26238             this.el.addClass("x-btn-pressed");
26239         }
26240         if(this.repeat){
26241             var repeater = new Roo.util.ClickRepeater(btn,
26242                 typeof this.repeat == "object" ? this.repeat : {}
26243             );
26244             repeater.on("click", this.onClick,  this);
26245         }
26246         
26247         this.fireEvent('render', this);
26248         
26249     },
26250     /**
26251      * Returns the button's underlying element
26252      * @return {Roo.Element} The element
26253      */
26254     getEl : function(){
26255         return this.el;  
26256     },
26257     
26258     /**
26259      * Destroys this Button and removes any listeners.
26260      */
26261     destroy : function(){
26262         Roo.ButtonToggleMgr.unregister(this);
26263         this.el.removeAllListeners();
26264         this.purgeListeners();
26265         this.el.remove();
26266     },
26267
26268     // private
26269     autoWidth : function(){
26270         if(this.el){
26271             this.el.setWidth("auto");
26272             if(Roo.isIE7 && Roo.isStrict){
26273                 var ib = this.el.child('button');
26274                 if(ib && ib.getWidth() > 20){
26275                     ib.clip();
26276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26277                 }
26278             }
26279             if(this.minWidth){
26280                 if(this.hidden){
26281                     this.el.beginMeasure();
26282                 }
26283                 if(this.el.getWidth() < this.minWidth){
26284                     this.el.setWidth(this.minWidth);
26285                 }
26286                 if(this.hidden){
26287                     this.el.endMeasure();
26288                 }
26289             }
26290         }
26291     },
26292
26293     /**
26294      * Assigns this button's click handler
26295      * @param {Function} handler The function to call when the button is clicked
26296      * @param {Object} scope (optional) Scope for the function passed in
26297      */
26298     setHandler : function(handler, scope){
26299         this.handler = handler;
26300         this.scope = scope;  
26301     },
26302     
26303     /**
26304      * Sets this button's text
26305      * @param {String} text The button text
26306      */
26307     setText : function(text){
26308         this.text = text;
26309         if(this.el){
26310             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26311         }
26312         this.autoWidth();
26313     },
26314     
26315     /**
26316      * Gets the text for this button
26317      * @return {String} The button text
26318      */
26319     getText : function(){
26320         return this.text;  
26321     },
26322     
26323     /**
26324      * Show this button
26325      */
26326     show: function(){
26327         this.hidden = false;
26328         if(this.el){
26329             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26330         }
26331     },
26332     
26333     /**
26334      * Hide this button
26335      */
26336     hide: function(){
26337         this.hidden = true;
26338         if(this.el){
26339             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26340         }
26341     },
26342     
26343     /**
26344      * Convenience function for boolean show/hide
26345      * @param {Boolean} visible True to show, false to hide
26346      */
26347     setVisible: function(visible){
26348         if(visible) {
26349             this.show();
26350         }else{
26351             this.hide();
26352         }
26353     },
26354     
26355     /**
26356      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26357      * @param {Boolean} state (optional) Force a particular state
26358      */
26359     toggle : function(state){
26360         state = state === undefined ? !this.pressed : state;
26361         if(state != this.pressed){
26362             if(state){
26363                 this.el.addClass("x-btn-pressed");
26364                 this.pressed = true;
26365                 this.fireEvent("toggle", this, true);
26366             }else{
26367                 this.el.removeClass("x-btn-pressed");
26368                 this.pressed = false;
26369                 this.fireEvent("toggle", this, false);
26370             }
26371             if(this.toggleHandler){
26372                 this.toggleHandler.call(this.scope || this, this, state);
26373             }
26374         }
26375     },
26376     
26377     /**
26378      * Focus the button
26379      */
26380     focus : function(){
26381         this.el.child('button:first').focus();
26382     },
26383     
26384     /**
26385      * Disable this button
26386      */
26387     disable : function(){
26388         if(this.el){
26389             this.el.addClass("x-btn-disabled");
26390         }
26391         this.disabled = true;
26392     },
26393     
26394     /**
26395      * Enable this button
26396      */
26397     enable : function(){
26398         if(this.el){
26399             this.el.removeClass("x-btn-disabled");
26400         }
26401         this.disabled = false;
26402     },
26403
26404     /**
26405      * Convenience function for boolean enable/disable
26406      * @param {Boolean} enabled True to enable, false to disable
26407      */
26408     setDisabled : function(v){
26409         this[v !== true ? "enable" : "disable"]();
26410     },
26411
26412     // private
26413     onClick : function(e){
26414         if(e){
26415             e.preventDefault();
26416         }
26417         if(e.button != 0){
26418             return;
26419         }
26420         if(!this.disabled){
26421             if(this.enableToggle){
26422                 this.toggle();
26423             }
26424             if(this.menu && !this.menu.isVisible()){
26425                 this.menu.show(this.el, this.menuAlign);
26426             }
26427             this.fireEvent("click", this, e);
26428             if(this.handler){
26429                 this.el.removeClass("x-btn-over");
26430                 this.handler.call(this.scope || this, this, e);
26431             }
26432         }
26433     },
26434     // private
26435     onMouseOver : function(e){
26436         if(!this.disabled){
26437             this.el.addClass("x-btn-over");
26438             this.fireEvent('mouseover', this, e);
26439         }
26440     },
26441     // private
26442     onMouseOut : function(e){
26443         if(!e.within(this.el,  true)){
26444             this.el.removeClass("x-btn-over");
26445             this.fireEvent('mouseout', this, e);
26446         }
26447     },
26448     // private
26449     onFocus : function(e){
26450         if(!this.disabled){
26451             this.el.addClass("x-btn-focus");
26452         }
26453     },
26454     // private
26455     onBlur : function(e){
26456         this.el.removeClass("x-btn-focus");
26457     },
26458     // private
26459     onMouseDown : function(e){
26460         if(!this.disabled && e.button == 0){
26461             this.el.addClass("x-btn-click");
26462             Roo.get(document).on('mouseup', this.onMouseUp, this);
26463         }
26464     },
26465     // private
26466     onMouseUp : function(e){
26467         if(e.button == 0){
26468             this.el.removeClass("x-btn-click");
26469             Roo.get(document).un('mouseup', this.onMouseUp, this);
26470         }
26471     },
26472     // private
26473     onMenuShow : function(e){
26474         this.el.addClass("x-btn-menu-active");
26475     },
26476     // private
26477     onMenuHide : function(e){
26478         this.el.removeClass("x-btn-menu-active");
26479     }   
26480 });
26481
26482 // Private utility class used by Button
26483 Roo.ButtonToggleMgr = function(){
26484    var groups = {};
26485    
26486    function toggleGroup(btn, state){
26487        if(state){
26488            var g = groups[btn.toggleGroup];
26489            for(var i = 0, l = g.length; i < l; i++){
26490                if(g[i] != btn){
26491                    g[i].toggle(false);
26492                }
26493            }
26494        }
26495    }
26496    
26497    return {
26498        register : function(btn){
26499            if(!btn.toggleGroup){
26500                return;
26501            }
26502            var g = groups[btn.toggleGroup];
26503            if(!g){
26504                g = groups[btn.toggleGroup] = [];
26505            }
26506            g.push(btn);
26507            btn.on("toggle", toggleGroup);
26508        },
26509        
26510        unregister : function(btn){
26511            if(!btn.toggleGroup){
26512                return;
26513            }
26514            var g = groups[btn.toggleGroup];
26515            if(g){
26516                g.remove(btn);
26517                btn.un("toggle", toggleGroup);
26518            }
26519        }
26520    };
26521 }();/*
26522  * Based on:
26523  * Ext JS Library 1.1.1
26524  * Copyright(c) 2006-2007, Ext JS, LLC.
26525  *
26526  * Originally Released Under LGPL - original licence link has changed is not relivant.
26527  *
26528  * Fork - LGPL
26529  * <script type="text/javascript">
26530  */
26531  
26532 /**
26533  * @class Roo.SplitButton
26534  * @extends Roo.Button
26535  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26536  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26537  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26538  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26539  * @cfg {String} arrowTooltip The title attribute of the arrow
26540  * @constructor
26541  * Create a new menu button
26542  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26543  * @param {Object} config The config object
26544  */
26545 Roo.SplitButton = function(renderTo, config){
26546     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26547     /**
26548      * @event arrowclick
26549      * Fires when this button's arrow is clicked
26550      * @param {SplitButton} this
26551      * @param {EventObject} e The click event
26552      */
26553     this.addEvents({"arrowclick":true});
26554 };
26555
26556 Roo.extend(Roo.SplitButton, Roo.Button, {
26557     render : function(renderTo){
26558         // this is one sweet looking template!
26559         var tpl = new Roo.Template(
26560             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26561             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26562             '<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>',
26563             "</tbody></table></td><td>",
26564             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26565             '<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>',
26566             "</tbody></table></td></tr></table>"
26567         );
26568         var btn = tpl.append(renderTo, [this.text, this.type], true);
26569         var btnEl = btn.child("button");
26570         if(this.cls){
26571             btn.addClass(this.cls);
26572         }
26573         if(this.icon){
26574             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26575         }
26576         if(this.iconCls){
26577             btnEl.addClass(this.iconCls);
26578             if(!this.cls){
26579                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26580             }
26581         }
26582         this.el = btn;
26583         if(this.handleMouseEvents){
26584             btn.on("mouseover", this.onMouseOver, this);
26585             btn.on("mouseout", this.onMouseOut, this);
26586             btn.on("mousedown", this.onMouseDown, this);
26587             btn.on("mouseup", this.onMouseUp, this);
26588         }
26589         btn.on(this.clickEvent, this.onClick, this);
26590         if(this.tooltip){
26591             if(typeof this.tooltip == 'object'){
26592                 Roo.QuickTips.tips(Roo.apply({
26593                       target: btnEl.id
26594                 }, this.tooltip));
26595             } else {
26596                 btnEl.dom[this.tooltipType] = this.tooltip;
26597             }
26598         }
26599         if(this.arrowTooltip){
26600             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26601         }
26602         if(this.hidden){
26603             this.hide();
26604         }
26605         if(this.disabled){
26606             this.disable();
26607         }
26608         if(this.pressed){
26609             this.el.addClass("x-btn-pressed");
26610         }
26611         if(Roo.isIE && !Roo.isIE7){
26612             this.autoWidth.defer(1, this);
26613         }else{
26614             this.autoWidth();
26615         }
26616         if(this.menu){
26617             this.menu.on("show", this.onMenuShow, this);
26618             this.menu.on("hide", this.onMenuHide, this);
26619         }
26620         this.fireEvent('render', this);
26621     },
26622
26623     // private
26624     autoWidth : function(){
26625         if(this.el){
26626             var tbl = this.el.child("table:first");
26627             var tbl2 = this.el.child("table:last");
26628             this.el.setWidth("auto");
26629             tbl.setWidth("auto");
26630             if(Roo.isIE7 && Roo.isStrict){
26631                 var ib = this.el.child('button:first');
26632                 if(ib && ib.getWidth() > 20){
26633                     ib.clip();
26634                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26635                 }
26636             }
26637             if(this.minWidth){
26638                 if(this.hidden){
26639                     this.el.beginMeasure();
26640                 }
26641                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26642                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26643                 }
26644                 if(this.hidden){
26645                     this.el.endMeasure();
26646                 }
26647             }
26648             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26649         } 
26650     },
26651     /**
26652      * Sets this button's click handler
26653      * @param {Function} handler The function to call when the button is clicked
26654      * @param {Object} scope (optional) Scope for the function passed above
26655      */
26656     setHandler : function(handler, scope){
26657         this.handler = handler;
26658         this.scope = scope;  
26659     },
26660     
26661     /**
26662      * Sets this button's arrow click handler
26663      * @param {Function} handler The function to call when the arrow is clicked
26664      * @param {Object} scope (optional) Scope for the function passed above
26665      */
26666     setArrowHandler : function(handler, scope){
26667         this.arrowHandler = handler;
26668         this.scope = scope;  
26669     },
26670     
26671     /**
26672      * Focus the button
26673      */
26674     focus : function(){
26675         if(this.el){
26676             this.el.child("button:first").focus();
26677         }
26678     },
26679
26680     // private
26681     onClick : function(e){
26682         e.preventDefault();
26683         if(!this.disabled){
26684             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26685                 if(this.menu && !this.menu.isVisible()){
26686                     this.menu.show(this.el, this.menuAlign);
26687                 }
26688                 this.fireEvent("arrowclick", this, e);
26689                 if(this.arrowHandler){
26690                     this.arrowHandler.call(this.scope || this, this, e);
26691                 }
26692             }else{
26693                 this.fireEvent("click", this, e);
26694                 if(this.handler){
26695                     this.handler.call(this.scope || this, this, e);
26696                 }
26697             }
26698         }
26699     },
26700     // private
26701     onMouseDown : function(e){
26702         if(!this.disabled){
26703             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26704         }
26705     },
26706     // private
26707     onMouseUp : function(e){
26708         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26709     }   
26710 });
26711
26712
26713 // backwards compat
26714 Roo.MenuButton = Roo.SplitButton;/*
26715  * Based on:
26716  * Ext JS Library 1.1.1
26717  * Copyright(c) 2006-2007, Ext JS, LLC.
26718  *
26719  * Originally Released Under LGPL - original licence link has changed is not relivant.
26720  *
26721  * Fork - LGPL
26722  * <script type="text/javascript">
26723  */
26724
26725 /**
26726  * @class Roo.Toolbar
26727  * Basic Toolbar class.
26728  * @constructor
26729  * Creates a new Toolbar
26730  * @param {Object} container The config object
26731  */ 
26732 Roo.Toolbar = function(container, buttons, config)
26733 {
26734     /// old consturctor format still supported..
26735     if(container instanceof Array){ // omit the container for later rendering
26736         buttons = container;
26737         config = buttons;
26738         container = null;
26739     }
26740     if (typeof(container) == 'object' && container.xtype) {
26741         config = container;
26742         container = config.container;
26743         buttons = config.buttons || []; // not really - use items!!
26744     }
26745     var xitems = [];
26746     if (config && config.items) {
26747         xitems = config.items;
26748         delete config.items;
26749     }
26750     Roo.apply(this, config);
26751     this.buttons = buttons;
26752     
26753     if(container){
26754         this.render(container);
26755     }
26756     this.xitems = xitems;
26757     Roo.each(xitems, function(b) {
26758         this.add(b);
26759     }, this);
26760     
26761 };
26762
26763 Roo.Toolbar.prototype = {
26764     /**
26765      * @cfg {Array} items
26766      * array of button configs or elements to add (will be converted to a MixedCollection)
26767      */
26768     
26769     /**
26770      * @cfg {String/HTMLElement/Element} container
26771      * The id or element that will contain the toolbar
26772      */
26773     // private
26774     render : function(ct){
26775         this.el = Roo.get(ct);
26776         if(this.cls){
26777             this.el.addClass(this.cls);
26778         }
26779         // using a table allows for vertical alignment
26780         // 100% width is needed by Safari...
26781         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26782         this.tr = this.el.child("tr", true);
26783         var autoId = 0;
26784         this.items = new Roo.util.MixedCollection(false, function(o){
26785             return o.id || ("item" + (++autoId));
26786         });
26787         if(this.buttons){
26788             this.add.apply(this, this.buttons);
26789             delete this.buttons;
26790         }
26791     },
26792
26793     /**
26794      * Adds element(s) to the toolbar -- this function takes a variable number of 
26795      * arguments of mixed type and adds them to the toolbar.
26796      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26797      * <ul>
26798      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26799      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26800      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26801      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26802      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26803      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26804      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26805      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26806      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26807      * </ul>
26808      * @param {Mixed} arg2
26809      * @param {Mixed} etc.
26810      */
26811     add : function(){
26812         var a = arguments, l = a.length;
26813         for(var i = 0; i < l; i++){
26814             this._add(a[i]);
26815         }
26816     },
26817     // private..
26818     _add : function(el) {
26819         
26820         if (el.xtype) {
26821             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26822         }
26823         
26824         if (el.applyTo){ // some kind of form field
26825             return this.addField(el);
26826         } 
26827         if (el.render){ // some kind of Toolbar.Item
26828             return this.addItem(el);
26829         }
26830         if (typeof el == "string"){ // string
26831             if(el == "separator" || el == "-"){
26832                 return this.addSeparator();
26833             }
26834             if (el == " "){
26835                 return this.addSpacer();
26836             }
26837             if(el == "->"){
26838                 return this.addFill();
26839             }
26840             return this.addText(el);
26841             
26842         }
26843         if(el.tagName){ // element
26844             return this.addElement(el);
26845         }
26846         if(typeof el == "object"){ // must be button config?
26847             return this.addButton(el);
26848         }
26849         // and now what?!?!
26850         return false;
26851         
26852     },
26853     
26854     /**
26855      * Add an Xtype element
26856      * @param {Object} xtype Xtype Object
26857      * @return {Object} created Object
26858      */
26859     addxtype : function(e){
26860         return this.add(e);  
26861     },
26862     
26863     /**
26864      * Returns the Element for this toolbar.
26865      * @return {Roo.Element}
26866      */
26867     getEl : function(){
26868         return this.el;  
26869     },
26870     
26871     /**
26872      * Adds a separator
26873      * @return {Roo.Toolbar.Item} The separator item
26874      */
26875     addSeparator : function(){
26876         return this.addItem(new Roo.Toolbar.Separator());
26877     },
26878
26879     /**
26880      * Adds a spacer element
26881      * @return {Roo.Toolbar.Spacer} The spacer item
26882      */
26883     addSpacer : function(){
26884         return this.addItem(new Roo.Toolbar.Spacer());
26885     },
26886
26887     /**
26888      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26889      * @return {Roo.Toolbar.Fill} The fill item
26890      */
26891     addFill : function(){
26892         return this.addItem(new Roo.Toolbar.Fill());
26893     },
26894
26895     /**
26896      * Adds any standard HTML element to the toolbar
26897      * @param {String/HTMLElement/Element} el The element or id of the element to add
26898      * @return {Roo.Toolbar.Item} The element's item
26899      */
26900     addElement : function(el){
26901         return this.addItem(new Roo.Toolbar.Item(el));
26902     },
26903     /**
26904      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26905      * @type Roo.util.MixedCollection  
26906      */
26907     items : false,
26908      
26909     /**
26910      * Adds any Toolbar.Item or subclass
26911      * @param {Roo.Toolbar.Item} item
26912      * @return {Roo.Toolbar.Item} The item
26913      */
26914     addItem : function(item){
26915         var td = this.nextBlock();
26916         item.render(td);
26917         this.items.add(item);
26918         return item;
26919     },
26920     
26921     /**
26922      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26923      * @param {Object/Array} config A button config or array of configs
26924      * @return {Roo.Toolbar.Button/Array}
26925      */
26926     addButton : function(config){
26927         if(config instanceof Array){
26928             var buttons = [];
26929             for(var i = 0, len = config.length; i < len; i++) {
26930                 buttons.push(this.addButton(config[i]));
26931             }
26932             return buttons;
26933         }
26934         var b = config;
26935         if(!(config instanceof Roo.Toolbar.Button)){
26936             b = config.split ?
26937                 new Roo.Toolbar.SplitButton(config) :
26938                 new Roo.Toolbar.Button(config);
26939         }
26940         var td = this.nextBlock();
26941         b.render(td);
26942         this.items.add(b);
26943         return b;
26944     },
26945     
26946     /**
26947      * Adds text to the toolbar
26948      * @param {String} text The text to add
26949      * @return {Roo.Toolbar.Item} The element's item
26950      */
26951     addText : function(text){
26952         return this.addItem(new Roo.Toolbar.TextItem(text));
26953     },
26954     
26955     /**
26956      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26957      * @param {Number} index The index where the item is to be inserted
26958      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26959      * @return {Roo.Toolbar.Button/Item}
26960      */
26961     insertButton : function(index, item){
26962         if(item instanceof Array){
26963             var buttons = [];
26964             for(var i = 0, len = item.length; i < len; i++) {
26965                buttons.push(this.insertButton(index + i, item[i]));
26966             }
26967             return buttons;
26968         }
26969         if (!(item instanceof Roo.Toolbar.Button)){
26970            item = new Roo.Toolbar.Button(item);
26971         }
26972         var td = document.createElement("td");
26973         this.tr.insertBefore(td, this.tr.childNodes[index]);
26974         item.render(td);
26975         this.items.insert(index, item);
26976         return item;
26977     },
26978     
26979     /**
26980      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26981      * @param {Object} config
26982      * @return {Roo.Toolbar.Item} The element's item
26983      */
26984     addDom : function(config, returnEl){
26985         var td = this.nextBlock();
26986         Roo.DomHelper.overwrite(td, config);
26987         var ti = new Roo.Toolbar.Item(td.firstChild);
26988         ti.render(td);
26989         this.items.add(ti);
26990         return ti;
26991     },
26992
26993     /**
26994      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26995      * @type Roo.util.MixedCollection  
26996      */
26997     fields : false,
26998     
26999     /**
27000      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27001      * Note: the field should not have been rendered yet. For a field that has already been
27002      * rendered, use {@link #addElement}.
27003      * @param {Roo.form.Field} field
27004      * @return {Roo.ToolbarItem}
27005      */
27006      
27007       
27008     addField : function(field) {
27009         if (!this.fields) {
27010             var autoId = 0;
27011             this.fields = new Roo.util.MixedCollection(false, function(o){
27012                 return o.id || ("item" + (++autoId));
27013             });
27014
27015         }
27016         
27017         var td = this.nextBlock();
27018         field.render(td);
27019         var ti = new Roo.Toolbar.Item(td.firstChild);
27020         ti.render(td);
27021         this.items.add(ti);
27022         this.fields.add(field);
27023         return ti;
27024     },
27025     /**
27026      * Hide the toolbar
27027      * @method hide
27028      */
27029      
27030       
27031     hide : function()
27032     {
27033         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27034         this.el.child('div').hide();
27035     },
27036     /**
27037      * Show the toolbar
27038      * @method show
27039      */
27040     show : function()
27041     {
27042         this.el.child('div').show();
27043     },
27044       
27045     // private
27046     nextBlock : function(){
27047         var td = document.createElement("td");
27048         this.tr.appendChild(td);
27049         return td;
27050     },
27051
27052     // private
27053     destroy : function(){
27054         if(this.items){ // rendered?
27055             Roo.destroy.apply(Roo, this.items.items);
27056         }
27057         if(this.fields){ // rendered?
27058             Roo.destroy.apply(Roo, this.fields.items);
27059         }
27060         Roo.Element.uncache(this.el, this.tr);
27061     }
27062 };
27063
27064 /**
27065  * @class Roo.Toolbar.Item
27066  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27067  * @constructor
27068  * Creates a new Item
27069  * @param {HTMLElement} el 
27070  */
27071 Roo.Toolbar.Item = function(el){
27072     this.el = Roo.getDom(el);
27073     this.id = Roo.id(this.el);
27074     this.hidden = false;
27075 };
27076
27077 Roo.Toolbar.Item.prototype = {
27078     
27079     /**
27080      * Get this item's HTML Element
27081      * @return {HTMLElement}
27082      */
27083     getEl : function(){
27084        return this.el;  
27085     },
27086
27087     // private
27088     render : function(td){
27089         this.td = td;
27090         td.appendChild(this.el);
27091     },
27092     
27093     /**
27094      * Removes and destroys this item.
27095      */
27096     destroy : function(){
27097         this.td.parentNode.removeChild(this.td);
27098     },
27099     
27100     /**
27101      * Shows this item.
27102      */
27103     show: function(){
27104         this.hidden = false;
27105         this.td.style.display = "";
27106     },
27107     
27108     /**
27109      * Hides this item.
27110      */
27111     hide: function(){
27112         this.hidden = true;
27113         this.td.style.display = "none";
27114     },
27115     
27116     /**
27117      * Convenience function for boolean show/hide.
27118      * @param {Boolean} visible true to show/false to hide
27119      */
27120     setVisible: function(visible){
27121         if(visible) {
27122             this.show();
27123         }else{
27124             this.hide();
27125         }
27126     },
27127     
27128     /**
27129      * Try to focus this item.
27130      */
27131     focus : function(){
27132         Roo.fly(this.el).focus();
27133     },
27134     
27135     /**
27136      * Disables this item.
27137      */
27138     disable : function(){
27139         Roo.fly(this.td).addClass("x-item-disabled");
27140         this.disabled = true;
27141         this.el.disabled = true;
27142     },
27143     
27144     /**
27145      * Enables this item.
27146      */
27147     enable : function(){
27148         Roo.fly(this.td).removeClass("x-item-disabled");
27149         this.disabled = false;
27150         this.el.disabled = false;
27151     }
27152 };
27153
27154
27155 /**
27156  * @class Roo.Toolbar.Separator
27157  * @extends Roo.Toolbar.Item
27158  * A simple toolbar separator class
27159  * @constructor
27160  * Creates a new Separator
27161  */
27162 Roo.Toolbar.Separator = function(){
27163     var s = document.createElement("span");
27164     s.className = "ytb-sep";
27165     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27166 };
27167 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27168     enable:Roo.emptyFn,
27169     disable:Roo.emptyFn,
27170     focus:Roo.emptyFn
27171 });
27172
27173 /**
27174  * @class Roo.Toolbar.Spacer
27175  * @extends Roo.Toolbar.Item
27176  * A simple element that adds extra horizontal space to a toolbar.
27177  * @constructor
27178  * Creates a new Spacer
27179  */
27180 Roo.Toolbar.Spacer = function(){
27181     var s = document.createElement("div");
27182     s.className = "ytb-spacer";
27183     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27184 };
27185 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27186     enable:Roo.emptyFn,
27187     disable:Roo.emptyFn,
27188     focus:Roo.emptyFn
27189 });
27190
27191 /**
27192  * @class Roo.Toolbar.Fill
27193  * @extends Roo.Toolbar.Spacer
27194  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27195  * @constructor
27196  * Creates a new Spacer
27197  */
27198 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27199     // private
27200     render : function(td){
27201         td.style.width = '100%';
27202         Roo.Toolbar.Fill.superclass.render.call(this, td);
27203     }
27204 });
27205
27206 /**
27207  * @class Roo.Toolbar.TextItem
27208  * @extends Roo.Toolbar.Item
27209  * A simple class that renders text directly into a toolbar.
27210  * @constructor
27211  * Creates a new TextItem
27212  * @param {String} text
27213  */
27214 Roo.Toolbar.TextItem = function(text){
27215     if (typeof(text) == 'object') {
27216         text = text.text;
27217     }
27218     var s = document.createElement("span");
27219     s.className = "ytb-text";
27220     s.innerHTML = text;
27221     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27222 };
27223 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27224     enable:Roo.emptyFn,
27225     disable:Roo.emptyFn,
27226     focus:Roo.emptyFn
27227 });
27228
27229 /**
27230  * @class Roo.Toolbar.Button
27231  * @extends Roo.Button
27232  * A button that renders into a toolbar.
27233  * @constructor
27234  * Creates a new Button
27235  * @param {Object} config A standard {@link Roo.Button} config object
27236  */
27237 Roo.Toolbar.Button = function(config){
27238     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27239 };
27240 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27241     render : function(td){
27242         this.td = td;
27243         Roo.Toolbar.Button.superclass.render.call(this, td);
27244     },
27245     
27246     /**
27247      * Removes and destroys this button
27248      */
27249     destroy : function(){
27250         Roo.Toolbar.Button.superclass.destroy.call(this);
27251         this.td.parentNode.removeChild(this.td);
27252     },
27253     
27254     /**
27255      * Shows this button
27256      */
27257     show: function(){
27258         this.hidden = false;
27259         this.td.style.display = "";
27260     },
27261     
27262     /**
27263      * Hides this button
27264      */
27265     hide: function(){
27266         this.hidden = true;
27267         this.td.style.display = "none";
27268     },
27269
27270     /**
27271      * Disables this item
27272      */
27273     disable : function(){
27274         Roo.fly(this.td).addClass("x-item-disabled");
27275         this.disabled = true;
27276     },
27277
27278     /**
27279      * Enables this item
27280      */
27281     enable : function(){
27282         Roo.fly(this.td).removeClass("x-item-disabled");
27283         this.disabled = false;
27284     }
27285 });
27286 // backwards compat
27287 Roo.ToolbarButton = Roo.Toolbar.Button;
27288
27289 /**
27290  * @class Roo.Toolbar.SplitButton
27291  * @extends Roo.SplitButton
27292  * A menu button that renders into a toolbar.
27293  * @constructor
27294  * Creates a new SplitButton
27295  * @param {Object} config A standard {@link Roo.SplitButton} config object
27296  */
27297 Roo.Toolbar.SplitButton = function(config){
27298     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27299 };
27300 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27301     render : function(td){
27302         this.td = td;
27303         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27304     },
27305     
27306     /**
27307      * Removes and destroys this button
27308      */
27309     destroy : function(){
27310         Roo.Toolbar.SplitButton.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 // backwards compat
27332 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27333  * Based on:
27334  * Ext JS Library 1.1.1
27335  * Copyright(c) 2006-2007, Ext JS, LLC.
27336  *
27337  * Originally Released Under LGPL - original licence link has changed is not relivant.
27338  *
27339  * Fork - LGPL
27340  * <script type="text/javascript">
27341  */
27342  
27343 /**
27344  * @class Roo.PagingToolbar
27345  * @extends Roo.Toolbar
27346  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27347  * @constructor
27348  * Create a new PagingToolbar
27349  * @param {Object} config The config object
27350  */
27351 Roo.PagingToolbar = function(el, ds, config)
27352 {
27353     // old args format still supported... - xtype is prefered..
27354     if (typeof(el) == 'object' && el.xtype) {
27355         // created from xtype...
27356         config = el;
27357         ds = el.dataSource;
27358         el = config.container;
27359     }
27360     var items = [];
27361     if (config.items) {
27362         items = config.items;
27363         config.items = [];
27364     }
27365     
27366     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27367     this.ds = ds;
27368     this.cursor = 0;
27369     this.renderButtons(this.el);
27370     this.bind(ds);
27371     
27372     // supprot items array.
27373    
27374     Roo.each(items, function(e) {
27375         this.add(Roo.factory(e));
27376     },this);
27377     
27378 };
27379
27380 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27381     /**
27382      * @cfg {Roo.data.Store} dataSource
27383      * The underlying data store providing the paged data
27384      */
27385     /**
27386      * @cfg {String/HTMLElement/Element} container
27387      * container The id or element that will contain the toolbar
27388      */
27389     /**
27390      * @cfg {Boolean} displayInfo
27391      * True to display the displayMsg (defaults to false)
27392      */
27393     /**
27394      * @cfg {Number} pageSize
27395      * The number of records to display per page (defaults to 20)
27396      */
27397     pageSize: 20,
27398     /**
27399      * @cfg {String} displayMsg
27400      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27401      */
27402     displayMsg : 'Displaying {0} - {1} of {2}',
27403     /**
27404      * @cfg {String} emptyMsg
27405      * The message to display when no records are found (defaults to "No data to display")
27406      */
27407     emptyMsg : 'No data to display',
27408     /**
27409      * Customizable piece of the default paging text (defaults to "Page")
27410      * @type String
27411      */
27412     beforePageText : "Page",
27413     /**
27414      * Customizable piece of the default paging text (defaults to "of %0")
27415      * @type String
27416      */
27417     afterPageText : "of {0}",
27418     /**
27419      * Customizable piece of the default paging text (defaults to "First Page")
27420      * @type String
27421      */
27422     firstText : "First Page",
27423     /**
27424      * Customizable piece of the default paging text (defaults to "Previous Page")
27425      * @type String
27426      */
27427     prevText : "Previous Page",
27428     /**
27429      * Customizable piece of the default paging text (defaults to "Next Page")
27430      * @type String
27431      */
27432     nextText : "Next Page",
27433     /**
27434      * Customizable piece of the default paging text (defaults to "Last Page")
27435      * @type String
27436      */
27437     lastText : "Last Page",
27438     /**
27439      * Customizable piece of the default paging text (defaults to "Refresh")
27440      * @type String
27441      */
27442     refreshText : "Refresh",
27443
27444     // private
27445     renderButtons : function(el){
27446         Roo.PagingToolbar.superclass.render.call(this, el);
27447         this.first = this.addButton({
27448             tooltip: this.firstText,
27449             cls: "x-btn-icon x-grid-page-first",
27450             disabled: true,
27451             handler: this.onClick.createDelegate(this, ["first"])
27452         });
27453         this.prev = this.addButton({
27454             tooltip: this.prevText,
27455             cls: "x-btn-icon x-grid-page-prev",
27456             disabled: true,
27457             handler: this.onClick.createDelegate(this, ["prev"])
27458         });
27459         //this.addSeparator();
27460         this.add(this.beforePageText);
27461         this.field = Roo.get(this.addDom({
27462            tag: "input",
27463            type: "text",
27464            size: "3",
27465            value: "1",
27466            cls: "x-grid-page-number"
27467         }).el);
27468         this.field.on("keydown", this.onPagingKeydown, this);
27469         this.field.on("focus", function(){this.dom.select();});
27470         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27471         this.field.setHeight(18);
27472         //this.addSeparator();
27473         this.next = this.addButton({
27474             tooltip: this.nextText,
27475             cls: "x-btn-icon x-grid-page-next",
27476             disabled: true,
27477             handler: this.onClick.createDelegate(this, ["next"])
27478         });
27479         this.last = this.addButton({
27480             tooltip: this.lastText,
27481             cls: "x-btn-icon x-grid-page-last",
27482             disabled: true,
27483             handler: this.onClick.createDelegate(this, ["last"])
27484         });
27485         //this.addSeparator();
27486         this.loading = this.addButton({
27487             tooltip: this.refreshText,
27488             cls: "x-btn-icon x-grid-loading",
27489             handler: this.onClick.createDelegate(this, ["refresh"])
27490         });
27491
27492         if(this.displayInfo){
27493             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27494         }
27495     },
27496
27497     // private
27498     updateInfo : function(){
27499         if(this.displayEl){
27500             var count = this.ds.getCount();
27501             var msg = count == 0 ?
27502                 this.emptyMsg :
27503                 String.format(
27504                     this.displayMsg,
27505                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27506                 );
27507             this.displayEl.update(msg);
27508         }
27509     },
27510
27511     // private
27512     onLoad : function(ds, r, o){
27513        this.cursor = o.params ? o.params.start : 0;
27514        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27515
27516        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27517        this.field.dom.value = ap;
27518        this.first.setDisabled(ap == 1);
27519        this.prev.setDisabled(ap == 1);
27520        this.next.setDisabled(ap == ps);
27521        this.last.setDisabled(ap == ps);
27522        this.loading.enable();
27523        this.updateInfo();
27524     },
27525
27526     // private
27527     getPageData : function(){
27528         var total = this.ds.getTotalCount();
27529         return {
27530             total : total,
27531             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27532             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27533         };
27534     },
27535
27536     // private
27537     onLoadError : function(){
27538         this.loading.enable();
27539     },
27540
27541     // private
27542     onPagingKeydown : function(e){
27543         var k = e.getKey();
27544         var d = this.getPageData();
27545         if(k == e.RETURN){
27546             var v = this.field.dom.value, pageNum;
27547             if(!v || isNaN(pageNum = parseInt(v, 10))){
27548                 this.field.dom.value = d.activePage;
27549                 return;
27550             }
27551             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27552             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27553             e.stopEvent();
27554         }
27555         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))
27556         {
27557           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27558           this.field.dom.value = pageNum;
27559           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27560           e.stopEvent();
27561         }
27562         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27563         {
27564           var v = this.field.dom.value, pageNum; 
27565           var increment = (e.shiftKey) ? 10 : 1;
27566           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27567             increment *= -1;
27568           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27569             this.field.dom.value = d.activePage;
27570             return;
27571           }
27572           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27573           {
27574             this.field.dom.value = parseInt(v, 10) + increment;
27575             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27576             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27577           }
27578           e.stopEvent();
27579         }
27580     },
27581
27582     // private
27583     beforeLoad : function(){
27584         if(this.loading){
27585             this.loading.disable();
27586         }
27587     },
27588
27589     // private
27590     onClick : function(which){
27591         var ds = this.ds;
27592         switch(which){
27593             case "first":
27594                 ds.load({params:{start: 0, limit: this.pageSize}});
27595             break;
27596             case "prev":
27597                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27598             break;
27599             case "next":
27600                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27601             break;
27602             case "last":
27603                 var total = ds.getTotalCount();
27604                 var extra = total % this.pageSize;
27605                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27606                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27607             break;
27608             case "refresh":
27609                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27610             break;
27611         }
27612     },
27613
27614     /**
27615      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27616      * @param {Roo.data.Store} store The data store to unbind
27617      */
27618     unbind : function(ds){
27619         ds.un("beforeload", this.beforeLoad, this);
27620         ds.un("load", this.onLoad, this);
27621         ds.un("loadexception", this.onLoadError, this);
27622         ds.un("remove", this.updateInfo, this);
27623         ds.un("add", this.updateInfo, this);
27624         this.ds = undefined;
27625     },
27626
27627     /**
27628      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27629      * @param {Roo.data.Store} store The data store to bind
27630      */
27631     bind : function(ds){
27632         ds.on("beforeload", this.beforeLoad, this);
27633         ds.on("load", this.onLoad, this);
27634         ds.on("loadexception", this.onLoadError, this);
27635         ds.on("remove", this.updateInfo, this);
27636         ds.on("add", this.updateInfo, this);
27637         this.ds = ds;
27638     }
27639 });/*
27640  * Based on:
27641  * Ext JS Library 1.1.1
27642  * Copyright(c) 2006-2007, Ext JS, LLC.
27643  *
27644  * Originally Released Under LGPL - original licence link has changed is not relivant.
27645  *
27646  * Fork - LGPL
27647  * <script type="text/javascript">
27648  */
27649
27650 /**
27651  * @class Roo.Resizable
27652  * @extends Roo.util.Observable
27653  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27654  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27655  * 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
27656  * the element will be wrapped for you automatically.</p>
27657  * <p>Here is the list of valid resize handles:</p>
27658  * <pre>
27659 Value   Description
27660 ------  -------------------
27661  'n'     north
27662  's'     south
27663  'e'     east
27664  'w'     west
27665  'nw'    northwest
27666  'sw'    southwest
27667  'se'    southeast
27668  'ne'    northeast
27669  'hd'    horizontal drag
27670  'all'   all
27671 </pre>
27672  * <p>Here's an example showing the creation of a typical Resizable:</p>
27673  * <pre><code>
27674 var resizer = new Roo.Resizable("element-id", {
27675     handles: 'all',
27676     minWidth: 200,
27677     minHeight: 100,
27678     maxWidth: 500,
27679     maxHeight: 400,
27680     pinned: true
27681 });
27682 resizer.on("resize", myHandler);
27683 </code></pre>
27684  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27685  * resizer.east.setDisplayed(false);</p>
27686  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27687  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27688  * resize operation's new size (defaults to [0, 0])
27689  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27690  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27691  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27692  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27693  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27694  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27695  * @cfg {Number} width The width of the element in pixels (defaults to null)
27696  * @cfg {Number} height The height of the element in pixels (defaults to null)
27697  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27698  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27699  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27700  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27701  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27702  * in favor of the handles config option (defaults to false)
27703  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27704  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27705  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27706  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27707  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27708  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27709  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27710  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27711  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27712  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27713  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27714  * @constructor
27715  * Create a new resizable component
27716  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27717  * @param {Object} config configuration options
27718   */
27719 Roo.Resizable = function(el, config)
27720 {
27721     this.el = Roo.get(el);
27722
27723     if(config && config.wrap){
27724         config.resizeChild = this.el;
27725         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27726         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27727         this.el.setStyle("overflow", "hidden");
27728         this.el.setPositioning(config.resizeChild.getPositioning());
27729         config.resizeChild.clearPositioning();
27730         if(!config.width || !config.height){
27731             var csize = config.resizeChild.getSize();
27732             this.el.setSize(csize.width, csize.height);
27733         }
27734         if(config.pinned && !config.adjustments){
27735             config.adjustments = "auto";
27736         }
27737     }
27738
27739     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27740     this.proxy.unselectable();
27741     this.proxy.enableDisplayMode('block');
27742
27743     Roo.apply(this, config);
27744
27745     if(this.pinned){
27746         this.disableTrackOver = true;
27747         this.el.addClass("x-resizable-pinned");
27748     }
27749     // if the element isn't positioned, make it relative
27750     var position = this.el.getStyle("position");
27751     if(position != "absolute" && position != "fixed"){
27752         this.el.setStyle("position", "relative");
27753     }
27754     if(!this.handles){ // no handles passed, must be legacy style
27755         this.handles = 's,e,se';
27756         if(this.multiDirectional){
27757             this.handles += ',n,w';
27758         }
27759     }
27760     if(this.handles == "all"){
27761         this.handles = "n s e w ne nw se sw";
27762     }
27763     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27764     var ps = Roo.Resizable.positions;
27765     for(var i = 0, len = hs.length; i < len; i++){
27766         if(hs[i] && ps[hs[i]]){
27767             var pos = ps[hs[i]];
27768             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27769         }
27770     }
27771     // legacy
27772     this.corner = this.southeast;
27773     
27774     // updateBox = the box can move..
27775     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27776         this.updateBox = true;
27777     }
27778
27779     this.activeHandle = null;
27780
27781     if(this.resizeChild){
27782         if(typeof this.resizeChild == "boolean"){
27783             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27784         }else{
27785             this.resizeChild = Roo.get(this.resizeChild, true);
27786         }
27787     }
27788     
27789     if(this.adjustments == "auto"){
27790         var rc = this.resizeChild;
27791         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27792         if(rc && (hw || hn)){
27793             rc.position("relative");
27794             rc.setLeft(hw ? hw.el.getWidth() : 0);
27795             rc.setTop(hn ? hn.el.getHeight() : 0);
27796         }
27797         this.adjustments = [
27798             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27799             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27800         ];
27801     }
27802
27803     if(this.draggable){
27804         this.dd = this.dynamic ?
27805             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27806         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27807     }
27808
27809     // public events
27810     this.addEvents({
27811         /**
27812          * @event beforeresize
27813          * Fired before resize is allowed. Set enabled to false to cancel resize.
27814          * @param {Roo.Resizable} this
27815          * @param {Roo.EventObject} e The mousedown event
27816          */
27817         "beforeresize" : true,
27818         /**
27819          * @event resize
27820          * Fired after a resize.
27821          * @param {Roo.Resizable} this
27822          * @param {Number} width The new width
27823          * @param {Number} height The new height
27824          * @param {Roo.EventObject} e The mouseup event
27825          */
27826         "resize" : true
27827     });
27828
27829     if(this.width !== null && this.height !== null){
27830         this.resizeTo(this.width, this.height);
27831     }else{
27832         this.updateChildSize();
27833     }
27834     if(Roo.isIE){
27835         this.el.dom.style.zoom = 1;
27836     }
27837     Roo.Resizable.superclass.constructor.call(this);
27838 };
27839
27840 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27841         resizeChild : false,
27842         adjustments : [0, 0],
27843         minWidth : 5,
27844         minHeight : 5,
27845         maxWidth : 10000,
27846         maxHeight : 10000,
27847         enabled : true,
27848         animate : false,
27849         duration : .35,
27850         dynamic : false,
27851         handles : false,
27852         multiDirectional : false,
27853         disableTrackOver : false,
27854         easing : 'easeOutStrong',
27855         widthIncrement : 0,
27856         heightIncrement : 0,
27857         pinned : false,
27858         width : null,
27859         height : null,
27860         preserveRatio : false,
27861         transparent: false,
27862         minX: 0,
27863         minY: 0,
27864         draggable: false,
27865
27866         /**
27867          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27868          */
27869         constrainTo: undefined,
27870         /**
27871          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27872          */
27873         resizeRegion: undefined,
27874
27875
27876     /**
27877      * Perform a manual resize
27878      * @param {Number} width
27879      * @param {Number} height
27880      */
27881     resizeTo : function(width, height){
27882         this.el.setSize(width, height);
27883         this.updateChildSize();
27884         this.fireEvent("resize", this, width, height, null);
27885     },
27886
27887     // private
27888     startSizing : function(e, handle){
27889         this.fireEvent("beforeresize", this, e);
27890         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27891
27892             if(!this.overlay){
27893                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27894                 this.overlay.unselectable();
27895                 this.overlay.enableDisplayMode("block");
27896                 this.overlay.on("mousemove", this.onMouseMove, this);
27897                 this.overlay.on("mouseup", this.onMouseUp, this);
27898             }
27899             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27900
27901             this.resizing = true;
27902             this.startBox = this.el.getBox();
27903             this.startPoint = e.getXY();
27904             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27905                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27906
27907             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27908             this.overlay.show();
27909
27910             if(this.constrainTo) {
27911                 var ct = Roo.get(this.constrainTo);
27912                 this.resizeRegion = ct.getRegion().adjust(
27913                     ct.getFrameWidth('t'),
27914                     ct.getFrameWidth('l'),
27915                     -ct.getFrameWidth('b'),
27916                     -ct.getFrameWidth('r')
27917                 );
27918             }
27919
27920             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27921             this.proxy.show();
27922             this.proxy.setBox(this.startBox);
27923             if(!this.dynamic){
27924                 this.proxy.setStyle('visibility', 'visible');
27925             }
27926         }
27927     },
27928
27929     // private
27930     onMouseDown : function(handle, e){
27931         if(this.enabled){
27932             e.stopEvent();
27933             this.activeHandle = handle;
27934             this.startSizing(e, handle);
27935         }
27936     },
27937
27938     // private
27939     onMouseUp : function(e){
27940         var size = this.resizeElement();
27941         this.resizing = false;
27942         this.handleOut();
27943         this.overlay.hide();
27944         this.proxy.hide();
27945         this.fireEvent("resize", this, size.width, size.height, e);
27946     },
27947
27948     // private
27949     updateChildSize : function(){
27950         if(this.resizeChild){
27951             var el = this.el;
27952             var child = this.resizeChild;
27953             var adj = this.adjustments;
27954             if(el.dom.offsetWidth){
27955                 var b = el.getSize(true);
27956                 child.setSize(b.width+adj[0], b.height+adj[1]);
27957             }
27958             // Second call here for IE
27959             // The first call enables instant resizing and
27960             // the second call corrects scroll bars if they
27961             // exist
27962             if(Roo.isIE){
27963                 setTimeout(function(){
27964                     if(el.dom.offsetWidth){
27965                         var b = el.getSize(true);
27966                         child.setSize(b.width+adj[0], b.height+adj[1]);
27967                     }
27968                 }, 10);
27969             }
27970         }
27971     },
27972
27973     // private
27974     snap : function(value, inc, min){
27975         if(!inc || !value) return value;
27976         var newValue = value;
27977         var m = value % inc;
27978         if(m > 0){
27979             if(m > (inc/2)){
27980                 newValue = value + (inc-m);
27981             }else{
27982                 newValue = value - m;
27983             }
27984         }
27985         return Math.max(min, newValue);
27986     },
27987
27988     // private
27989     resizeElement : function(){
27990         var box = this.proxy.getBox();
27991         if(this.updateBox){
27992             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27993         }else{
27994             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27995         }
27996         this.updateChildSize();
27997         if(!this.dynamic){
27998             this.proxy.hide();
27999         }
28000         return box;
28001     },
28002
28003     // private
28004     constrain : function(v, diff, m, mx){
28005         if(v - diff < m){
28006             diff = v - m;
28007         }else if(v - diff > mx){
28008             diff = mx - v;
28009         }
28010         return diff;
28011     },
28012
28013     // private
28014     onMouseMove : function(e){
28015         if(this.enabled){
28016             try{// try catch so if something goes wrong the user doesn't get hung
28017
28018             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28019                 return;
28020             }
28021
28022             //var curXY = this.startPoint;
28023             var curSize = this.curSize || this.startBox;
28024             var x = this.startBox.x, y = this.startBox.y;
28025             var ox = x, oy = y;
28026             var w = curSize.width, h = curSize.height;
28027             var ow = w, oh = h;
28028             var mw = this.minWidth, mh = this.minHeight;
28029             var mxw = this.maxWidth, mxh = this.maxHeight;
28030             var wi = this.widthIncrement;
28031             var hi = this.heightIncrement;
28032
28033             var eventXY = e.getXY();
28034             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28035             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28036
28037             var pos = this.activeHandle.position;
28038
28039             switch(pos){
28040                 case "east":
28041                     w += diffX;
28042                     w = Math.min(Math.max(mw, w), mxw);
28043                     break;
28044              
28045                 case "south":
28046                     h += diffY;
28047                     h = Math.min(Math.max(mh, h), mxh);
28048                     break;
28049                 case "southeast":
28050                     w += diffX;
28051                     h += diffY;
28052                     w = Math.min(Math.max(mw, w), mxw);
28053                     h = Math.min(Math.max(mh, h), mxh);
28054                     break;
28055                 case "north":
28056                     diffY = this.constrain(h, diffY, mh, mxh);
28057                     y += diffY;
28058                     h -= diffY;
28059                     break;
28060                 case "hdrag":
28061                     
28062                     if (wi) {
28063                         var adiffX = Math.abs(diffX);
28064                         var sub = (adiffX % wi); // how much 
28065                         if (sub > (wi/2)) { // far enough to snap
28066                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28067                         } else {
28068                             // remove difference.. 
28069                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28070                         }
28071                     }
28072                     x += diffX;
28073                     x = Math.max(this.minX, x);
28074                     break;
28075                 case "west":
28076                     diffX = this.constrain(w, diffX, mw, mxw);
28077                     x += diffX;
28078                     w -= diffX;
28079                     break;
28080                 case "northeast":
28081                     w += diffX;
28082                     w = Math.min(Math.max(mw, w), mxw);
28083                     diffY = this.constrain(h, diffY, mh, mxh);
28084                     y += diffY;
28085                     h -= diffY;
28086                     break;
28087                 case "northwest":
28088                     diffX = this.constrain(w, diffX, mw, mxw);
28089                     diffY = this.constrain(h, diffY, mh, mxh);
28090                     y += diffY;
28091                     h -= diffY;
28092                     x += diffX;
28093                     w -= diffX;
28094                     break;
28095                case "southwest":
28096                     diffX = this.constrain(w, diffX, mw, mxw);
28097                     h += diffY;
28098                     h = Math.min(Math.max(mh, h), mxh);
28099                     x += diffX;
28100                     w -= diffX;
28101                     break;
28102             }
28103
28104             var sw = this.snap(w, wi, mw);
28105             var sh = this.snap(h, hi, mh);
28106             if(sw != w || sh != h){
28107                 switch(pos){
28108                     case "northeast":
28109                         y -= sh - h;
28110                     break;
28111                     case "north":
28112                         y -= sh - h;
28113                         break;
28114                     case "southwest":
28115                         x -= sw - w;
28116                     break;
28117                     case "west":
28118                         x -= sw - w;
28119                         break;
28120                     case "northwest":
28121                         x -= sw - w;
28122                         y -= sh - h;
28123                     break;
28124                 }
28125                 w = sw;
28126                 h = sh;
28127             }
28128
28129             if(this.preserveRatio){
28130                 switch(pos){
28131                     case "southeast":
28132                     case "east":
28133                         h = oh * (w/ow);
28134                         h = Math.min(Math.max(mh, h), mxh);
28135                         w = ow * (h/oh);
28136                        break;
28137                     case "south":
28138                         w = ow * (h/oh);
28139                         w = Math.min(Math.max(mw, w), mxw);
28140                         h = oh * (w/ow);
28141                         break;
28142                     case "northeast":
28143                         w = ow * (h/oh);
28144                         w = Math.min(Math.max(mw, w), mxw);
28145                         h = oh * (w/ow);
28146                     break;
28147                     case "north":
28148                         var tw = w;
28149                         w = ow * (h/oh);
28150                         w = Math.min(Math.max(mw, w), mxw);
28151                         h = oh * (w/ow);
28152                         x += (tw - w) / 2;
28153                         break;
28154                     case "southwest":
28155                         h = oh * (w/ow);
28156                         h = Math.min(Math.max(mh, h), mxh);
28157                         var tw = w;
28158                         w = ow * (h/oh);
28159                         x += tw - w;
28160                         break;
28161                     case "west":
28162                         var th = h;
28163                         h = oh * (w/ow);
28164                         h = Math.min(Math.max(mh, h), mxh);
28165                         y += (th - h) / 2;
28166                         var tw = w;
28167                         w = ow * (h/oh);
28168                         x += tw - w;
28169                        break;
28170                     case "northwest":
28171                         var tw = w;
28172                         var th = h;
28173                         h = oh * (w/ow);
28174                         h = Math.min(Math.max(mh, h), mxh);
28175                         w = ow * (h/oh);
28176                         y += th - h;
28177                         x += tw - w;
28178                        break;
28179
28180                 }
28181             }
28182             if (pos == 'hdrag') {
28183                 w = ow;
28184             }
28185             this.proxy.setBounds(x, y, w, h);
28186             if(this.dynamic){
28187                 this.resizeElement();
28188             }
28189             }catch(e){}
28190         }
28191     },
28192
28193     // private
28194     handleOver : function(){
28195         if(this.enabled){
28196             this.el.addClass("x-resizable-over");
28197         }
28198     },
28199
28200     // private
28201     handleOut : function(){
28202         if(!this.resizing){
28203             this.el.removeClass("x-resizable-over");
28204         }
28205     },
28206
28207     /**
28208      * Returns the element this component is bound to.
28209      * @return {Roo.Element}
28210      */
28211     getEl : function(){
28212         return this.el;
28213     },
28214
28215     /**
28216      * Returns the resizeChild element (or null).
28217      * @return {Roo.Element}
28218      */
28219     getResizeChild : function(){
28220         return this.resizeChild;
28221     },
28222
28223     /**
28224      * Destroys this resizable. If the element was wrapped and
28225      * removeEl is not true then the element remains.
28226      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28227      */
28228     destroy : function(removeEl){
28229         this.proxy.remove();
28230         if(this.overlay){
28231             this.overlay.removeAllListeners();
28232             this.overlay.remove();
28233         }
28234         var ps = Roo.Resizable.positions;
28235         for(var k in ps){
28236             if(typeof ps[k] != "function" && this[ps[k]]){
28237                 var h = this[ps[k]];
28238                 h.el.removeAllListeners();
28239                 h.el.remove();
28240             }
28241         }
28242         if(removeEl){
28243             this.el.update("");
28244             this.el.remove();
28245         }
28246     }
28247 });
28248
28249 // private
28250 // hash to map config positions to true positions
28251 Roo.Resizable.positions = {
28252     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28253     hd: "hdrag"
28254 };
28255
28256 // private
28257 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28258     if(!this.tpl){
28259         // only initialize the template if resizable is used
28260         var tpl = Roo.DomHelper.createTemplate(
28261             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28262         );
28263         tpl.compile();
28264         Roo.Resizable.Handle.prototype.tpl = tpl;
28265     }
28266     this.position = pos;
28267     this.rz = rz;
28268     // show north drag fro topdra
28269     var handlepos = pos == 'hdrag' ? 'north' : pos;
28270     
28271     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28272     if (pos == 'hdrag') {
28273         this.el.setStyle('cursor', 'pointer');
28274     }
28275     this.el.unselectable();
28276     if(transparent){
28277         this.el.setOpacity(0);
28278     }
28279     this.el.on("mousedown", this.onMouseDown, this);
28280     if(!disableTrackOver){
28281         this.el.on("mouseover", this.onMouseOver, this);
28282         this.el.on("mouseout", this.onMouseOut, this);
28283     }
28284 };
28285
28286 // private
28287 Roo.Resizable.Handle.prototype = {
28288     afterResize : function(rz){
28289         // do nothing
28290     },
28291     // private
28292     onMouseDown : function(e){
28293         this.rz.onMouseDown(this, e);
28294     },
28295     // private
28296     onMouseOver : function(e){
28297         this.rz.handleOver(this, e);
28298     },
28299     // private
28300     onMouseOut : function(e){
28301         this.rz.handleOut(this, e);
28302     }
28303 };/*
28304  * Based on:
28305  * Ext JS Library 1.1.1
28306  * Copyright(c) 2006-2007, Ext JS, LLC.
28307  *
28308  * Originally Released Under LGPL - original licence link has changed is not relivant.
28309  *
28310  * Fork - LGPL
28311  * <script type="text/javascript">
28312  */
28313
28314 /**
28315  * @class Roo.Editor
28316  * @extends Roo.Component
28317  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28318  * @constructor
28319  * Create a new Editor
28320  * @param {Roo.form.Field} field The Field object (or descendant)
28321  * @param {Object} config The config object
28322  */
28323 Roo.Editor = function(field, config){
28324     Roo.Editor.superclass.constructor.call(this, config);
28325     this.field = field;
28326     this.addEvents({
28327         /**
28328              * @event beforestartedit
28329              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28330              * false from the handler of this event.
28331              * @param {Editor} this
28332              * @param {Roo.Element} boundEl The underlying element bound to this editor
28333              * @param {Mixed} value The field value being set
28334              */
28335         "beforestartedit" : true,
28336         /**
28337              * @event startedit
28338              * Fires when this editor is displayed
28339              * @param {Roo.Element} boundEl The underlying element bound to this editor
28340              * @param {Mixed} value The starting field value
28341              */
28342         "startedit" : true,
28343         /**
28344              * @event beforecomplete
28345              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28346              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28347              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28348              * event will not fire since no edit actually occurred.
28349              * @param {Editor} this
28350              * @param {Mixed} value The current field value
28351              * @param {Mixed} startValue The original field value
28352              */
28353         "beforecomplete" : true,
28354         /**
28355              * @event complete
28356              * Fires after editing is complete and any changed value has been written to the underlying field.
28357              * @param {Editor} this
28358              * @param {Mixed} value The current field value
28359              * @param {Mixed} startValue The original field value
28360              */
28361         "complete" : true,
28362         /**
28363          * @event specialkey
28364          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28365          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28366          * @param {Roo.form.Field} this
28367          * @param {Roo.EventObject} e The event object
28368          */
28369         "specialkey" : true
28370     });
28371 };
28372
28373 Roo.extend(Roo.Editor, Roo.Component, {
28374     /**
28375      * @cfg {Boolean/String} autosize
28376      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28377      * or "height" to adopt the height only (defaults to false)
28378      */
28379     /**
28380      * @cfg {Boolean} revertInvalid
28381      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28382      * validation fails (defaults to true)
28383      */
28384     /**
28385      * @cfg {Boolean} ignoreNoChange
28386      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28387      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28388      * will never be ignored.
28389      */
28390     /**
28391      * @cfg {Boolean} hideEl
28392      * False to keep the bound element visible while the editor is displayed (defaults to true)
28393      */
28394     /**
28395      * @cfg {Mixed} value
28396      * The data value of the underlying field (defaults to "")
28397      */
28398     value : "",
28399     /**
28400      * @cfg {String} alignment
28401      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28402      */
28403     alignment: "c-c?",
28404     /**
28405      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28406      * for bottom-right shadow (defaults to "frame")
28407      */
28408     shadow : "frame",
28409     /**
28410      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28411      */
28412     constrain : false,
28413     /**
28414      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28415      */
28416     completeOnEnter : false,
28417     /**
28418      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28419      */
28420     cancelOnEsc : false,
28421     /**
28422      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28423      */
28424     updateEl : false,
28425
28426     // private
28427     onRender : function(ct, position){
28428         this.el = new Roo.Layer({
28429             shadow: this.shadow,
28430             cls: "x-editor",
28431             parentEl : ct,
28432             shim : this.shim,
28433             shadowOffset:4,
28434             id: this.id,
28435             constrain: this.constrain
28436         });
28437         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28438         if(this.field.msgTarget != 'title'){
28439             this.field.msgTarget = 'qtip';
28440         }
28441         this.field.render(this.el);
28442         if(Roo.isGecko){
28443             this.field.el.dom.setAttribute('autocomplete', 'off');
28444         }
28445         this.field.on("specialkey", this.onSpecialKey, this);
28446         if(this.swallowKeys){
28447             this.field.el.swallowEvent(['keydown','keypress']);
28448         }
28449         this.field.show();
28450         this.field.on("blur", this.onBlur, this);
28451         if(this.field.grow){
28452             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28453         }
28454     },
28455
28456     onSpecialKey : function(field, e)
28457     {
28458         //Roo.log('editor onSpecialKey');
28459         if(this.completeOnEnter && e.getKey() == e.ENTER){
28460             e.stopEvent();
28461             this.completeEdit();
28462             return;
28463         }
28464         // do not fire special key otherwise it might hide close the editor...
28465         if(e.getKey() == e.ENTER){    
28466             return;
28467         }
28468         if(this.cancelOnEsc && e.getKey() == e.ESC){
28469             this.cancelEdit();
28470             return;
28471         } 
28472         this.fireEvent('specialkey', field, e);
28473     
28474     },
28475
28476     /**
28477      * Starts the editing process and shows the editor.
28478      * @param {String/HTMLElement/Element} el The element to edit
28479      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28480       * to the innerHTML of el.
28481      */
28482     startEdit : function(el, value){
28483         if(this.editing){
28484             this.completeEdit();
28485         }
28486         this.boundEl = Roo.get(el);
28487         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28488         if(!this.rendered){
28489             this.render(this.parentEl || document.body);
28490         }
28491         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28492             return;
28493         }
28494         this.startValue = v;
28495         this.field.setValue(v);
28496         if(this.autoSize){
28497             var sz = this.boundEl.getSize();
28498             switch(this.autoSize){
28499                 case "width":
28500                 this.setSize(sz.width,  "");
28501                 break;
28502                 case "height":
28503                 this.setSize("",  sz.height);
28504                 break;
28505                 default:
28506                 this.setSize(sz.width,  sz.height);
28507             }
28508         }
28509         this.el.alignTo(this.boundEl, this.alignment);
28510         this.editing = true;
28511         if(Roo.QuickTips){
28512             Roo.QuickTips.disable();
28513         }
28514         this.show();
28515     },
28516
28517     /**
28518      * Sets the height and width of this editor.
28519      * @param {Number} width The new width
28520      * @param {Number} height The new height
28521      */
28522     setSize : function(w, h){
28523         this.field.setSize(w, h);
28524         if(this.el){
28525             this.el.sync();
28526         }
28527     },
28528
28529     /**
28530      * Realigns the editor to the bound field based on the current alignment config value.
28531      */
28532     realign : function(){
28533         this.el.alignTo(this.boundEl, this.alignment);
28534     },
28535
28536     /**
28537      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28538      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28539      */
28540     completeEdit : function(remainVisible){
28541         if(!this.editing){
28542             return;
28543         }
28544         var v = this.getValue();
28545         if(this.revertInvalid !== false && !this.field.isValid()){
28546             v = this.startValue;
28547             this.cancelEdit(true);
28548         }
28549         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28550             this.editing = false;
28551             this.hide();
28552             return;
28553         }
28554         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28555             this.editing = false;
28556             if(this.updateEl && this.boundEl){
28557                 this.boundEl.update(v);
28558             }
28559             if(remainVisible !== true){
28560                 this.hide();
28561             }
28562             this.fireEvent("complete", this, v, this.startValue);
28563         }
28564     },
28565
28566     // private
28567     onShow : function(){
28568         this.el.show();
28569         if(this.hideEl !== false){
28570             this.boundEl.hide();
28571         }
28572         this.field.show();
28573         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28574             this.fixIEFocus = true;
28575             this.deferredFocus.defer(50, this);
28576         }else{
28577             this.field.focus();
28578         }
28579         this.fireEvent("startedit", this.boundEl, this.startValue);
28580     },
28581
28582     deferredFocus : function(){
28583         if(this.editing){
28584             this.field.focus();
28585         }
28586     },
28587
28588     /**
28589      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28590      * reverted to the original starting value.
28591      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28592      * cancel (defaults to false)
28593      */
28594     cancelEdit : function(remainVisible){
28595         if(this.editing){
28596             this.setValue(this.startValue);
28597             if(remainVisible !== true){
28598                 this.hide();
28599             }
28600         }
28601     },
28602
28603     // private
28604     onBlur : function(){
28605         if(this.allowBlur !== true && this.editing){
28606             this.completeEdit();
28607         }
28608     },
28609
28610     // private
28611     onHide : function(){
28612         if(this.editing){
28613             this.completeEdit();
28614             return;
28615         }
28616         this.field.blur();
28617         if(this.field.collapse){
28618             this.field.collapse();
28619         }
28620         this.el.hide();
28621         if(this.hideEl !== false){
28622             this.boundEl.show();
28623         }
28624         if(Roo.QuickTips){
28625             Roo.QuickTips.enable();
28626         }
28627     },
28628
28629     /**
28630      * Sets the data value of the editor
28631      * @param {Mixed} value Any valid value supported by the underlying field
28632      */
28633     setValue : function(v){
28634         this.field.setValue(v);
28635     },
28636
28637     /**
28638      * Gets the data value of the editor
28639      * @return {Mixed} The data value
28640      */
28641     getValue : function(){
28642         return this.field.getValue();
28643     }
28644 });/*
28645  * Based on:
28646  * Ext JS Library 1.1.1
28647  * Copyright(c) 2006-2007, Ext JS, LLC.
28648  *
28649  * Originally Released Under LGPL - original licence link has changed is not relivant.
28650  *
28651  * Fork - LGPL
28652  * <script type="text/javascript">
28653  */
28654  
28655 /**
28656  * @class Roo.BasicDialog
28657  * @extends Roo.util.Observable
28658  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28659  * <pre><code>
28660 var dlg = new Roo.BasicDialog("my-dlg", {
28661     height: 200,
28662     width: 300,
28663     minHeight: 100,
28664     minWidth: 150,
28665     modal: true,
28666     proxyDrag: true,
28667     shadow: true
28668 });
28669 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28670 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28671 dlg.addButton('Cancel', dlg.hide, dlg);
28672 dlg.show();
28673 </code></pre>
28674   <b>A Dialog should always be a direct child of the body element.</b>
28675  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28676  * @cfg {String} title Default text to display in the title bar (defaults to null)
28677  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28678  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28679  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28680  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28681  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28682  * (defaults to null with no animation)
28683  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28684  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28685  * property for valid values (defaults to 'all')
28686  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28687  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28688  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28689  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28690  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28691  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28692  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28693  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28694  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28695  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28696  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28697  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28698  * draggable = true (defaults to false)
28699  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28700  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28701  * shadow (defaults to false)
28702  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28703  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28704  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28705  * @cfg {Array} buttons Array of buttons
28706  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28707  * @constructor
28708  * Create a new BasicDialog.
28709  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28710  * @param {Object} config Configuration options
28711  */
28712 Roo.BasicDialog = function(el, config){
28713     this.el = Roo.get(el);
28714     var dh = Roo.DomHelper;
28715     if(!this.el && config && config.autoCreate){
28716         if(typeof config.autoCreate == "object"){
28717             if(!config.autoCreate.id){
28718                 config.autoCreate.id = el;
28719             }
28720             this.el = dh.append(document.body,
28721                         config.autoCreate, true);
28722         }else{
28723             this.el = dh.append(document.body,
28724                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28725         }
28726     }
28727     el = this.el;
28728     el.setDisplayed(true);
28729     el.hide = this.hideAction;
28730     this.id = el.id;
28731     el.addClass("x-dlg");
28732
28733     Roo.apply(this, config);
28734
28735     this.proxy = el.createProxy("x-dlg-proxy");
28736     this.proxy.hide = this.hideAction;
28737     this.proxy.setOpacity(.5);
28738     this.proxy.hide();
28739
28740     if(config.width){
28741         el.setWidth(config.width);
28742     }
28743     if(config.height){
28744         el.setHeight(config.height);
28745     }
28746     this.size = el.getSize();
28747     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28748         this.xy = [config.x,config.y];
28749     }else{
28750         this.xy = el.getCenterXY(true);
28751     }
28752     /** The header element @type Roo.Element */
28753     this.header = el.child("> .x-dlg-hd");
28754     /** The body element @type Roo.Element */
28755     this.body = el.child("> .x-dlg-bd");
28756     /** The footer element @type Roo.Element */
28757     this.footer = el.child("> .x-dlg-ft");
28758
28759     if(!this.header){
28760         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28761     }
28762     if(!this.body){
28763         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28764     }
28765
28766     this.header.unselectable();
28767     if(this.title){
28768         this.header.update(this.title);
28769     }
28770     // this element allows the dialog to be focused for keyboard event
28771     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28772     this.focusEl.swallowEvent("click", true);
28773
28774     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28775
28776     // wrap the body and footer for special rendering
28777     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28778     if(this.footer){
28779         this.bwrap.dom.appendChild(this.footer.dom);
28780     }
28781
28782     this.bg = this.el.createChild({
28783         tag: "div", cls:"x-dlg-bg",
28784         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28785     });
28786     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28787
28788
28789     if(this.autoScroll !== false && !this.autoTabs){
28790         this.body.setStyle("overflow", "auto");
28791     }
28792
28793     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28794
28795     if(this.closable !== false){
28796         this.el.addClass("x-dlg-closable");
28797         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28798         this.close.on("click", this.closeClick, this);
28799         this.close.addClassOnOver("x-dlg-close-over");
28800     }
28801     if(this.collapsible !== false){
28802         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28803         this.collapseBtn.on("click", this.collapseClick, this);
28804         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28805         this.header.on("dblclick", this.collapseClick, this);
28806     }
28807     if(this.resizable !== false){
28808         this.el.addClass("x-dlg-resizable");
28809         this.resizer = new Roo.Resizable(el, {
28810             minWidth: this.minWidth || 80,
28811             minHeight:this.minHeight || 80,
28812             handles: this.resizeHandles || "all",
28813             pinned: true
28814         });
28815         this.resizer.on("beforeresize", this.beforeResize, this);
28816         this.resizer.on("resize", this.onResize, this);
28817     }
28818     if(this.draggable !== false){
28819         el.addClass("x-dlg-draggable");
28820         if (!this.proxyDrag) {
28821             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28822         }
28823         else {
28824             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28825         }
28826         dd.setHandleElId(this.header.id);
28827         dd.endDrag = this.endMove.createDelegate(this);
28828         dd.startDrag = this.startMove.createDelegate(this);
28829         dd.onDrag = this.onDrag.createDelegate(this);
28830         dd.scroll = false;
28831         this.dd = dd;
28832     }
28833     if(this.modal){
28834         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28835         this.mask.enableDisplayMode("block");
28836         this.mask.hide();
28837         this.el.addClass("x-dlg-modal");
28838     }
28839     if(this.shadow){
28840         this.shadow = new Roo.Shadow({
28841             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28842             offset : this.shadowOffset
28843         });
28844     }else{
28845         this.shadowOffset = 0;
28846     }
28847     if(Roo.useShims && this.shim !== false){
28848         this.shim = this.el.createShim();
28849         this.shim.hide = this.hideAction;
28850         this.shim.hide();
28851     }else{
28852         this.shim = false;
28853     }
28854     if(this.autoTabs){
28855         this.initTabs();
28856     }
28857     if (this.buttons) { 
28858         var bts= this.buttons;
28859         this.buttons = [];
28860         Roo.each(bts, function(b) {
28861             this.addButton(b);
28862         }, this);
28863     }
28864     
28865     
28866     this.addEvents({
28867         /**
28868          * @event keydown
28869          * Fires when a key is pressed
28870          * @param {Roo.BasicDialog} this
28871          * @param {Roo.EventObject} e
28872          */
28873         "keydown" : true,
28874         /**
28875          * @event move
28876          * Fires when this dialog is moved by the user.
28877          * @param {Roo.BasicDialog} this
28878          * @param {Number} x The new page X
28879          * @param {Number} y The new page Y
28880          */
28881         "move" : true,
28882         /**
28883          * @event resize
28884          * Fires when this dialog is resized by the user.
28885          * @param {Roo.BasicDialog} this
28886          * @param {Number} width The new width
28887          * @param {Number} height The new height
28888          */
28889         "resize" : true,
28890         /**
28891          * @event beforehide
28892          * Fires before this dialog is hidden.
28893          * @param {Roo.BasicDialog} this
28894          */
28895         "beforehide" : true,
28896         /**
28897          * @event hide
28898          * Fires when this dialog is hidden.
28899          * @param {Roo.BasicDialog} this
28900          */
28901         "hide" : true,
28902         /**
28903          * @event beforeshow
28904          * Fires before this dialog is shown.
28905          * @param {Roo.BasicDialog} this
28906          */
28907         "beforeshow" : true,
28908         /**
28909          * @event show
28910          * Fires when this dialog is shown.
28911          * @param {Roo.BasicDialog} this
28912          */
28913         "show" : true
28914     });
28915     el.on("keydown", this.onKeyDown, this);
28916     el.on("mousedown", this.toFront, this);
28917     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28918     this.el.hide();
28919     Roo.DialogManager.register(this);
28920     Roo.BasicDialog.superclass.constructor.call(this);
28921 };
28922
28923 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28924     shadowOffset: Roo.isIE ? 6 : 5,
28925     minHeight: 80,
28926     minWidth: 200,
28927     minButtonWidth: 75,
28928     defaultButton: null,
28929     buttonAlign: "right",
28930     tabTag: 'div',
28931     firstShow: true,
28932
28933     /**
28934      * Sets the dialog title text
28935      * @param {String} text The title text to display
28936      * @return {Roo.BasicDialog} this
28937      */
28938     setTitle : function(text){
28939         this.header.update(text);
28940         return this;
28941     },
28942
28943     // private
28944     closeClick : function(){
28945         this.hide();
28946     },
28947
28948     // private
28949     collapseClick : function(){
28950         this[this.collapsed ? "expand" : "collapse"]();
28951     },
28952
28953     /**
28954      * Collapses the dialog to its minimized state (only the title bar is visible).
28955      * Equivalent to the user clicking the collapse dialog button.
28956      */
28957     collapse : function(){
28958         if(!this.collapsed){
28959             this.collapsed = true;
28960             this.el.addClass("x-dlg-collapsed");
28961             this.restoreHeight = this.el.getHeight();
28962             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28963         }
28964     },
28965
28966     /**
28967      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28968      * clicking the expand dialog button.
28969      */
28970     expand : function(){
28971         if(this.collapsed){
28972             this.collapsed = false;
28973             this.el.removeClass("x-dlg-collapsed");
28974             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28975         }
28976     },
28977
28978     /**
28979      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28980      * @return {Roo.TabPanel} The tabs component
28981      */
28982     initTabs : function(){
28983         var tabs = this.getTabs();
28984         while(tabs.getTab(0)){
28985             tabs.removeTab(0);
28986         }
28987         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28988             var dom = el.dom;
28989             tabs.addTab(Roo.id(dom), dom.title);
28990             dom.title = "";
28991         });
28992         tabs.activate(0);
28993         return tabs;
28994     },
28995
28996     // private
28997     beforeResize : function(){
28998         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28999     },
29000
29001     // private
29002     onResize : function(){
29003         this.refreshSize();
29004         this.syncBodyHeight();
29005         this.adjustAssets();
29006         this.focus();
29007         this.fireEvent("resize", this, this.size.width, this.size.height);
29008     },
29009
29010     // private
29011     onKeyDown : function(e){
29012         if(this.isVisible()){
29013             this.fireEvent("keydown", this, e);
29014         }
29015     },
29016
29017     /**
29018      * Resizes the dialog.
29019      * @param {Number} width
29020      * @param {Number} height
29021      * @return {Roo.BasicDialog} this
29022      */
29023     resizeTo : function(width, height){
29024         this.el.setSize(width, height);
29025         this.size = {width: width, height: height};
29026         this.syncBodyHeight();
29027         if(this.fixedcenter){
29028             this.center();
29029         }
29030         if(this.isVisible()){
29031             this.constrainXY();
29032             this.adjustAssets();
29033         }
29034         this.fireEvent("resize", this, width, height);
29035         return this;
29036     },
29037
29038
29039     /**
29040      * Resizes the dialog to fit the specified content size.
29041      * @param {Number} width
29042      * @param {Number} height
29043      * @return {Roo.BasicDialog} this
29044      */
29045     setContentSize : function(w, h){
29046         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29047         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29048         //if(!this.el.isBorderBox()){
29049             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29050             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29051         //}
29052         if(this.tabs){
29053             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29054             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29055         }
29056         this.resizeTo(w, h);
29057         return this;
29058     },
29059
29060     /**
29061      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29062      * executed in response to a particular key being pressed while the dialog is active.
29063      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29064      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29065      * @param {Function} fn The function to call
29066      * @param {Object} scope (optional) The scope of the function
29067      * @return {Roo.BasicDialog} this
29068      */
29069     addKeyListener : function(key, fn, scope){
29070         var keyCode, shift, ctrl, alt;
29071         if(typeof key == "object" && !(key instanceof Array)){
29072             keyCode = key["key"];
29073             shift = key["shift"];
29074             ctrl = key["ctrl"];
29075             alt = key["alt"];
29076         }else{
29077             keyCode = key;
29078         }
29079         var handler = function(dlg, e){
29080             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29081                 var k = e.getKey();
29082                 if(keyCode instanceof Array){
29083                     for(var i = 0, len = keyCode.length; i < len; i++){
29084                         if(keyCode[i] == k){
29085                           fn.call(scope || window, dlg, k, e);
29086                           return;
29087                         }
29088                     }
29089                 }else{
29090                     if(k == keyCode){
29091                         fn.call(scope || window, dlg, k, e);
29092                     }
29093                 }
29094             }
29095         };
29096         this.on("keydown", handler);
29097         return this;
29098     },
29099
29100     /**
29101      * Returns the TabPanel component (creates it if it doesn't exist).
29102      * Note: If you wish to simply check for the existence of tabs without creating them,
29103      * check for a null 'tabs' property.
29104      * @return {Roo.TabPanel} The tabs component
29105      */
29106     getTabs : function(){
29107         if(!this.tabs){
29108             this.el.addClass("x-dlg-auto-tabs");
29109             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29110             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29111         }
29112         return this.tabs;
29113     },
29114
29115     /**
29116      * Adds a button to the footer section of the dialog.
29117      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29118      * object or a valid Roo.DomHelper element config
29119      * @param {Function} handler The function called when the button is clicked
29120      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29121      * @return {Roo.Button} The new button
29122      */
29123     addButton : function(config, handler, scope){
29124         var dh = Roo.DomHelper;
29125         if(!this.footer){
29126             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29127         }
29128         if(!this.btnContainer){
29129             var tb = this.footer.createChild({
29130
29131                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29132                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29133             }, null, true);
29134             this.btnContainer = tb.firstChild.firstChild.firstChild;
29135         }
29136         var bconfig = {
29137             handler: handler,
29138             scope: scope,
29139             minWidth: this.minButtonWidth,
29140             hideParent:true
29141         };
29142         if(typeof config == "string"){
29143             bconfig.text = config;
29144         }else{
29145             if(config.tag){
29146                 bconfig.dhconfig = config;
29147             }else{
29148                 Roo.apply(bconfig, config);
29149             }
29150         }
29151         var fc = false;
29152         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29153             bconfig.position = Math.max(0, bconfig.position);
29154             fc = this.btnContainer.childNodes[bconfig.position];
29155         }
29156          
29157         var btn = new Roo.Button(
29158             fc ? 
29159                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29160                 : this.btnContainer.appendChild(document.createElement("td")),
29161             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29162             bconfig
29163         );
29164         this.syncBodyHeight();
29165         if(!this.buttons){
29166             /**
29167              * Array of all the buttons that have been added to this dialog via addButton
29168              * @type Array
29169              */
29170             this.buttons = [];
29171         }
29172         this.buttons.push(btn);
29173         return btn;
29174     },
29175
29176     /**
29177      * Sets the default button to be focused when the dialog is displayed.
29178      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29179      * @return {Roo.BasicDialog} this
29180      */
29181     setDefaultButton : function(btn){
29182         this.defaultButton = btn;
29183         return this;
29184     },
29185
29186     // private
29187     getHeaderFooterHeight : function(safe){
29188         var height = 0;
29189         if(this.header){
29190            height += this.header.getHeight();
29191         }
29192         if(this.footer){
29193            var fm = this.footer.getMargins();
29194             height += (this.footer.getHeight()+fm.top+fm.bottom);
29195         }
29196         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29197         height += this.centerBg.getPadding("tb");
29198         return height;
29199     },
29200
29201     // private
29202     syncBodyHeight : function(){
29203         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29204         var height = this.size.height - this.getHeaderFooterHeight(false);
29205         bd.setHeight(height-bd.getMargins("tb"));
29206         var hh = this.header.getHeight();
29207         var h = this.size.height-hh;
29208         cb.setHeight(h);
29209         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29210         bw.setHeight(h-cb.getPadding("tb"));
29211         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29212         bd.setWidth(bw.getWidth(true));
29213         if(this.tabs){
29214             this.tabs.syncHeight();
29215             if(Roo.isIE){
29216                 this.tabs.el.repaint();
29217             }
29218         }
29219     },
29220
29221     /**
29222      * Restores the previous state of the dialog if Roo.state is configured.
29223      * @return {Roo.BasicDialog} this
29224      */
29225     restoreState : function(){
29226         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29227         if(box && box.width){
29228             this.xy = [box.x, box.y];
29229             this.resizeTo(box.width, box.height);
29230         }
29231         return this;
29232     },
29233
29234     // private
29235     beforeShow : function(){
29236         this.expand();
29237         if(this.fixedcenter){
29238             this.xy = this.el.getCenterXY(true);
29239         }
29240         if(this.modal){
29241             Roo.get(document.body).addClass("x-body-masked");
29242             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29243             this.mask.show();
29244         }
29245         this.constrainXY();
29246     },
29247
29248     // private
29249     animShow : function(){
29250         var b = Roo.get(this.animateTarget).getBox();
29251         this.proxy.setSize(b.width, b.height);
29252         this.proxy.setLocation(b.x, b.y);
29253         this.proxy.show();
29254         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29255                     true, .35, this.showEl.createDelegate(this));
29256     },
29257
29258     /**
29259      * Shows the dialog.
29260      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29261      * @return {Roo.BasicDialog} this
29262      */
29263     show : function(animateTarget){
29264         if (this.fireEvent("beforeshow", this) === false){
29265             return;
29266         }
29267         if(this.syncHeightBeforeShow){
29268             this.syncBodyHeight();
29269         }else if(this.firstShow){
29270             this.firstShow = false;
29271             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29272         }
29273         this.animateTarget = animateTarget || this.animateTarget;
29274         if(!this.el.isVisible()){
29275             this.beforeShow();
29276             if(this.animateTarget && Roo.get(this.animateTarget)){
29277                 this.animShow();
29278             }else{
29279                 this.showEl();
29280             }
29281         }
29282         return this;
29283     },
29284
29285     // private
29286     showEl : function(){
29287         this.proxy.hide();
29288         this.el.setXY(this.xy);
29289         this.el.show();
29290         this.adjustAssets(true);
29291         this.toFront();
29292         this.focus();
29293         // IE peekaboo bug - fix found by Dave Fenwick
29294         if(Roo.isIE){
29295             this.el.repaint();
29296         }
29297         this.fireEvent("show", this);
29298     },
29299
29300     /**
29301      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29302      * dialog itself will receive focus.
29303      */
29304     focus : function(){
29305         if(this.defaultButton){
29306             this.defaultButton.focus();
29307         }else{
29308             this.focusEl.focus();
29309         }
29310     },
29311
29312     // private
29313     constrainXY : function(){
29314         if(this.constraintoviewport !== false){
29315             if(!this.viewSize){
29316                 if(this.container){
29317                     var s = this.container.getSize();
29318                     this.viewSize = [s.width, s.height];
29319                 }else{
29320                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29321                 }
29322             }
29323             var s = Roo.get(this.container||document).getScroll();
29324
29325             var x = this.xy[0], y = this.xy[1];
29326             var w = this.size.width, h = this.size.height;
29327             var vw = this.viewSize[0], vh = this.viewSize[1];
29328             // only move it if it needs it
29329             var moved = false;
29330             // first validate right/bottom
29331             if(x + w > vw+s.left){
29332                 x = vw - w;
29333                 moved = true;
29334             }
29335             if(y + h > vh+s.top){
29336                 y = vh - h;
29337                 moved = true;
29338             }
29339             // then make sure top/left isn't negative
29340             if(x < s.left){
29341                 x = s.left;
29342                 moved = true;
29343             }
29344             if(y < s.top){
29345                 y = s.top;
29346                 moved = true;
29347             }
29348             if(moved){
29349                 // cache xy
29350                 this.xy = [x, y];
29351                 if(this.isVisible()){
29352                     this.el.setLocation(x, y);
29353                     this.adjustAssets();
29354                 }
29355             }
29356         }
29357     },
29358
29359     // private
29360     onDrag : function(){
29361         if(!this.proxyDrag){
29362             this.xy = this.el.getXY();
29363             this.adjustAssets();
29364         }
29365     },
29366
29367     // private
29368     adjustAssets : function(doShow){
29369         var x = this.xy[0], y = this.xy[1];
29370         var w = this.size.width, h = this.size.height;
29371         if(doShow === true){
29372             if(this.shadow){
29373                 this.shadow.show(this.el);
29374             }
29375             if(this.shim){
29376                 this.shim.show();
29377             }
29378         }
29379         if(this.shadow && this.shadow.isVisible()){
29380             this.shadow.show(this.el);
29381         }
29382         if(this.shim && this.shim.isVisible()){
29383             this.shim.setBounds(x, y, w, h);
29384         }
29385     },
29386
29387     // private
29388     adjustViewport : function(w, h){
29389         if(!w || !h){
29390             w = Roo.lib.Dom.getViewWidth();
29391             h = Roo.lib.Dom.getViewHeight();
29392         }
29393         // cache the size
29394         this.viewSize = [w, h];
29395         if(this.modal && this.mask.isVisible()){
29396             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29397             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29398         }
29399         if(this.isVisible()){
29400             this.constrainXY();
29401         }
29402     },
29403
29404     /**
29405      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29406      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29407      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29408      */
29409     destroy : function(removeEl){
29410         if(this.isVisible()){
29411             this.animateTarget = null;
29412             this.hide();
29413         }
29414         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29415         if(this.tabs){
29416             this.tabs.destroy(removeEl);
29417         }
29418         Roo.destroy(
29419              this.shim,
29420              this.proxy,
29421              this.resizer,
29422              this.close,
29423              this.mask
29424         );
29425         if(this.dd){
29426             this.dd.unreg();
29427         }
29428         if(this.buttons){
29429            for(var i = 0, len = this.buttons.length; i < len; i++){
29430                this.buttons[i].destroy();
29431            }
29432         }
29433         this.el.removeAllListeners();
29434         if(removeEl === true){
29435             this.el.update("");
29436             this.el.remove();
29437         }
29438         Roo.DialogManager.unregister(this);
29439     },
29440
29441     // private
29442     startMove : function(){
29443         if(this.proxyDrag){
29444             this.proxy.show();
29445         }
29446         if(this.constraintoviewport !== false){
29447             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29448         }
29449     },
29450
29451     // private
29452     endMove : function(){
29453         if(!this.proxyDrag){
29454             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29455         }else{
29456             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29457             this.proxy.hide();
29458         }
29459         this.refreshSize();
29460         this.adjustAssets();
29461         this.focus();
29462         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29463     },
29464
29465     /**
29466      * Brings this dialog to the front of any other visible dialogs
29467      * @return {Roo.BasicDialog} this
29468      */
29469     toFront : function(){
29470         Roo.DialogManager.bringToFront(this);
29471         return this;
29472     },
29473
29474     /**
29475      * Sends this dialog to the back (under) of any other visible dialogs
29476      * @return {Roo.BasicDialog} this
29477      */
29478     toBack : function(){
29479         Roo.DialogManager.sendToBack(this);
29480         return this;
29481     },
29482
29483     /**
29484      * Centers this dialog in the viewport
29485      * @return {Roo.BasicDialog} this
29486      */
29487     center : function(){
29488         var xy = this.el.getCenterXY(true);
29489         this.moveTo(xy[0], xy[1]);
29490         return this;
29491     },
29492
29493     /**
29494      * Moves the dialog's top-left corner to the specified point
29495      * @param {Number} x
29496      * @param {Number} y
29497      * @return {Roo.BasicDialog} this
29498      */
29499     moveTo : function(x, y){
29500         this.xy = [x,y];
29501         if(this.isVisible()){
29502             this.el.setXY(this.xy);
29503             this.adjustAssets();
29504         }
29505         return this;
29506     },
29507
29508     /**
29509      * Aligns the dialog to the specified element
29510      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29511      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29512      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29513      * @return {Roo.BasicDialog} this
29514      */
29515     alignTo : function(element, position, offsets){
29516         this.xy = this.el.getAlignToXY(element, position, offsets);
29517         if(this.isVisible()){
29518             this.el.setXY(this.xy);
29519             this.adjustAssets();
29520         }
29521         return this;
29522     },
29523
29524     /**
29525      * Anchors an element to another element and realigns it when the window is resized.
29526      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29527      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29528      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29529      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29530      * is a number, it is used as the buffer delay (defaults to 50ms).
29531      * @return {Roo.BasicDialog} this
29532      */
29533     anchorTo : function(el, alignment, offsets, monitorScroll){
29534         var action = function(){
29535             this.alignTo(el, alignment, offsets);
29536         };
29537         Roo.EventManager.onWindowResize(action, this);
29538         var tm = typeof monitorScroll;
29539         if(tm != 'undefined'){
29540             Roo.EventManager.on(window, 'scroll', action, this,
29541                 {buffer: tm == 'number' ? monitorScroll : 50});
29542         }
29543         action.call(this);
29544         return this;
29545     },
29546
29547     /**
29548      * Returns true if the dialog is visible
29549      * @return {Boolean}
29550      */
29551     isVisible : function(){
29552         return this.el.isVisible();
29553     },
29554
29555     // private
29556     animHide : function(callback){
29557         var b = Roo.get(this.animateTarget).getBox();
29558         this.proxy.show();
29559         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29560         this.el.hide();
29561         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29562                     this.hideEl.createDelegate(this, [callback]));
29563     },
29564
29565     /**
29566      * Hides the dialog.
29567      * @param {Function} callback (optional) Function to call when the dialog is hidden
29568      * @return {Roo.BasicDialog} this
29569      */
29570     hide : function(callback){
29571         if (this.fireEvent("beforehide", this) === false){
29572             return;
29573         }
29574         if(this.shadow){
29575             this.shadow.hide();
29576         }
29577         if(this.shim) {
29578           this.shim.hide();
29579         }
29580         // sometimes animateTarget seems to get set.. causing problems...
29581         // this just double checks..
29582         if(this.animateTarget && Roo.get(this.animateTarget)) {
29583            this.animHide(callback);
29584         }else{
29585             this.el.hide();
29586             this.hideEl(callback);
29587         }
29588         return this;
29589     },
29590
29591     // private
29592     hideEl : function(callback){
29593         this.proxy.hide();
29594         if(this.modal){
29595             this.mask.hide();
29596             Roo.get(document.body).removeClass("x-body-masked");
29597         }
29598         this.fireEvent("hide", this);
29599         if(typeof callback == "function"){
29600             callback();
29601         }
29602     },
29603
29604     // private
29605     hideAction : function(){
29606         this.setLeft("-10000px");
29607         this.setTop("-10000px");
29608         this.setStyle("visibility", "hidden");
29609     },
29610
29611     // private
29612     refreshSize : function(){
29613         this.size = this.el.getSize();
29614         this.xy = this.el.getXY();
29615         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29616     },
29617
29618     // private
29619     // z-index is managed by the DialogManager and may be overwritten at any time
29620     setZIndex : function(index){
29621         if(this.modal){
29622             this.mask.setStyle("z-index", index);
29623         }
29624         if(this.shim){
29625             this.shim.setStyle("z-index", ++index);
29626         }
29627         if(this.shadow){
29628             this.shadow.setZIndex(++index);
29629         }
29630         this.el.setStyle("z-index", ++index);
29631         if(this.proxy){
29632             this.proxy.setStyle("z-index", ++index);
29633         }
29634         if(this.resizer){
29635             this.resizer.proxy.setStyle("z-index", ++index);
29636         }
29637
29638         this.lastZIndex = index;
29639     },
29640
29641     /**
29642      * Returns the element for this dialog
29643      * @return {Roo.Element} The underlying dialog Element
29644      */
29645     getEl : function(){
29646         return this.el;
29647     }
29648 });
29649
29650 /**
29651  * @class Roo.DialogManager
29652  * Provides global access to BasicDialogs that have been created and
29653  * support for z-indexing (layering) multiple open dialogs.
29654  */
29655 Roo.DialogManager = function(){
29656     var list = {};
29657     var accessList = [];
29658     var front = null;
29659
29660     // private
29661     var sortDialogs = function(d1, d2){
29662         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29663     };
29664
29665     // private
29666     var orderDialogs = function(){
29667         accessList.sort(sortDialogs);
29668         var seed = Roo.DialogManager.zseed;
29669         for(var i = 0, len = accessList.length; i < len; i++){
29670             var dlg = accessList[i];
29671             if(dlg){
29672                 dlg.setZIndex(seed + (i*10));
29673             }
29674         }
29675     };
29676
29677     return {
29678         /**
29679          * The starting z-index for BasicDialogs (defaults to 9000)
29680          * @type Number The z-index value
29681          */
29682         zseed : 9000,
29683
29684         // private
29685         register : function(dlg){
29686             list[dlg.id] = dlg;
29687             accessList.push(dlg);
29688         },
29689
29690         // private
29691         unregister : function(dlg){
29692             delete list[dlg.id];
29693             var i=0;
29694             var len=0;
29695             if(!accessList.indexOf){
29696                 for(  i = 0, len = accessList.length; i < len; i++){
29697                     if(accessList[i] == dlg){
29698                         accessList.splice(i, 1);
29699                         return;
29700                     }
29701                 }
29702             }else{
29703                  i = accessList.indexOf(dlg);
29704                 if(i != -1){
29705                     accessList.splice(i, 1);
29706                 }
29707             }
29708         },
29709
29710         /**
29711          * Gets a registered dialog by id
29712          * @param {String/Object} id The id of the dialog or a dialog
29713          * @return {Roo.BasicDialog} this
29714          */
29715         get : function(id){
29716             return typeof id == "object" ? id : list[id];
29717         },
29718
29719         /**
29720          * Brings the specified dialog to the front
29721          * @param {String/Object} dlg The id of the dialog or a dialog
29722          * @return {Roo.BasicDialog} this
29723          */
29724         bringToFront : function(dlg){
29725             dlg = this.get(dlg);
29726             if(dlg != front){
29727                 front = dlg;
29728                 dlg._lastAccess = new Date().getTime();
29729                 orderDialogs();
29730             }
29731             return dlg;
29732         },
29733
29734         /**
29735          * Sends the specified dialog to the back
29736          * @param {String/Object} dlg The id of the dialog or a dialog
29737          * @return {Roo.BasicDialog} this
29738          */
29739         sendToBack : function(dlg){
29740             dlg = this.get(dlg);
29741             dlg._lastAccess = -(new Date().getTime());
29742             orderDialogs();
29743             return dlg;
29744         },
29745
29746         /**
29747          * Hides all dialogs
29748          */
29749         hideAll : function(){
29750             for(var id in list){
29751                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29752                     list[id].hide();
29753                 }
29754             }
29755         }
29756     };
29757 }();
29758
29759 /**
29760  * @class Roo.LayoutDialog
29761  * @extends Roo.BasicDialog
29762  * Dialog which provides adjustments for working with a layout in a Dialog.
29763  * Add your necessary layout config options to the dialog's config.<br>
29764  * Example usage (including a nested layout):
29765  * <pre><code>
29766 if(!dialog){
29767     dialog = new Roo.LayoutDialog("download-dlg", {
29768         modal: true,
29769         width:600,
29770         height:450,
29771         shadow:true,
29772         minWidth:500,
29773         minHeight:350,
29774         autoTabs:true,
29775         proxyDrag:true,
29776         // layout config merges with the dialog config
29777         center:{
29778             tabPosition: "top",
29779             alwaysShowTabs: true
29780         }
29781     });
29782     dialog.addKeyListener(27, dialog.hide, dialog);
29783     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29784     dialog.addButton("Build It!", this.getDownload, this);
29785
29786     // we can even add nested layouts
29787     var innerLayout = new Roo.BorderLayout("dl-inner", {
29788         east: {
29789             initialSize: 200,
29790             autoScroll:true,
29791             split:true
29792         },
29793         center: {
29794             autoScroll:true
29795         }
29796     });
29797     innerLayout.beginUpdate();
29798     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29799     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29800     innerLayout.endUpdate(true);
29801
29802     var layout = dialog.getLayout();
29803     layout.beginUpdate();
29804     layout.add("center", new Roo.ContentPanel("standard-panel",
29805                         {title: "Download the Source", fitToFrame:true}));
29806     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29807                {title: "Build your own roo.js"}));
29808     layout.getRegion("center").showPanel(sp);
29809     layout.endUpdate();
29810 }
29811 </code></pre>
29812     * @constructor
29813     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29814     * @param {Object} config configuration options
29815   */
29816 Roo.LayoutDialog = function(el, cfg){
29817     
29818     var config=  cfg;
29819     if (typeof(cfg) == 'undefined') {
29820         config = Roo.apply({}, el);
29821         // not sure why we use documentElement here.. - it should always be body.
29822         // IE7 borks horribly if we use documentElement.
29823         // webkit also does not like documentElement - it creates a body element...
29824         el = Roo.get( document.body || document.documentElement ).createChild();
29825         //config.autoCreate = true;
29826     }
29827     
29828     
29829     config.autoTabs = false;
29830     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29831     this.body.setStyle({overflow:"hidden", position:"relative"});
29832     this.layout = new Roo.BorderLayout(this.body.dom, config);
29833     this.layout.monitorWindowResize = false;
29834     this.el.addClass("x-dlg-auto-layout");
29835     // fix case when center region overwrites center function
29836     this.center = Roo.BasicDialog.prototype.center;
29837     this.on("show", this.layout.layout, this.layout, true);
29838     if (config.items) {
29839         var xitems = config.items;
29840         delete config.items;
29841         Roo.each(xitems, this.addxtype, this);
29842     }
29843     
29844     
29845 };
29846 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29847     /**
29848      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29849      * @deprecated
29850      */
29851     endUpdate : function(){
29852         this.layout.endUpdate();
29853     },
29854
29855     /**
29856      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29857      *  @deprecated
29858      */
29859     beginUpdate : function(){
29860         this.layout.beginUpdate();
29861     },
29862
29863     /**
29864      * Get the BorderLayout for this dialog
29865      * @return {Roo.BorderLayout}
29866      */
29867     getLayout : function(){
29868         return this.layout;
29869     },
29870
29871     showEl : function(){
29872         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29873         if(Roo.isIE7){
29874             this.layout.layout();
29875         }
29876     },
29877
29878     // private
29879     // Use the syncHeightBeforeShow config option to control this automatically
29880     syncBodyHeight : function(){
29881         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29882         if(this.layout){this.layout.layout();}
29883     },
29884     
29885       /**
29886      * Add an xtype element (actually adds to the layout.)
29887      * @return {Object} xdata xtype object data.
29888      */
29889     
29890     addxtype : function(c) {
29891         return this.layout.addxtype(c);
29892     }
29893 });/*
29894  * Based on:
29895  * Ext JS Library 1.1.1
29896  * Copyright(c) 2006-2007, Ext JS, LLC.
29897  *
29898  * Originally Released Under LGPL - original licence link has changed is not relivant.
29899  *
29900  * Fork - LGPL
29901  * <script type="text/javascript">
29902  */
29903  
29904 /**
29905  * @class Roo.MessageBox
29906  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29907  * Example usage:
29908  *<pre><code>
29909 // Basic alert:
29910 Roo.Msg.alert('Status', 'Changes saved successfully.');
29911
29912 // Prompt for user data:
29913 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29914     if (btn == 'ok'){
29915         // process text value...
29916     }
29917 });
29918
29919 // Show a dialog using config options:
29920 Roo.Msg.show({
29921    title:'Save Changes?',
29922    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29923    buttons: Roo.Msg.YESNOCANCEL,
29924    fn: processResult,
29925    animEl: 'elId'
29926 });
29927 </code></pre>
29928  * @singleton
29929  */
29930 Roo.MessageBox = function(){
29931     var dlg, opt, mask, waitTimer;
29932     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29933     var buttons, activeTextEl, bwidth;
29934
29935     // private
29936     var handleButton = function(button){
29937         dlg.hide();
29938         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29939     };
29940
29941     // private
29942     var handleHide = function(){
29943         if(opt && opt.cls){
29944             dlg.el.removeClass(opt.cls);
29945         }
29946         if(waitTimer){
29947             Roo.TaskMgr.stop(waitTimer);
29948             waitTimer = null;
29949         }
29950     };
29951
29952     // private
29953     var updateButtons = function(b){
29954         var width = 0;
29955         if(!b){
29956             buttons["ok"].hide();
29957             buttons["cancel"].hide();
29958             buttons["yes"].hide();
29959             buttons["no"].hide();
29960             dlg.footer.dom.style.display = 'none';
29961             return width;
29962         }
29963         dlg.footer.dom.style.display = '';
29964         for(var k in buttons){
29965             if(typeof buttons[k] != "function"){
29966                 if(b[k]){
29967                     buttons[k].show();
29968                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29969                     width += buttons[k].el.getWidth()+15;
29970                 }else{
29971                     buttons[k].hide();
29972                 }
29973             }
29974         }
29975         return width;
29976     };
29977
29978     // private
29979     var handleEsc = function(d, k, e){
29980         if(opt && opt.closable !== false){
29981             dlg.hide();
29982         }
29983         if(e){
29984             e.stopEvent();
29985         }
29986     };
29987
29988     return {
29989         /**
29990          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29991          * @return {Roo.BasicDialog} The BasicDialog element
29992          */
29993         getDialog : function(){
29994            if(!dlg){
29995                 dlg = new Roo.BasicDialog("x-msg-box", {
29996                     autoCreate : true,
29997                     shadow: true,
29998                     draggable: true,
29999                     resizable:false,
30000                     constraintoviewport:false,
30001                     fixedcenter:true,
30002                     collapsible : false,
30003                     shim:true,
30004                     modal: true,
30005                     width:400, height:100,
30006                     buttonAlign:"center",
30007                     closeClick : function(){
30008                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30009                             handleButton("no");
30010                         }else{
30011                             handleButton("cancel");
30012                         }
30013                     }
30014                 });
30015                 dlg.on("hide", handleHide);
30016                 mask = dlg.mask;
30017                 dlg.addKeyListener(27, handleEsc);
30018                 buttons = {};
30019                 var bt = this.buttonText;
30020                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30021                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30022                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30023                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30024                 bodyEl = dlg.body.createChild({
30025
30026                     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>'
30027                 });
30028                 msgEl = bodyEl.dom.firstChild;
30029                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30030                 textboxEl.enableDisplayMode();
30031                 textboxEl.addKeyListener([10,13], function(){
30032                     if(dlg.isVisible() && opt && opt.buttons){
30033                         if(opt.buttons.ok){
30034                             handleButton("ok");
30035                         }else if(opt.buttons.yes){
30036                             handleButton("yes");
30037                         }
30038                     }
30039                 });
30040                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30041                 textareaEl.enableDisplayMode();
30042                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30043                 progressEl.enableDisplayMode();
30044                 var pf = progressEl.dom.firstChild;
30045                 if (pf) {
30046                     pp = Roo.get(pf.firstChild);
30047                     pp.setHeight(pf.offsetHeight);
30048                 }
30049                 
30050             }
30051             return dlg;
30052         },
30053
30054         /**
30055          * Updates the message box body text
30056          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30057          * the XHTML-compliant non-breaking space character '&amp;#160;')
30058          * @return {Roo.MessageBox} This message box
30059          */
30060         updateText : function(text){
30061             if(!dlg.isVisible() && !opt.width){
30062                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30063             }
30064             msgEl.innerHTML = text || '&#160;';
30065             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30066                         Math.max(opt.minWidth || this.minWidth, bwidth));
30067             if(opt.prompt){
30068                 activeTextEl.setWidth(w);
30069             }
30070             if(dlg.isVisible()){
30071                 dlg.fixedcenter = false;
30072             }
30073             dlg.setContentSize(w, bodyEl.getHeight());
30074             if(dlg.isVisible()){
30075                 dlg.fixedcenter = true;
30076             }
30077             return this;
30078         },
30079
30080         /**
30081          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30082          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30083          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30084          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30085          * @return {Roo.MessageBox} This message box
30086          */
30087         updateProgress : function(value, text){
30088             if(text){
30089                 this.updateText(text);
30090             }
30091             if (pp) { // weird bug on my firefox - for some reason this is not defined
30092                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30093             }
30094             return this;
30095         },        
30096
30097         /**
30098          * Returns true if the message box is currently displayed
30099          * @return {Boolean} True if the message box is visible, else false
30100          */
30101         isVisible : function(){
30102             return dlg && dlg.isVisible();  
30103         },
30104
30105         /**
30106          * Hides the message box if it is displayed
30107          */
30108         hide : function(){
30109             if(this.isVisible()){
30110                 dlg.hide();
30111             }  
30112         },
30113
30114         /**
30115          * Displays a new message box, or reinitializes an existing message box, based on the config options
30116          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30117          * The following config object properties are supported:
30118          * <pre>
30119 Property    Type             Description
30120 ----------  ---------------  ------------------------------------------------------------------------------------
30121 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30122                                    closes (defaults to undefined)
30123 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30124                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30125 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30126                                    progress and wait dialogs will ignore this property and always hide the
30127                                    close button as they can only be closed programmatically.
30128 cls               String           A custom CSS class to apply to the message box element
30129 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30130                                    displayed (defaults to 75)
30131 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30132                                    function will be btn (the name of the button that was clicked, if applicable,
30133                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30134                                    Progress and wait dialogs will ignore this option since they do not respond to
30135                                    user actions and can only be closed programmatically, so any required function
30136                                    should be called by the same code after it closes the dialog.
30137 icon              String           A CSS class that provides a background image to be used as an icon for
30138                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30139 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30140 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30141 modal             Boolean          False to allow user interaction with the page while the message box is
30142                                    displayed (defaults to true)
30143 msg               String           A string that will replace the existing message box body text (defaults
30144                                    to the XHTML-compliant non-breaking space character '&#160;')
30145 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30146 progress          Boolean          True to display a progress bar (defaults to false)
30147 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30148 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30149 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30150 title             String           The title text
30151 value             String           The string value to set into the active textbox element if displayed
30152 wait              Boolean          True to display a progress bar (defaults to false)
30153 width             Number           The width of the dialog in pixels
30154 </pre>
30155          *
30156          * Example usage:
30157          * <pre><code>
30158 Roo.Msg.show({
30159    title: 'Address',
30160    msg: 'Please enter your address:',
30161    width: 300,
30162    buttons: Roo.MessageBox.OKCANCEL,
30163    multiline: true,
30164    fn: saveAddress,
30165    animEl: 'addAddressBtn'
30166 });
30167 </code></pre>
30168          * @param {Object} config Configuration options
30169          * @return {Roo.MessageBox} This message box
30170          */
30171         show : function(options){
30172             if(this.isVisible()){
30173                 this.hide();
30174             }
30175             var d = this.getDialog();
30176             opt = options;
30177             d.setTitle(opt.title || "&#160;");
30178             d.close.setDisplayed(opt.closable !== false);
30179             activeTextEl = textboxEl;
30180             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30181             if(opt.prompt){
30182                 if(opt.multiline){
30183                     textboxEl.hide();
30184                     textareaEl.show();
30185                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30186                         opt.multiline : this.defaultTextHeight);
30187                     activeTextEl = textareaEl;
30188                 }else{
30189                     textboxEl.show();
30190                     textareaEl.hide();
30191                 }
30192             }else{
30193                 textboxEl.hide();
30194                 textareaEl.hide();
30195             }
30196             progressEl.setDisplayed(opt.progress === true);
30197             this.updateProgress(0);
30198             activeTextEl.dom.value = opt.value || "";
30199             if(opt.prompt){
30200                 dlg.setDefaultButton(activeTextEl);
30201             }else{
30202                 var bs = opt.buttons;
30203                 var db = null;
30204                 if(bs && bs.ok){
30205                     db = buttons["ok"];
30206                 }else if(bs && bs.yes){
30207                     db = buttons["yes"];
30208                 }
30209                 dlg.setDefaultButton(db);
30210             }
30211             bwidth = updateButtons(opt.buttons);
30212             this.updateText(opt.msg);
30213             if(opt.cls){
30214                 d.el.addClass(opt.cls);
30215             }
30216             d.proxyDrag = opt.proxyDrag === true;
30217             d.modal = opt.modal !== false;
30218             d.mask = opt.modal !== false ? mask : false;
30219             if(!d.isVisible()){
30220                 // force it to the end of the z-index stack so it gets a cursor in FF
30221                 document.body.appendChild(dlg.el.dom);
30222                 d.animateTarget = null;
30223                 d.show(options.animEl);
30224             }
30225             return this;
30226         },
30227
30228         /**
30229          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30230          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30231          * and closing the message box when the process is complete.
30232          * @param {String} title The title bar text
30233          * @param {String} msg The message box body text
30234          * @return {Roo.MessageBox} This message box
30235          */
30236         progress : function(title, msg){
30237             this.show({
30238                 title : title,
30239                 msg : msg,
30240                 buttons: false,
30241                 progress:true,
30242                 closable:false,
30243                 minWidth: this.minProgressWidth,
30244                 modal : true
30245             });
30246             return this;
30247         },
30248
30249         /**
30250          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30251          * If a callback function is passed it will be called after the user clicks the button, and the
30252          * id of the button that was clicked will be passed as the only parameter to the callback
30253          * (could also be the top-right close button).
30254          * @param {String} title The title bar text
30255          * @param {String} msg The message box body text
30256          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30257          * @param {Object} scope (optional) The scope of the callback function
30258          * @return {Roo.MessageBox} This message box
30259          */
30260         alert : function(title, msg, fn, scope){
30261             this.show({
30262                 title : title,
30263                 msg : msg,
30264                 buttons: this.OK,
30265                 fn: fn,
30266                 scope : scope,
30267                 modal : true
30268             });
30269             return this;
30270         },
30271
30272         /**
30273          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30274          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30275          * You are responsible for closing the message box when the process is complete.
30276          * @param {String} msg The message box body text
30277          * @param {String} title (optional) The title bar text
30278          * @return {Roo.MessageBox} This message box
30279          */
30280         wait : function(msg, title){
30281             this.show({
30282                 title : title,
30283                 msg : msg,
30284                 buttons: false,
30285                 closable:false,
30286                 progress:true,
30287                 modal:true,
30288                 width:300,
30289                 wait:true
30290             });
30291             waitTimer = Roo.TaskMgr.start({
30292                 run: function(i){
30293                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30294                 },
30295                 interval: 1000
30296             });
30297             return this;
30298         },
30299
30300         /**
30301          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30302          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30303          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30304          * @param {String} title The title bar text
30305          * @param {String} msg The message box body text
30306          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30307          * @param {Object} scope (optional) The scope of the callback function
30308          * @return {Roo.MessageBox} This message box
30309          */
30310         confirm : function(title, msg, fn, scope){
30311             this.show({
30312                 title : title,
30313                 msg : msg,
30314                 buttons: this.YESNO,
30315                 fn: fn,
30316                 scope : scope,
30317                 modal : true
30318             });
30319             return this;
30320         },
30321
30322         /**
30323          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30324          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30325          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30326          * (could also be the top-right close button) and the text that was entered will be passed as the two
30327          * parameters to the callback.
30328          * @param {String} title The title bar text
30329          * @param {String} msg The message box body text
30330          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30331          * @param {Object} scope (optional) The scope of the callback function
30332          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30333          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30334          * @return {Roo.MessageBox} This message box
30335          */
30336         prompt : function(title, msg, fn, scope, multiline){
30337             this.show({
30338                 title : title,
30339                 msg : msg,
30340                 buttons: this.OKCANCEL,
30341                 fn: fn,
30342                 minWidth:250,
30343                 scope : scope,
30344                 prompt:true,
30345                 multiline: multiline,
30346                 modal : true
30347             });
30348             return this;
30349         },
30350
30351         /**
30352          * Button config that displays a single OK button
30353          * @type Object
30354          */
30355         OK : {ok:true},
30356         /**
30357          * Button config that displays Yes and No buttons
30358          * @type Object
30359          */
30360         YESNO : {yes:true, no:true},
30361         /**
30362          * Button config that displays OK and Cancel buttons
30363          * @type Object
30364          */
30365         OKCANCEL : {ok:true, cancel:true},
30366         /**
30367          * Button config that displays Yes, No and Cancel buttons
30368          * @type Object
30369          */
30370         YESNOCANCEL : {yes:true, no:true, cancel:true},
30371
30372         /**
30373          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30374          * @type Number
30375          */
30376         defaultTextHeight : 75,
30377         /**
30378          * The maximum width in pixels of the message box (defaults to 600)
30379          * @type Number
30380          */
30381         maxWidth : 600,
30382         /**
30383          * The minimum width in pixels of the message box (defaults to 100)
30384          * @type Number
30385          */
30386         minWidth : 100,
30387         /**
30388          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30389          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30390          * @type Number
30391          */
30392         minProgressWidth : 250,
30393         /**
30394          * An object containing the default button text strings that can be overriden for localized language support.
30395          * Supported properties are: ok, cancel, yes and no.
30396          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30397          * @type Object
30398          */
30399         buttonText : {
30400             ok : "OK",
30401             cancel : "Cancel",
30402             yes : "Yes",
30403             no : "No"
30404         }
30405     };
30406 }();
30407
30408 /**
30409  * Shorthand for {@link Roo.MessageBox}
30410  */
30411 Roo.Msg = Roo.MessageBox;/*
30412  * Based on:
30413  * Ext JS Library 1.1.1
30414  * Copyright(c) 2006-2007, Ext JS, LLC.
30415  *
30416  * Originally Released Under LGPL - original licence link has changed is not relivant.
30417  *
30418  * Fork - LGPL
30419  * <script type="text/javascript">
30420  */
30421 /**
30422  * @class Roo.QuickTips
30423  * Provides attractive and customizable tooltips for any element.
30424  * @singleton
30425  */
30426 Roo.QuickTips = function(){
30427     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30428     var ce, bd, xy, dd;
30429     var visible = false, disabled = true, inited = false;
30430     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30431     
30432     var onOver = function(e){
30433         if(disabled){
30434             return;
30435         }
30436         var t = e.getTarget();
30437         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30438             return;
30439         }
30440         if(ce && t == ce.el){
30441             clearTimeout(hideProc);
30442             return;
30443         }
30444         if(t && tagEls[t.id]){
30445             tagEls[t.id].el = t;
30446             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30447             return;
30448         }
30449         var ttp, et = Roo.fly(t);
30450         var ns = cfg.namespace;
30451         if(tm.interceptTitles && t.title){
30452             ttp = t.title;
30453             t.qtip = ttp;
30454             t.removeAttribute("title");
30455             e.preventDefault();
30456         }else{
30457             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30458         }
30459         if(ttp){
30460             showProc = show.defer(tm.showDelay, tm, [{
30461                 el: t, 
30462                 text: ttp, 
30463                 width: et.getAttributeNS(ns, cfg.width),
30464                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30465                 title: et.getAttributeNS(ns, cfg.title),
30466                     cls: et.getAttributeNS(ns, cfg.cls)
30467             }]);
30468         }
30469     };
30470     
30471     var onOut = function(e){
30472         clearTimeout(showProc);
30473         var t = e.getTarget();
30474         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30475             hideProc = setTimeout(hide, tm.hideDelay);
30476         }
30477     };
30478     
30479     var onMove = function(e){
30480         if(disabled){
30481             return;
30482         }
30483         xy = e.getXY();
30484         xy[1] += 18;
30485         if(tm.trackMouse && ce){
30486             el.setXY(xy);
30487         }
30488     };
30489     
30490     var onDown = function(e){
30491         clearTimeout(showProc);
30492         clearTimeout(hideProc);
30493         if(!e.within(el)){
30494             if(tm.hideOnClick){
30495                 hide();
30496                 tm.disable();
30497                 tm.enable.defer(100, tm);
30498             }
30499         }
30500     };
30501     
30502     var getPad = function(){
30503         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30504     };
30505
30506     var show = function(o){
30507         if(disabled){
30508             return;
30509         }
30510         clearTimeout(dismissProc);
30511         ce = o;
30512         if(removeCls){ // in case manually hidden
30513             el.removeClass(removeCls);
30514             removeCls = null;
30515         }
30516         if(ce.cls){
30517             el.addClass(ce.cls);
30518             removeCls = ce.cls;
30519         }
30520         if(ce.title){
30521             tipTitle.update(ce.title);
30522             tipTitle.show();
30523         }else{
30524             tipTitle.update('');
30525             tipTitle.hide();
30526         }
30527         el.dom.style.width  = tm.maxWidth+'px';
30528         //tipBody.dom.style.width = '';
30529         tipBodyText.update(o.text);
30530         var p = getPad(), w = ce.width;
30531         if(!w){
30532             var td = tipBodyText.dom;
30533             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30534             if(aw > tm.maxWidth){
30535                 w = tm.maxWidth;
30536             }else if(aw < tm.minWidth){
30537                 w = tm.minWidth;
30538             }else{
30539                 w = aw;
30540             }
30541         }
30542         //tipBody.setWidth(w);
30543         el.setWidth(parseInt(w, 10) + p);
30544         if(ce.autoHide === false){
30545             close.setDisplayed(true);
30546             if(dd){
30547                 dd.unlock();
30548             }
30549         }else{
30550             close.setDisplayed(false);
30551             if(dd){
30552                 dd.lock();
30553             }
30554         }
30555         if(xy){
30556             el.avoidY = xy[1]-18;
30557             el.setXY(xy);
30558         }
30559         if(tm.animate){
30560             el.setOpacity(.1);
30561             el.setStyle("visibility", "visible");
30562             el.fadeIn({callback: afterShow});
30563         }else{
30564             afterShow();
30565         }
30566     };
30567     
30568     var afterShow = function(){
30569         if(ce){
30570             el.show();
30571             esc.enable();
30572             if(tm.autoDismiss && ce.autoHide !== false){
30573                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30574             }
30575         }
30576     };
30577     
30578     var hide = function(noanim){
30579         clearTimeout(dismissProc);
30580         clearTimeout(hideProc);
30581         ce = null;
30582         if(el.isVisible()){
30583             esc.disable();
30584             if(noanim !== true && tm.animate){
30585                 el.fadeOut({callback: afterHide});
30586             }else{
30587                 afterHide();
30588             } 
30589         }
30590     };
30591     
30592     var afterHide = function(){
30593         el.hide();
30594         if(removeCls){
30595             el.removeClass(removeCls);
30596             removeCls = null;
30597         }
30598     };
30599     
30600     return {
30601         /**
30602         * @cfg {Number} minWidth
30603         * The minimum width of the quick tip (defaults to 40)
30604         */
30605        minWidth : 40,
30606         /**
30607         * @cfg {Number} maxWidth
30608         * The maximum width of the quick tip (defaults to 300)
30609         */
30610        maxWidth : 300,
30611         /**
30612         * @cfg {Boolean} interceptTitles
30613         * True to automatically use the element's DOM title value if available (defaults to false)
30614         */
30615        interceptTitles : false,
30616         /**
30617         * @cfg {Boolean} trackMouse
30618         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30619         */
30620        trackMouse : false,
30621         /**
30622         * @cfg {Boolean} hideOnClick
30623         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30624         */
30625        hideOnClick : true,
30626         /**
30627         * @cfg {Number} showDelay
30628         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30629         */
30630        showDelay : 500,
30631         /**
30632         * @cfg {Number} hideDelay
30633         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30634         */
30635        hideDelay : 200,
30636         /**
30637         * @cfg {Boolean} autoHide
30638         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30639         * Used in conjunction with hideDelay.
30640         */
30641        autoHide : true,
30642         /**
30643         * @cfg {Boolean}
30644         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30645         * (defaults to true).  Used in conjunction with autoDismissDelay.
30646         */
30647        autoDismiss : true,
30648         /**
30649         * @cfg {Number}
30650         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30651         */
30652        autoDismissDelay : 5000,
30653        /**
30654         * @cfg {Boolean} animate
30655         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30656         */
30657        animate : false,
30658
30659        /**
30660         * @cfg {String} title
30661         * Title text to display (defaults to '').  This can be any valid HTML markup.
30662         */
30663         title: '',
30664        /**
30665         * @cfg {String} text
30666         * Body text to display (defaults to '').  This can be any valid HTML markup.
30667         */
30668         text : '',
30669        /**
30670         * @cfg {String} cls
30671         * A CSS class to apply to the base quick tip element (defaults to '').
30672         */
30673         cls : '',
30674        /**
30675         * @cfg {Number} width
30676         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30677         * minWidth or maxWidth.
30678         */
30679         width : null,
30680
30681     /**
30682      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30683      * or display QuickTips in a page.
30684      */
30685        init : function(){
30686           tm = Roo.QuickTips;
30687           cfg = tm.tagConfig;
30688           if(!inited){
30689               if(!Roo.isReady){ // allow calling of init() before onReady
30690                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30691                   return;
30692               }
30693               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30694               el.fxDefaults = {stopFx: true};
30695               // maximum custom styling
30696               //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>');
30697               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>');              
30698               tipTitle = el.child('h3');
30699               tipTitle.enableDisplayMode("block");
30700               tipBody = el.child('div.x-tip-bd');
30701               tipBodyText = el.child('div.x-tip-bd-inner');
30702               //bdLeft = el.child('div.x-tip-bd-left');
30703               //bdRight = el.child('div.x-tip-bd-right');
30704               close = el.child('div.x-tip-close');
30705               close.enableDisplayMode("block");
30706               close.on("click", hide);
30707               var d = Roo.get(document);
30708               d.on("mousedown", onDown);
30709               d.on("mouseover", onOver);
30710               d.on("mouseout", onOut);
30711               d.on("mousemove", onMove);
30712               esc = d.addKeyListener(27, hide);
30713               esc.disable();
30714               if(Roo.dd.DD){
30715                   dd = el.initDD("default", null, {
30716                       onDrag : function(){
30717                           el.sync();  
30718                       }
30719                   });
30720                   dd.setHandleElId(tipTitle.id);
30721                   dd.lock();
30722               }
30723               inited = true;
30724           }
30725           this.enable(); 
30726        },
30727
30728     /**
30729      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30730      * are supported:
30731      * <pre>
30732 Property    Type                   Description
30733 ----------  ---------------------  ------------------------------------------------------------------------
30734 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30735      * </ul>
30736      * @param {Object} config The config object
30737      */
30738        register : function(config){
30739            var cs = config instanceof Array ? config : arguments;
30740            for(var i = 0, len = cs.length; i < len; i++) {
30741                var c = cs[i];
30742                var target = c.target;
30743                if(target){
30744                    if(target instanceof Array){
30745                        for(var j = 0, jlen = target.length; j < jlen; j++){
30746                            tagEls[target[j]] = c;
30747                        }
30748                    }else{
30749                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30750                    }
30751                }
30752            }
30753        },
30754
30755     /**
30756      * Removes this quick tip from its element and destroys it.
30757      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30758      */
30759        unregister : function(el){
30760            delete tagEls[Roo.id(el)];
30761        },
30762
30763     /**
30764      * Enable this quick tip.
30765      */
30766        enable : function(){
30767            if(inited && disabled){
30768                locks.pop();
30769                if(locks.length < 1){
30770                    disabled = false;
30771                }
30772            }
30773        },
30774
30775     /**
30776      * Disable this quick tip.
30777      */
30778        disable : function(){
30779           disabled = true;
30780           clearTimeout(showProc);
30781           clearTimeout(hideProc);
30782           clearTimeout(dismissProc);
30783           if(ce){
30784               hide(true);
30785           }
30786           locks.push(1);
30787        },
30788
30789     /**
30790      * Returns true if the quick tip is enabled, else false.
30791      */
30792        isEnabled : function(){
30793             return !disabled;
30794        },
30795
30796         // private
30797        tagConfig : {
30798            namespace : "ext",
30799            attribute : "qtip",
30800            width : "width",
30801            target : "target",
30802            title : "qtitle",
30803            hide : "hide",
30804            cls : "qclass"
30805        }
30806    };
30807 }();
30808
30809 // backwards compat
30810 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30811  * Based on:
30812  * Ext JS Library 1.1.1
30813  * Copyright(c) 2006-2007, Ext JS, LLC.
30814  *
30815  * Originally Released Under LGPL - original licence link has changed is not relivant.
30816  *
30817  * Fork - LGPL
30818  * <script type="text/javascript">
30819  */
30820  
30821
30822 /**
30823  * @class Roo.tree.TreePanel
30824  * @extends Roo.data.Tree
30825
30826  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30827  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30828  * @cfg {Boolean} enableDD true to enable drag and drop
30829  * @cfg {Boolean} enableDrag true to enable just drag
30830  * @cfg {Boolean} enableDrop true to enable just drop
30831  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30832  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30833  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30834  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30835  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30836  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30837  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30838  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30839  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30840  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30841  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30842  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30843  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30844  * @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>
30845  * @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>
30846  * 
30847  * @constructor
30848  * @param {String/HTMLElement/Element} el The container element
30849  * @param {Object} config
30850  */
30851 Roo.tree.TreePanel = function(el, config){
30852     var root = false;
30853     var loader = false;
30854     if (config.root) {
30855         root = config.root;
30856         delete config.root;
30857     }
30858     if (config.loader) {
30859         loader = config.loader;
30860         delete config.loader;
30861     }
30862     
30863     Roo.apply(this, config);
30864     Roo.tree.TreePanel.superclass.constructor.call(this);
30865     this.el = Roo.get(el);
30866     this.el.addClass('x-tree');
30867     //console.log(root);
30868     if (root) {
30869         this.setRootNode( Roo.factory(root, Roo.tree));
30870     }
30871     if (loader) {
30872         this.loader = Roo.factory(loader, Roo.tree);
30873     }
30874    /**
30875     * Read-only. The id of the container element becomes this TreePanel's id.
30876     */
30877    this.id = this.el.id;
30878    this.addEvents({
30879         /**
30880         * @event beforeload
30881         * Fires before a node is loaded, return false to cancel
30882         * @param {Node} node The node being loaded
30883         */
30884         "beforeload" : true,
30885         /**
30886         * @event load
30887         * Fires when a node is loaded
30888         * @param {Node} node The node that was loaded
30889         */
30890         "load" : true,
30891         /**
30892         * @event textchange
30893         * Fires when the text for a node is changed
30894         * @param {Node} node The node
30895         * @param {String} text The new text
30896         * @param {String} oldText The old text
30897         */
30898         "textchange" : true,
30899         /**
30900         * @event beforeexpand
30901         * Fires before a node is expanded, return false to cancel.
30902         * @param {Node} node The node
30903         * @param {Boolean} deep
30904         * @param {Boolean} anim
30905         */
30906         "beforeexpand" : true,
30907         /**
30908         * @event beforecollapse
30909         * Fires before a node is collapsed, return false to cancel.
30910         * @param {Node} node The node
30911         * @param {Boolean} deep
30912         * @param {Boolean} anim
30913         */
30914         "beforecollapse" : true,
30915         /**
30916         * @event expand
30917         * Fires when a node is expanded
30918         * @param {Node} node The node
30919         */
30920         "expand" : true,
30921         /**
30922         * @event disabledchange
30923         * Fires when the disabled status of a node changes
30924         * @param {Node} node The node
30925         * @param {Boolean} disabled
30926         */
30927         "disabledchange" : true,
30928         /**
30929         * @event collapse
30930         * Fires when a node is collapsed
30931         * @param {Node} node The node
30932         */
30933         "collapse" : true,
30934         /**
30935         * @event beforeclick
30936         * Fires before click processing on a node. Return false to cancel the default action.
30937         * @param {Node} node The node
30938         * @param {Roo.EventObject} e The event object
30939         */
30940         "beforeclick":true,
30941         /**
30942         * @event checkchange
30943         * Fires when a node with a checkbox's checked property changes
30944         * @param {Node} this This node
30945         * @param {Boolean} checked
30946         */
30947         "checkchange":true,
30948         /**
30949         * @event click
30950         * Fires when a node is clicked
30951         * @param {Node} node The node
30952         * @param {Roo.EventObject} e The event object
30953         */
30954         "click":true,
30955         /**
30956         * @event dblclick
30957         * Fires when a node is double clicked
30958         * @param {Node} node The node
30959         * @param {Roo.EventObject} e The event object
30960         */
30961         "dblclick":true,
30962         /**
30963         * @event contextmenu
30964         * Fires when a node is right clicked
30965         * @param {Node} node The node
30966         * @param {Roo.EventObject} e The event object
30967         */
30968         "contextmenu":true,
30969         /**
30970         * @event beforechildrenrendered
30971         * Fires right before the child nodes for a node are rendered
30972         * @param {Node} node The node
30973         */
30974         "beforechildrenrendered":true,
30975        /**
30976              * @event startdrag
30977              * Fires when a node starts being dragged
30978              * @param {Roo.tree.TreePanel} this
30979              * @param {Roo.tree.TreeNode} node
30980              * @param {event} e The raw browser event
30981              */ 
30982             "startdrag" : true,
30983             /**
30984              * @event enddrag
30985              * Fires when a drag operation is complete
30986              * @param {Roo.tree.TreePanel} this
30987              * @param {Roo.tree.TreeNode} node
30988              * @param {event} e The raw browser event
30989              */
30990             "enddrag" : true,
30991             /**
30992              * @event dragdrop
30993              * Fires when a dragged node is dropped on a valid DD target
30994              * @param {Roo.tree.TreePanel} this
30995              * @param {Roo.tree.TreeNode} node
30996              * @param {DD} dd The dd it was dropped on
30997              * @param {event} e The raw browser event
30998              */
30999             "dragdrop" : true,
31000             /**
31001              * @event beforenodedrop
31002              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31003              * passed to handlers has the following properties:<br />
31004              * <ul style="padding:5px;padding-left:16px;">
31005              * <li>tree - The TreePanel</li>
31006              * <li>target - The node being targeted for the drop</li>
31007              * <li>data - The drag data from the drag source</li>
31008              * <li>point - The point of the drop - append, above or below</li>
31009              * <li>source - The drag source</li>
31010              * <li>rawEvent - Raw mouse event</li>
31011              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31012              * to be inserted by setting them on this object.</li>
31013              * <li>cancel - Set this to true to cancel the drop.</li>
31014              * </ul>
31015              * @param {Object} dropEvent
31016              */
31017             "beforenodedrop" : true,
31018             /**
31019              * @event nodedrop
31020              * Fires after a DD object is dropped on a node in this tree. The dropEvent
31021              * passed to handlers has the following properties:<br />
31022              * <ul style="padding:5px;padding-left:16px;">
31023              * <li>tree - The TreePanel</li>
31024              * <li>target - The node being targeted for the drop</li>
31025              * <li>data - The drag data from the drag source</li>
31026              * <li>point - The point of the drop - append, above or below</li>
31027              * <li>source - The drag source</li>
31028              * <li>rawEvent - Raw mouse event</li>
31029              * <li>dropNode - Dropped node(s).</li>
31030              * </ul>
31031              * @param {Object} dropEvent
31032              */
31033             "nodedrop" : true,
31034              /**
31035              * @event nodedragover
31036              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31037              * passed to handlers has the following properties:<br />
31038              * <ul style="padding:5px;padding-left:16px;">
31039              * <li>tree - The TreePanel</li>
31040              * <li>target - The node being targeted for the drop</li>
31041              * <li>data - The drag data from the drag source</li>
31042              * <li>point - The point of the drop - append, above or below</li>
31043              * <li>source - The drag source</li>
31044              * <li>rawEvent - Raw mouse event</li>
31045              * <li>dropNode - Drop node(s) provided by the source.</li>
31046              * <li>cancel - Set this to true to signal drop not allowed.</li>
31047              * </ul>
31048              * @param {Object} dragOverEvent
31049              */
31050             "nodedragover" : true
31051         
31052    });
31053    if(this.singleExpand){
31054        this.on("beforeexpand", this.restrictExpand, this);
31055    }
31056 };
31057 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31058     rootVisible : true,
31059     animate: Roo.enableFx,
31060     lines : true,
31061     enableDD : false,
31062     hlDrop : Roo.enableFx,
31063   
31064     renderer: false,
31065     
31066     rendererTip: false,
31067     // private
31068     restrictExpand : function(node){
31069         var p = node.parentNode;
31070         if(p){
31071             if(p.expandedChild && p.expandedChild.parentNode == p){
31072                 p.expandedChild.collapse();
31073             }
31074             p.expandedChild = node;
31075         }
31076     },
31077
31078     // private override
31079     setRootNode : function(node){
31080         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31081         if(!this.rootVisible){
31082             node.ui = new Roo.tree.RootTreeNodeUI(node);
31083         }
31084         return node;
31085     },
31086
31087     /**
31088      * Returns the container element for this TreePanel
31089      */
31090     getEl : function(){
31091         return this.el;
31092     },
31093
31094     /**
31095      * Returns the default TreeLoader for this TreePanel
31096      */
31097     getLoader : function(){
31098         return this.loader;
31099     },
31100
31101     /**
31102      * Expand all nodes
31103      */
31104     expandAll : function(){
31105         this.root.expand(true);
31106     },
31107
31108     /**
31109      * Collapse all nodes
31110      */
31111     collapseAll : function(){
31112         this.root.collapse(true);
31113     },
31114
31115     /**
31116      * Returns the selection model used by this TreePanel
31117      */
31118     getSelectionModel : function(){
31119         if(!this.selModel){
31120             this.selModel = new Roo.tree.DefaultSelectionModel();
31121         }
31122         return this.selModel;
31123     },
31124
31125     /**
31126      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31127      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31128      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31129      * @return {Array}
31130      */
31131     getChecked : function(a, startNode){
31132         startNode = startNode || this.root;
31133         var r = [];
31134         var f = function(){
31135             if(this.attributes.checked){
31136                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31137             }
31138         }
31139         startNode.cascade(f);
31140         return r;
31141     },
31142
31143     /**
31144      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31145      * @param {String} path
31146      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31147      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31148      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31149      */
31150     expandPath : function(path, attr, callback){
31151         attr = attr || "id";
31152         var keys = path.split(this.pathSeparator);
31153         var curNode = this.root;
31154         if(curNode.attributes[attr] != keys[1]){ // invalid root
31155             if(callback){
31156                 callback(false, null);
31157             }
31158             return;
31159         }
31160         var index = 1;
31161         var f = function(){
31162             if(++index == keys.length){
31163                 if(callback){
31164                     callback(true, curNode);
31165                 }
31166                 return;
31167             }
31168             var c = curNode.findChild(attr, keys[index]);
31169             if(!c){
31170                 if(callback){
31171                     callback(false, curNode);
31172                 }
31173                 return;
31174             }
31175             curNode = c;
31176             c.expand(false, false, f);
31177         };
31178         curNode.expand(false, false, f);
31179     },
31180
31181     /**
31182      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31183      * @param {String} path
31184      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31185      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31186      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31187      */
31188     selectPath : function(path, attr, callback){
31189         attr = attr || "id";
31190         var keys = path.split(this.pathSeparator);
31191         var v = keys.pop();
31192         if(keys.length > 0){
31193             var f = function(success, node){
31194                 if(success && node){
31195                     var n = node.findChild(attr, v);
31196                     if(n){
31197                         n.select();
31198                         if(callback){
31199                             callback(true, n);
31200                         }
31201                     }else if(callback){
31202                         callback(false, n);
31203                     }
31204                 }else{
31205                     if(callback){
31206                         callback(false, n);
31207                     }
31208                 }
31209             };
31210             this.expandPath(keys.join(this.pathSeparator), attr, f);
31211         }else{
31212             this.root.select();
31213             if(callback){
31214                 callback(true, this.root);
31215             }
31216         }
31217     },
31218
31219     getTreeEl : function(){
31220         return this.el;
31221     },
31222
31223     /**
31224      * Trigger rendering of this TreePanel
31225      */
31226     render : function(){
31227         if (this.innerCt) {
31228             return this; // stop it rendering more than once!!
31229         }
31230         
31231         this.innerCt = this.el.createChild({tag:"ul",
31232                cls:"x-tree-root-ct " +
31233                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31234
31235         if(this.containerScroll){
31236             Roo.dd.ScrollManager.register(this.el);
31237         }
31238         if((this.enableDD || this.enableDrop) && !this.dropZone){
31239            /**
31240             * The dropZone used by this tree if drop is enabled
31241             * @type Roo.tree.TreeDropZone
31242             */
31243              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31244                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31245            });
31246         }
31247         if((this.enableDD || this.enableDrag) && !this.dragZone){
31248            /**
31249             * The dragZone used by this tree if drag is enabled
31250             * @type Roo.tree.TreeDragZone
31251             */
31252             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31253                ddGroup: this.ddGroup || "TreeDD",
31254                scroll: this.ddScroll
31255            });
31256         }
31257         this.getSelectionModel().init(this);
31258         if (!this.root) {
31259             console.log("ROOT not set in tree");
31260             return;
31261         }
31262         this.root.render();
31263         if(!this.rootVisible){
31264             this.root.renderChildren();
31265         }
31266         return this;
31267     }
31268 });/*
31269  * Based on:
31270  * Ext JS Library 1.1.1
31271  * Copyright(c) 2006-2007, Ext JS, LLC.
31272  *
31273  * Originally Released Under LGPL - original licence link has changed is not relivant.
31274  *
31275  * Fork - LGPL
31276  * <script type="text/javascript">
31277  */
31278  
31279
31280 /**
31281  * @class Roo.tree.DefaultSelectionModel
31282  * @extends Roo.util.Observable
31283  * The default single selection for a TreePanel.
31284  */
31285 Roo.tree.DefaultSelectionModel = function(){
31286    this.selNode = null;
31287    
31288    this.addEvents({
31289        /**
31290         * @event selectionchange
31291         * Fires when the selected node changes
31292         * @param {DefaultSelectionModel} this
31293         * @param {TreeNode} node the new selection
31294         */
31295        "selectionchange" : true,
31296
31297        /**
31298         * @event beforeselect
31299         * Fires before the selected node changes, return false to cancel the change
31300         * @param {DefaultSelectionModel} this
31301         * @param {TreeNode} node the new selection
31302         * @param {TreeNode} node the old selection
31303         */
31304        "beforeselect" : true
31305    });
31306 };
31307
31308 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31309     init : function(tree){
31310         this.tree = tree;
31311         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31312         tree.on("click", this.onNodeClick, this);
31313     },
31314     
31315     onNodeClick : function(node, e){
31316         if (e.ctrlKey && this.selNode == node)  {
31317             this.unselect(node);
31318             return;
31319         }
31320         this.select(node);
31321     },
31322     
31323     /**
31324      * Select a node.
31325      * @param {TreeNode} node The node to select
31326      * @return {TreeNode} The selected node
31327      */
31328     select : function(node){
31329         var last = this.selNode;
31330         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31331             if(last){
31332                 last.ui.onSelectedChange(false);
31333             }
31334             this.selNode = node;
31335             node.ui.onSelectedChange(true);
31336             this.fireEvent("selectionchange", this, node, last);
31337         }
31338         return node;
31339     },
31340     
31341     /**
31342      * Deselect a node.
31343      * @param {TreeNode} node The node to unselect
31344      */
31345     unselect : function(node){
31346         if(this.selNode == node){
31347             this.clearSelections();
31348         }    
31349     },
31350     
31351     /**
31352      * Clear all selections
31353      */
31354     clearSelections : function(){
31355         var n = this.selNode;
31356         if(n){
31357             n.ui.onSelectedChange(false);
31358             this.selNode = null;
31359             this.fireEvent("selectionchange", this, null);
31360         }
31361         return n;
31362     },
31363     
31364     /**
31365      * Get the selected node
31366      * @return {TreeNode} The selected node
31367      */
31368     getSelectedNode : function(){
31369         return this.selNode;    
31370     },
31371     
31372     /**
31373      * Returns true if the node is selected
31374      * @param {TreeNode} node The node to check
31375      * @return {Boolean}
31376      */
31377     isSelected : function(node){
31378         return this.selNode == node;  
31379     },
31380
31381     /**
31382      * Selects the node above the selected node in the tree, intelligently walking the nodes
31383      * @return TreeNode The new selection
31384      */
31385     selectPrevious : function(){
31386         var s = this.selNode || this.lastSelNode;
31387         if(!s){
31388             return null;
31389         }
31390         var ps = s.previousSibling;
31391         if(ps){
31392             if(!ps.isExpanded() || ps.childNodes.length < 1){
31393                 return this.select(ps);
31394             } else{
31395                 var lc = ps.lastChild;
31396                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31397                     lc = lc.lastChild;
31398                 }
31399                 return this.select(lc);
31400             }
31401         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31402             return this.select(s.parentNode);
31403         }
31404         return null;
31405     },
31406
31407     /**
31408      * Selects the node above the selected node in the tree, intelligently walking the nodes
31409      * @return TreeNode The new selection
31410      */
31411     selectNext : function(){
31412         var s = this.selNode || this.lastSelNode;
31413         if(!s){
31414             return null;
31415         }
31416         if(s.firstChild && s.isExpanded()){
31417              return this.select(s.firstChild);
31418          }else if(s.nextSibling){
31419              return this.select(s.nextSibling);
31420          }else if(s.parentNode){
31421             var newS = null;
31422             s.parentNode.bubble(function(){
31423                 if(this.nextSibling){
31424                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31425                     return false;
31426                 }
31427             });
31428             return newS;
31429          }
31430         return null;
31431     },
31432
31433     onKeyDown : function(e){
31434         var s = this.selNode || this.lastSelNode;
31435         // undesirable, but required
31436         var sm = this;
31437         if(!s){
31438             return;
31439         }
31440         var k = e.getKey();
31441         switch(k){
31442              case e.DOWN:
31443                  e.stopEvent();
31444                  this.selectNext();
31445              break;
31446              case e.UP:
31447                  e.stopEvent();
31448                  this.selectPrevious();
31449              break;
31450              case e.RIGHT:
31451                  e.preventDefault();
31452                  if(s.hasChildNodes()){
31453                      if(!s.isExpanded()){
31454                          s.expand();
31455                      }else if(s.firstChild){
31456                          this.select(s.firstChild, e);
31457                      }
31458                  }
31459              break;
31460              case e.LEFT:
31461                  e.preventDefault();
31462                  if(s.hasChildNodes() && s.isExpanded()){
31463                      s.collapse();
31464                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31465                      this.select(s.parentNode, e);
31466                  }
31467              break;
31468         };
31469     }
31470 });
31471
31472 /**
31473  * @class Roo.tree.MultiSelectionModel
31474  * @extends Roo.util.Observable
31475  * Multi selection for a TreePanel.
31476  */
31477 Roo.tree.MultiSelectionModel = function(){
31478    this.selNodes = [];
31479    this.selMap = {};
31480    this.addEvents({
31481        /**
31482         * @event selectionchange
31483         * Fires when the selected nodes change
31484         * @param {MultiSelectionModel} this
31485         * @param {Array} nodes Array of the selected nodes
31486         */
31487        "selectionchange" : true
31488    });
31489 };
31490
31491 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31492     init : function(tree){
31493         this.tree = tree;
31494         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31495         tree.on("click", this.onNodeClick, this);
31496     },
31497     
31498     onNodeClick : function(node, e){
31499         this.select(node, e, e.ctrlKey);
31500     },
31501     
31502     /**
31503      * Select a node.
31504      * @param {TreeNode} node The node to select
31505      * @param {EventObject} e (optional) An event associated with the selection
31506      * @param {Boolean} keepExisting True to retain existing selections
31507      * @return {TreeNode} The selected node
31508      */
31509     select : function(node, e, keepExisting){
31510         if(keepExisting !== true){
31511             this.clearSelections(true);
31512         }
31513         if(this.isSelected(node)){
31514             this.lastSelNode = node;
31515             return node;
31516         }
31517         this.selNodes.push(node);
31518         this.selMap[node.id] = node;
31519         this.lastSelNode = node;
31520         node.ui.onSelectedChange(true);
31521         this.fireEvent("selectionchange", this, this.selNodes);
31522         return node;
31523     },
31524     
31525     /**
31526      * Deselect a node.
31527      * @param {TreeNode} node The node to unselect
31528      */
31529     unselect : function(node){
31530         if(this.selMap[node.id]){
31531             node.ui.onSelectedChange(false);
31532             var sn = this.selNodes;
31533             var index = -1;
31534             if(sn.indexOf){
31535                 index = sn.indexOf(node);
31536             }else{
31537                 for(var i = 0, len = sn.length; i < len; i++){
31538                     if(sn[i] == node){
31539                         index = i;
31540                         break;
31541                     }
31542                 }
31543             }
31544             if(index != -1){
31545                 this.selNodes.splice(index, 1);
31546             }
31547             delete this.selMap[node.id];
31548             this.fireEvent("selectionchange", this, this.selNodes);
31549         }
31550     },
31551     
31552     /**
31553      * Clear all selections
31554      */
31555     clearSelections : function(suppressEvent){
31556         var sn = this.selNodes;
31557         if(sn.length > 0){
31558             for(var i = 0, len = sn.length; i < len; i++){
31559                 sn[i].ui.onSelectedChange(false);
31560             }
31561             this.selNodes = [];
31562             this.selMap = {};
31563             if(suppressEvent !== true){
31564                 this.fireEvent("selectionchange", this, this.selNodes);
31565             }
31566         }
31567     },
31568     
31569     /**
31570      * Returns true if the node is selected
31571      * @param {TreeNode} node The node to check
31572      * @return {Boolean}
31573      */
31574     isSelected : function(node){
31575         return this.selMap[node.id] ? true : false;  
31576     },
31577     
31578     /**
31579      * Returns an array of the selected nodes
31580      * @return {Array}
31581      */
31582     getSelectedNodes : function(){
31583         return this.selNodes;    
31584     },
31585
31586     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31587
31588     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31589
31590     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31591 });/*
31592  * Based on:
31593  * Ext JS Library 1.1.1
31594  * Copyright(c) 2006-2007, Ext JS, LLC.
31595  *
31596  * Originally Released Under LGPL - original licence link has changed is not relivant.
31597  *
31598  * Fork - LGPL
31599  * <script type="text/javascript">
31600  */
31601  
31602 /**
31603  * @class Roo.tree.TreeNode
31604  * @extends Roo.data.Node
31605  * @cfg {String} text The text for this node
31606  * @cfg {Boolean} expanded true to start the node expanded
31607  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31608  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31609  * @cfg {Boolean} disabled true to start the node disabled
31610  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31611  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31612  * @cfg {String} cls A css class to be added to the node
31613  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31614  * @cfg {String} href URL of the link used for the node (defaults to #)
31615  * @cfg {String} hrefTarget target frame for the link
31616  * @cfg {String} qtip An Ext QuickTip for the node
31617  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31618  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31619  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31620  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31621  * (defaults to undefined with no checkbox rendered)
31622  * @constructor
31623  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31624  */
31625 Roo.tree.TreeNode = function(attributes){
31626     attributes = attributes || {};
31627     if(typeof attributes == "string"){
31628         attributes = {text: attributes};
31629     }
31630     this.childrenRendered = false;
31631     this.rendered = false;
31632     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31633     this.expanded = attributes.expanded === true;
31634     this.isTarget = attributes.isTarget !== false;
31635     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31636     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31637
31638     /**
31639      * Read-only. The text for this node. To change it use setText().
31640      * @type String
31641      */
31642     this.text = attributes.text;
31643     /**
31644      * True if this node is disabled.
31645      * @type Boolean
31646      */
31647     this.disabled = attributes.disabled === true;
31648
31649     this.addEvents({
31650         /**
31651         * @event textchange
31652         * Fires when the text for this node is changed
31653         * @param {Node} this This node
31654         * @param {String} text The new text
31655         * @param {String} oldText The old text
31656         */
31657         "textchange" : true,
31658         /**
31659         * @event beforeexpand
31660         * Fires before this node is expanded, return false to cancel.
31661         * @param {Node} this This node
31662         * @param {Boolean} deep
31663         * @param {Boolean} anim
31664         */
31665         "beforeexpand" : true,
31666         /**
31667         * @event beforecollapse
31668         * Fires before this node is collapsed, return false to cancel.
31669         * @param {Node} this This node
31670         * @param {Boolean} deep
31671         * @param {Boolean} anim
31672         */
31673         "beforecollapse" : true,
31674         /**
31675         * @event expand
31676         * Fires when this node is expanded
31677         * @param {Node} this This node
31678         */
31679         "expand" : true,
31680         /**
31681         * @event disabledchange
31682         * Fires when the disabled status of this node changes
31683         * @param {Node} this This node
31684         * @param {Boolean} disabled
31685         */
31686         "disabledchange" : true,
31687         /**
31688         * @event collapse
31689         * Fires when this node is collapsed
31690         * @param {Node} this This node
31691         */
31692         "collapse" : true,
31693         /**
31694         * @event beforeclick
31695         * Fires before click processing. Return false to cancel the default action.
31696         * @param {Node} this This node
31697         * @param {Roo.EventObject} e The event object
31698         */
31699         "beforeclick":true,
31700         /**
31701         * @event checkchange
31702         * Fires when a node with a checkbox's checked property changes
31703         * @param {Node} this This node
31704         * @param {Boolean} checked
31705         */
31706         "checkchange":true,
31707         /**
31708         * @event click
31709         * Fires when this node is clicked
31710         * @param {Node} this This node
31711         * @param {Roo.EventObject} e The event object
31712         */
31713         "click":true,
31714         /**
31715         * @event dblclick
31716         * Fires when this node is double clicked
31717         * @param {Node} this This node
31718         * @param {Roo.EventObject} e The event object
31719         */
31720         "dblclick":true,
31721         /**
31722         * @event contextmenu
31723         * Fires when this node is right clicked
31724         * @param {Node} this This node
31725         * @param {Roo.EventObject} e The event object
31726         */
31727         "contextmenu":true,
31728         /**
31729         * @event beforechildrenrendered
31730         * Fires right before the child nodes for this node are rendered
31731         * @param {Node} this This node
31732         */
31733         "beforechildrenrendered":true
31734     });
31735
31736     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31737
31738     /**
31739      * Read-only. The UI for this node
31740      * @type TreeNodeUI
31741      */
31742     this.ui = new uiClass(this);
31743 };
31744 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31745     preventHScroll: true,
31746     /**
31747      * Returns true if this node is expanded
31748      * @return {Boolean}
31749      */
31750     isExpanded : function(){
31751         return this.expanded;
31752     },
31753
31754     /**
31755      * Returns the UI object for this node
31756      * @return {TreeNodeUI}
31757      */
31758     getUI : function(){
31759         return this.ui;
31760     },
31761
31762     // private override
31763     setFirstChild : function(node){
31764         var of = this.firstChild;
31765         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31766         if(this.childrenRendered && of && node != of){
31767             of.renderIndent(true, true);
31768         }
31769         if(this.rendered){
31770             this.renderIndent(true, true);
31771         }
31772     },
31773
31774     // private override
31775     setLastChild : function(node){
31776         var ol = this.lastChild;
31777         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31778         if(this.childrenRendered && ol && node != ol){
31779             ol.renderIndent(true, true);
31780         }
31781         if(this.rendered){
31782             this.renderIndent(true, true);
31783         }
31784     },
31785
31786     // these methods are overridden to provide lazy rendering support
31787     // private override
31788     appendChild : function(){
31789         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31790         if(node && this.childrenRendered){
31791             node.render();
31792         }
31793         this.ui.updateExpandIcon();
31794         return node;
31795     },
31796
31797     // private override
31798     removeChild : function(node){
31799         this.ownerTree.getSelectionModel().unselect(node);
31800         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31801         // if it's been rendered remove dom node
31802         if(this.childrenRendered){
31803             node.ui.remove();
31804         }
31805         if(this.childNodes.length < 1){
31806             this.collapse(false, false);
31807         }else{
31808             this.ui.updateExpandIcon();
31809         }
31810         if(!this.firstChild) {
31811             this.childrenRendered = false;
31812         }
31813         return node;
31814     },
31815
31816     // private override
31817     insertBefore : function(node, refNode){
31818         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31819         if(newNode && refNode && this.childrenRendered){
31820             node.render();
31821         }
31822         this.ui.updateExpandIcon();
31823         return newNode;
31824     },
31825
31826     /**
31827      * Sets the text for this node
31828      * @param {String} text
31829      */
31830     setText : function(text){
31831         var oldText = this.text;
31832         this.text = text;
31833         this.attributes.text = text;
31834         if(this.rendered){ // event without subscribing
31835             this.ui.onTextChange(this, text, oldText);
31836         }
31837         this.fireEvent("textchange", this, text, oldText);
31838     },
31839
31840     /**
31841      * Triggers selection of this node
31842      */
31843     select : function(){
31844         this.getOwnerTree().getSelectionModel().select(this);
31845     },
31846
31847     /**
31848      * Triggers deselection of this node
31849      */
31850     unselect : function(){
31851         this.getOwnerTree().getSelectionModel().unselect(this);
31852     },
31853
31854     /**
31855      * Returns true if this node is selected
31856      * @return {Boolean}
31857      */
31858     isSelected : function(){
31859         return this.getOwnerTree().getSelectionModel().isSelected(this);
31860     },
31861
31862     /**
31863      * Expand this node.
31864      * @param {Boolean} deep (optional) True to expand all children as well
31865      * @param {Boolean} anim (optional) false to cancel the default animation
31866      * @param {Function} callback (optional) A callback to be called when
31867      * expanding this node completes (does not wait for deep expand to complete).
31868      * Called with 1 parameter, this node.
31869      */
31870     expand : function(deep, anim, callback){
31871         if(!this.expanded){
31872             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31873                 return;
31874             }
31875             if(!this.childrenRendered){
31876                 this.renderChildren();
31877             }
31878             this.expanded = true;
31879             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31880                 this.ui.animExpand(function(){
31881                     this.fireEvent("expand", this);
31882                     if(typeof callback == "function"){
31883                         callback(this);
31884                     }
31885                     if(deep === true){
31886                         this.expandChildNodes(true);
31887                     }
31888                 }.createDelegate(this));
31889                 return;
31890             }else{
31891                 this.ui.expand();
31892                 this.fireEvent("expand", this);
31893                 if(typeof callback == "function"){
31894                     callback(this);
31895                 }
31896             }
31897         }else{
31898            if(typeof callback == "function"){
31899                callback(this);
31900            }
31901         }
31902         if(deep === true){
31903             this.expandChildNodes(true);
31904         }
31905     },
31906
31907     isHiddenRoot : function(){
31908         return this.isRoot && !this.getOwnerTree().rootVisible;
31909     },
31910
31911     /**
31912      * Collapse this node.
31913      * @param {Boolean} deep (optional) True to collapse all children as well
31914      * @param {Boolean} anim (optional) false to cancel the default animation
31915      */
31916     collapse : function(deep, anim){
31917         if(this.expanded && !this.isHiddenRoot()){
31918             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31919                 return;
31920             }
31921             this.expanded = false;
31922             if((this.getOwnerTree().animate && anim !== false) || anim){
31923                 this.ui.animCollapse(function(){
31924                     this.fireEvent("collapse", this);
31925                     if(deep === true){
31926                         this.collapseChildNodes(true);
31927                     }
31928                 }.createDelegate(this));
31929                 return;
31930             }else{
31931                 this.ui.collapse();
31932                 this.fireEvent("collapse", this);
31933             }
31934         }
31935         if(deep === true){
31936             var cs = this.childNodes;
31937             for(var i = 0, len = cs.length; i < len; i++) {
31938                 cs[i].collapse(true, false);
31939             }
31940         }
31941     },
31942
31943     // private
31944     delayedExpand : function(delay){
31945         if(!this.expandProcId){
31946             this.expandProcId = this.expand.defer(delay, this);
31947         }
31948     },
31949
31950     // private
31951     cancelExpand : function(){
31952         if(this.expandProcId){
31953             clearTimeout(this.expandProcId);
31954         }
31955         this.expandProcId = false;
31956     },
31957
31958     /**
31959      * Toggles expanded/collapsed state of the node
31960      */
31961     toggle : function(){
31962         if(this.expanded){
31963             this.collapse();
31964         }else{
31965             this.expand();
31966         }
31967     },
31968
31969     /**
31970      * Ensures all parent nodes are expanded
31971      */
31972     ensureVisible : function(callback){
31973         var tree = this.getOwnerTree();
31974         tree.expandPath(this.parentNode.getPath(), false, function(){
31975             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31976             Roo.callback(callback);
31977         }.createDelegate(this));
31978     },
31979
31980     /**
31981      * Expand all child nodes
31982      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31983      */
31984     expandChildNodes : function(deep){
31985         var cs = this.childNodes;
31986         for(var i = 0, len = cs.length; i < len; i++) {
31987                 cs[i].expand(deep);
31988         }
31989     },
31990
31991     /**
31992      * Collapse all child nodes
31993      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31994      */
31995     collapseChildNodes : function(deep){
31996         var cs = this.childNodes;
31997         for(var i = 0, len = cs.length; i < len; i++) {
31998                 cs[i].collapse(deep);
31999         }
32000     },
32001
32002     /**
32003      * Disables this node
32004      */
32005     disable : function(){
32006         this.disabled = true;
32007         this.unselect();
32008         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32009             this.ui.onDisableChange(this, true);
32010         }
32011         this.fireEvent("disabledchange", this, true);
32012     },
32013
32014     /**
32015      * Enables this node
32016      */
32017     enable : function(){
32018         this.disabled = false;
32019         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32020             this.ui.onDisableChange(this, false);
32021         }
32022         this.fireEvent("disabledchange", this, false);
32023     },
32024
32025     // private
32026     renderChildren : function(suppressEvent){
32027         if(suppressEvent !== false){
32028             this.fireEvent("beforechildrenrendered", this);
32029         }
32030         var cs = this.childNodes;
32031         for(var i = 0, len = cs.length; i < len; i++){
32032             cs[i].render(true);
32033         }
32034         this.childrenRendered = true;
32035     },
32036
32037     // private
32038     sort : function(fn, scope){
32039         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32040         if(this.childrenRendered){
32041             var cs = this.childNodes;
32042             for(var i = 0, len = cs.length; i < len; i++){
32043                 cs[i].render(true);
32044             }
32045         }
32046     },
32047
32048     // private
32049     render : function(bulkRender){
32050         this.ui.render(bulkRender);
32051         if(!this.rendered){
32052             this.rendered = true;
32053             if(this.expanded){
32054                 this.expanded = false;
32055                 this.expand(false, false);
32056             }
32057         }
32058     },
32059
32060     // private
32061     renderIndent : function(deep, refresh){
32062         if(refresh){
32063             this.ui.childIndent = null;
32064         }
32065         this.ui.renderIndent();
32066         if(deep === true && this.childrenRendered){
32067             var cs = this.childNodes;
32068             for(var i = 0, len = cs.length; i < len; i++){
32069                 cs[i].renderIndent(true, refresh);
32070             }
32071         }
32072     }
32073 });/*
32074  * Based on:
32075  * Ext JS Library 1.1.1
32076  * Copyright(c) 2006-2007, Ext JS, LLC.
32077  *
32078  * Originally Released Under LGPL - original licence link has changed is not relivant.
32079  *
32080  * Fork - LGPL
32081  * <script type="text/javascript">
32082  */
32083  
32084 /**
32085  * @class Roo.tree.AsyncTreeNode
32086  * @extends Roo.tree.TreeNode
32087  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32088  * @constructor
32089  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32090  */
32091  Roo.tree.AsyncTreeNode = function(config){
32092     this.loaded = false;
32093     this.loading = false;
32094     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32095     /**
32096     * @event beforeload
32097     * Fires before this node is loaded, return false to cancel
32098     * @param {Node} this This node
32099     */
32100     this.addEvents({'beforeload':true, 'load': true});
32101     /**
32102     * @event load
32103     * Fires when this node is loaded
32104     * @param {Node} this This node
32105     */
32106     /**
32107      * The loader used by this node (defaults to using the tree's defined loader)
32108      * @type TreeLoader
32109      * @property loader
32110      */
32111 };
32112 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32113     expand : function(deep, anim, callback){
32114         if(this.loading){ // if an async load is already running, waiting til it's done
32115             var timer;
32116             var f = function(){
32117                 if(!this.loading){ // done loading
32118                     clearInterval(timer);
32119                     this.expand(deep, anim, callback);
32120                 }
32121             }.createDelegate(this);
32122             timer = setInterval(f, 200);
32123             return;
32124         }
32125         if(!this.loaded){
32126             if(this.fireEvent("beforeload", this) === false){
32127                 return;
32128             }
32129             this.loading = true;
32130             this.ui.beforeLoad(this);
32131             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32132             if(loader){
32133                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32134                 return;
32135             }
32136         }
32137         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32138     },
32139     
32140     /**
32141      * Returns true if this node is currently loading
32142      * @return {Boolean}
32143      */
32144     isLoading : function(){
32145         return this.loading;  
32146     },
32147     
32148     loadComplete : function(deep, anim, callback){
32149         this.loading = false;
32150         this.loaded = true;
32151         this.ui.afterLoad(this);
32152         this.fireEvent("load", this);
32153         this.expand(deep, anim, callback);
32154     },
32155     
32156     /**
32157      * Returns true if this node has been loaded
32158      * @return {Boolean}
32159      */
32160     isLoaded : function(){
32161         return this.loaded;
32162     },
32163     
32164     hasChildNodes : function(){
32165         if(!this.isLeaf() && !this.loaded){
32166             return true;
32167         }else{
32168             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32169         }
32170     },
32171
32172     /**
32173      * Trigger a reload for this node
32174      * @param {Function} callback
32175      */
32176     reload : function(callback){
32177         this.collapse(false, false);
32178         while(this.firstChild){
32179             this.removeChild(this.firstChild);
32180         }
32181         this.childrenRendered = false;
32182         this.loaded = false;
32183         if(this.isHiddenRoot()){
32184             this.expanded = false;
32185         }
32186         this.expand(false, false, callback);
32187     }
32188 });/*
32189  * Based on:
32190  * Ext JS Library 1.1.1
32191  * Copyright(c) 2006-2007, Ext JS, LLC.
32192  *
32193  * Originally Released Under LGPL - original licence link has changed is not relivant.
32194  *
32195  * Fork - LGPL
32196  * <script type="text/javascript">
32197  */
32198  
32199 /**
32200  * @class Roo.tree.TreeNodeUI
32201  * @constructor
32202  * @param {Object} node The node to render
32203  * The TreeNode UI implementation is separate from the
32204  * tree implementation. Unless you are customizing the tree UI,
32205  * you should never have to use this directly.
32206  */
32207 Roo.tree.TreeNodeUI = function(node){
32208     this.node = node;
32209     this.rendered = false;
32210     this.animating = false;
32211     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32212 };
32213
32214 Roo.tree.TreeNodeUI.prototype = {
32215     removeChild : function(node){
32216         if(this.rendered){
32217             this.ctNode.removeChild(node.ui.getEl());
32218         }
32219     },
32220
32221     beforeLoad : function(){
32222          this.addClass("x-tree-node-loading");
32223     },
32224
32225     afterLoad : function(){
32226          this.removeClass("x-tree-node-loading");
32227     },
32228
32229     onTextChange : function(node, text, oldText){
32230         if(this.rendered){
32231             this.textNode.innerHTML = text;
32232         }
32233     },
32234
32235     onDisableChange : function(node, state){
32236         this.disabled = state;
32237         if(state){
32238             this.addClass("x-tree-node-disabled");
32239         }else{
32240             this.removeClass("x-tree-node-disabled");
32241         }
32242     },
32243
32244     onSelectedChange : function(state){
32245         if(state){
32246             this.focus();
32247             this.addClass("x-tree-selected");
32248         }else{
32249             //this.blur();
32250             this.removeClass("x-tree-selected");
32251         }
32252     },
32253
32254     onMove : function(tree, node, oldParent, newParent, index, refNode){
32255         this.childIndent = null;
32256         if(this.rendered){
32257             var targetNode = newParent.ui.getContainer();
32258             if(!targetNode){//target not rendered
32259                 this.holder = document.createElement("div");
32260                 this.holder.appendChild(this.wrap);
32261                 return;
32262             }
32263             var insertBefore = refNode ? refNode.ui.getEl() : null;
32264             if(insertBefore){
32265                 targetNode.insertBefore(this.wrap, insertBefore);
32266             }else{
32267                 targetNode.appendChild(this.wrap);
32268             }
32269             this.node.renderIndent(true);
32270         }
32271     },
32272
32273     addClass : function(cls){
32274         if(this.elNode){
32275             Roo.fly(this.elNode).addClass(cls);
32276         }
32277     },
32278
32279     removeClass : function(cls){
32280         if(this.elNode){
32281             Roo.fly(this.elNode).removeClass(cls);
32282         }
32283     },
32284
32285     remove : function(){
32286         if(this.rendered){
32287             this.holder = document.createElement("div");
32288             this.holder.appendChild(this.wrap);
32289         }
32290     },
32291
32292     fireEvent : function(){
32293         return this.node.fireEvent.apply(this.node, arguments);
32294     },
32295
32296     initEvents : function(){
32297         this.node.on("move", this.onMove, this);
32298         var E = Roo.EventManager;
32299         var a = this.anchor;
32300
32301         var el = Roo.fly(a, '_treeui');
32302
32303         if(Roo.isOpera){ // opera render bug ignores the CSS
32304             el.setStyle("text-decoration", "none");
32305         }
32306
32307         el.on("click", this.onClick, this);
32308         el.on("dblclick", this.onDblClick, this);
32309
32310         if(this.checkbox){
32311             Roo.EventManager.on(this.checkbox,
32312                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32313         }
32314
32315         el.on("contextmenu", this.onContextMenu, this);
32316
32317         var icon = Roo.fly(this.iconNode);
32318         icon.on("click", this.onClick, this);
32319         icon.on("dblclick", this.onDblClick, this);
32320         icon.on("contextmenu", this.onContextMenu, this);
32321         E.on(this.ecNode, "click", this.ecClick, this, true);
32322
32323         if(this.node.disabled){
32324             this.addClass("x-tree-node-disabled");
32325         }
32326         if(this.node.hidden){
32327             this.addClass("x-tree-node-disabled");
32328         }
32329         var ot = this.node.getOwnerTree();
32330         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32331         if(dd && (!this.node.isRoot || ot.rootVisible)){
32332             Roo.dd.Registry.register(this.elNode, {
32333                 node: this.node,
32334                 handles: this.getDDHandles(),
32335                 isHandle: false
32336             });
32337         }
32338     },
32339
32340     getDDHandles : function(){
32341         return [this.iconNode, this.textNode];
32342     },
32343
32344     hide : function(){
32345         if(this.rendered){
32346             this.wrap.style.display = "none";
32347         }
32348     },
32349
32350     show : function(){
32351         if(this.rendered){
32352             this.wrap.style.display = "";
32353         }
32354     },
32355
32356     onContextMenu : function(e){
32357         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32358             e.preventDefault();
32359             this.focus();
32360             this.fireEvent("contextmenu", this.node, e);
32361         }
32362     },
32363
32364     onClick : function(e){
32365         if(this.dropping){
32366             e.stopEvent();
32367             return;
32368         }
32369         if(this.fireEvent("beforeclick", this.node, e) !== false){
32370             if(!this.disabled && this.node.attributes.href){
32371                 this.fireEvent("click", this.node, e);
32372                 return;
32373             }
32374             e.preventDefault();
32375             if(this.disabled){
32376                 return;
32377             }
32378
32379             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32380                 this.node.toggle();
32381             }
32382
32383             this.fireEvent("click", this.node, e);
32384         }else{
32385             e.stopEvent();
32386         }
32387     },
32388
32389     onDblClick : function(e){
32390         e.preventDefault();
32391         if(this.disabled){
32392             return;
32393         }
32394         if(this.checkbox){
32395             this.toggleCheck();
32396         }
32397         if(!this.animating && this.node.hasChildNodes()){
32398             this.node.toggle();
32399         }
32400         this.fireEvent("dblclick", this.node, e);
32401     },
32402
32403     onCheckChange : function(){
32404         var checked = this.checkbox.checked;
32405         this.node.attributes.checked = checked;
32406         this.fireEvent('checkchange', this.node, checked);
32407     },
32408
32409     ecClick : function(e){
32410         if(!this.animating && this.node.hasChildNodes()){
32411             this.node.toggle();
32412         }
32413     },
32414
32415     startDrop : function(){
32416         this.dropping = true;
32417     },
32418
32419     // delayed drop so the click event doesn't get fired on a drop
32420     endDrop : function(){
32421        setTimeout(function(){
32422            this.dropping = false;
32423        }.createDelegate(this), 50);
32424     },
32425
32426     expand : function(){
32427         this.updateExpandIcon();
32428         this.ctNode.style.display = "";
32429     },
32430
32431     focus : function(){
32432         if(!this.node.preventHScroll){
32433             try{this.anchor.focus();
32434             }catch(e){}
32435         }else if(!Roo.isIE){
32436             try{
32437                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32438                 var l = noscroll.scrollLeft;
32439                 this.anchor.focus();
32440                 noscroll.scrollLeft = l;
32441             }catch(e){}
32442         }
32443     },
32444
32445     toggleCheck : function(value){
32446         var cb = this.checkbox;
32447         if(cb){
32448             cb.checked = (value === undefined ? !cb.checked : value);
32449         }
32450     },
32451
32452     blur : function(){
32453         try{
32454             this.anchor.blur();
32455         }catch(e){}
32456     },
32457
32458     animExpand : function(callback){
32459         var ct = Roo.get(this.ctNode);
32460         ct.stopFx();
32461         if(!this.node.hasChildNodes()){
32462             this.updateExpandIcon();
32463             this.ctNode.style.display = "";
32464             Roo.callback(callback);
32465             return;
32466         }
32467         this.animating = true;
32468         this.updateExpandIcon();
32469
32470         ct.slideIn('t', {
32471            callback : function(){
32472                this.animating = false;
32473                Roo.callback(callback);
32474             },
32475             scope: this,
32476             duration: this.node.ownerTree.duration || .25
32477         });
32478     },
32479
32480     highlight : function(){
32481         var tree = this.node.getOwnerTree();
32482         Roo.fly(this.wrap).highlight(
32483             tree.hlColor || "C3DAF9",
32484             {endColor: tree.hlBaseColor}
32485         );
32486     },
32487
32488     collapse : function(){
32489         this.updateExpandIcon();
32490         this.ctNode.style.display = "none";
32491     },
32492
32493     animCollapse : function(callback){
32494         var ct = Roo.get(this.ctNode);
32495         ct.enableDisplayMode('block');
32496         ct.stopFx();
32497
32498         this.animating = true;
32499         this.updateExpandIcon();
32500
32501         ct.slideOut('t', {
32502             callback : function(){
32503                this.animating = false;
32504                Roo.callback(callback);
32505             },
32506             scope: this,
32507             duration: this.node.ownerTree.duration || .25
32508         });
32509     },
32510
32511     getContainer : function(){
32512         return this.ctNode;
32513     },
32514
32515     getEl : function(){
32516         return this.wrap;
32517     },
32518
32519     appendDDGhost : function(ghostNode){
32520         ghostNode.appendChild(this.elNode.cloneNode(true));
32521     },
32522
32523     getDDRepairXY : function(){
32524         return Roo.lib.Dom.getXY(this.iconNode);
32525     },
32526
32527     onRender : function(){
32528         this.render();
32529     },
32530
32531     render : function(bulkRender){
32532         var n = this.node, a = n.attributes;
32533         var targetNode = n.parentNode ?
32534               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32535
32536         if(!this.rendered){
32537             this.rendered = true;
32538
32539             this.renderElements(n, a, targetNode, bulkRender);
32540
32541             if(a.qtip){
32542                if(this.textNode.setAttributeNS){
32543                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32544                    if(a.qtipTitle){
32545                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32546                    }
32547                }else{
32548                    this.textNode.setAttribute("ext:qtip", a.qtip);
32549                    if(a.qtipTitle){
32550                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32551                    }
32552                }
32553             }else if(a.qtipCfg){
32554                 a.qtipCfg.target = Roo.id(this.textNode);
32555                 Roo.QuickTips.register(a.qtipCfg);
32556             }
32557             this.initEvents();
32558             if(!this.node.expanded){
32559                 this.updateExpandIcon();
32560             }
32561         }else{
32562             if(bulkRender === true) {
32563                 targetNode.appendChild(this.wrap);
32564             }
32565         }
32566     },
32567
32568     renderElements : function(n, a, targetNode, bulkRender){
32569         // add some indent caching, this helps performance when rendering a large tree
32570         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32571         var t = n.getOwnerTree();
32572         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32573         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32574         var cb = typeof a.checked == 'boolean';
32575         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32576         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32577             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32578             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32579             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32580             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32581             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32582              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32583                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32584             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32585             "</li>"];
32586
32587         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32588             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32589                                 n.nextSibling.ui.getEl(), buf.join(""));
32590         }else{
32591             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32592         }
32593
32594         this.elNode = this.wrap.childNodes[0];
32595         this.ctNode = this.wrap.childNodes[1];
32596         var cs = this.elNode.childNodes;
32597         this.indentNode = cs[0];
32598         this.ecNode = cs[1];
32599         this.iconNode = cs[2];
32600         var index = 3;
32601         if(cb){
32602             this.checkbox = cs[3];
32603             index++;
32604         }
32605         this.anchor = cs[index];
32606         this.textNode = cs[index].firstChild;
32607     },
32608
32609     getAnchor : function(){
32610         return this.anchor;
32611     },
32612
32613     getTextEl : function(){
32614         return this.textNode;
32615     },
32616
32617     getIconEl : function(){
32618         return this.iconNode;
32619     },
32620
32621     isChecked : function(){
32622         return this.checkbox ? this.checkbox.checked : false;
32623     },
32624
32625     updateExpandIcon : function(){
32626         if(this.rendered){
32627             var n = this.node, c1, c2;
32628             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32629             var hasChild = n.hasChildNodes();
32630             if(hasChild){
32631                 if(n.expanded){
32632                     cls += "-minus";
32633                     c1 = "x-tree-node-collapsed";
32634                     c2 = "x-tree-node-expanded";
32635                 }else{
32636                     cls += "-plus";
32637                     c1 = "x-tree-node-expanded";
32638                     c2 = "x-tree-node-collapsed";
32639                 }
32640                 if(this.wasLeaf){
32641                     this.removeClass("x-tree-node-leaf");
32642                     this.wasLeaf = false;
32643                 }
32644                 if(this.c1 != c1 || this.c2 != c2){
32645                     Roo.fly(this.elNode).replaceClass(c1, c2);
32646                     this.c1 = c1; this.c2 = c2;
32647                 }
32648             }else{
32649                 if(!this.wasLeaf){
32650                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32651                     delete this.c1;
32652                     delete this.c2;
32653                     this.wasLeaf = true;
32654                 }
32655             }
32656             var ecc = "x-tree-ec-icon "+cls;
32657             if(this.ecc != ecc){
32658                 this.ecNode.className = ecc;
32659                 this.ecc = ecc;
32660             }
32661         }
32662     },
32663
32664     getChildIndent : function(){
32665         if(!this.childIndent){
32666             var buf = [];
32667             var p = this.node;
32668             while(p){
32669                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32670                     if(!p.isLast()) {
32671                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32672                     } else {
32673                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32674                     }
32675                 }
32676                 p = p.parentNode;
32677             }
32678             this.childIndent = buf.join("");
32679         }
32680         return this.childIndent;
32681     },
32682
32683     renderIndent : function(){
32684         if(this.rendered){
32685             var indent = "";
32686             var p = this.node.parentNode;
32687             if(p){
32688                 indent = p.ui.getChildIndent();
32689             }
32690             if(this.indentMarkup != indent){ // don't rerender if not required
32691                 this.indentNode.innerHTML = indent;
32692                 this.indentMarkup = indent;
32693             }
32694             this.updateExpandIcon();
32695         }
32696     }
32697 };
32698
32699 Roo.tree.RootTreeNodeUI = function(){
32700     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32701 };
32702 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32703     render : function(){
32704         if(!this.rendered){
32705             var targetNode = this.node.ownerTree.innerCt.dom;
32706             this.node.expanded = true;
32707             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32708             this.wrap = this.ctNode = targetNode.firstChild;
32709         }
32710     },
32711     collapse : function(){
32712     },
32713     expand : function(){
32714     }
32715 });/*
32716  * Based on:
32717  * Ext JS Library 1.1.1
32718  * Copyright(c) 2006-2007, Ext JS, LLC.
32719  *
32720  * Originally Released Under LGPL - original licence link has changed is not relivant.
32721  *
32722  * Fork - LGPL
32723  * <script type="text/javascript">
32724  */
32725 /**
32726  * @class Roo.tree.TreeLoader
32727  * @extends Roo.util.Observable
32728  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32729  * nodes from a specified URL. The response must be a javascript Array definition
32730  * who's elements are node definition objects. eg:
32731  * <pre><code>
32732    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32733     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32734 </code></pre>
32735  * <br><br>
32736  * A server request is sent, and child nodes are loaded only when a node is expanded.
32737  * The loading node's id is passed to the server under the parameter name "node" to
32738  * enable the server to produce the correct child nodes.
32739  * <br><br>
32740  * To pass extra parameters, an event handler may be attached to the "beforeload"
32741  * event, and the parameters specified in the TreeLoader's baseParams property:
32742  * <pre><code>
32743     myTreeLoader.on("beforeload", function(treeLoader, node) {
32744         this.baseParams.category = node.attributes.category;
32745     }, this);
32746 </code></pre><
32747  * This would pass an HTTP parameter called "category" to the server containing
32748  * the value of the Node's "category" attribute.
32749  * @constructor
32750  * Creates a new Treeloader.
32751  * @param {Object} config A config object containing config properties.
32752  */
32753 Roo.tree.TreeLoader = function(config){
32754     this.baseParams = {};
32755     this.requestMethod = "POST";
32756     Roo.apply(this, config);
32757
32758     this.addEvents({
32759     
32760         /**
32761          * @event beforeload
32762          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32763          * @param {Object} This TreeLoader object.
32764          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32765          * @param {Object} callback The callback function specified in the {@link #load} call.
32766          */
32767         beforeload : true,
32768         /**
32769          * @event load
32770          * Fires when the node has been successfuly loaded.
32771          * @param {Object} This TreeLoader object.
32772          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32773          * @param {Object} response The response object containing the data from the server.
32774          */
32775         load : true,
32776         /**
32777          * @event loadexception
32778          * Fires if the network request failed.
32779          * @param {Object} This TreeLoader object.
32780          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32781          * @param {Object} response The response object containing the data from the server.
32782          */
32783         loadexception : true,
32784         /**
32785          * @event create
32786          * Fires before a node is created, enabling you to return custom Node types 
32787          * @param {Object} This TreeLoader object.
32788          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32789          */
32790         create : true
32791     });
32792
32793     Roo.tree.TreeLoader.superclass.constructor.call(this);
32794 };
32795
32796 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32797     /**
32798     * @cfg {String} dataUrl The URL from which to request a Json string which
32799     * specifies an array of node definition object representing the child nodes
32800     * to be loaded.
32801     */
32802     /**
32803     * @cfg {Object} baseParams (optional) An object containing properties which
32804     * specify HTTP parameters to be passed to each request for child nodes.
32805     */
32806     /**
32807     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32808     * created by this loader. If the attributes sent by the server have an attribute in this object,
32809     * they take priority.
32810     */
32811     /**
32812     * @cfg {Object} uiProviders (optional) An object containing properties which
32813     * 
32814     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32815     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32816     * <i>uiProvider</i> attribute of a returned child node is a string rather
32817     * than a reference to a TreeNodeUI implementation, this that string value
32818     * is used as a property name in the uiProviders object. You can define the provider named
32819     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32820     */
32821     uiProviders : {},
32822
32823     /**
32824     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32825     * child nodes before loading.
32826     */
32827     clearOnLoad : true,
32828
32829     /**
32830     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32831     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32832     * Grid query { data : [ .....] }
32833     */
32834     
32835     root : false,
32836      /**
32837     * @cfg {String} queryParam (optional) 
32838     * Name of the query as it will be passed on the querystring (defaults to 'node')
32839     * eg. the request will be ?node=[id]
32840     */
32841     
32842     
32843     queryParam: false,
32844     
32845     /**
32846      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32847      * This is called automatically when a node is expanded, but may be used to reload
32848      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32849      * @param {Roo.tree.TreeNode} node
32850      * @param {Function} callback
32851      */
32852     load : function(node, callback){
32853         if(this.clearOnLoad){
32854             while(node.firstChild){
32855                 node.removeChild(node.firstChild);
32856             }
32857         }
32858         if(node.attributes.children){ // preloaded json children
32859             var cs = node.attributes.children;
32860             for(var i = 0, len = cs.length; i < len; i++){
32861                 node.appendChild(this.createNode(cs[i]));
32862             }
32863             if(typeof callback == "function"){
32864                 callback();
32865             }
32866         }else if(this.dataUrl){
32867             this.requestData(node, callback);
32868         }
32869     },
32870
32871     getParams: function(node){
32872         var buf = [], bp = this.baseParams;
32873         for(var key in bp){
32874             if(typeof bp[key] != "function"){
32875                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32876             }
32877         }
32878         var n = this.queryParam === false ? 'node' : this.queryParam;
32879         buf.push(n + "=", encodeURIComponent(node.id));
32880         return buf.join("");
32881     },
32882
32883     requestData : function(node, callback){
32884         if(this.fireEvent("beforeload", this, node, callback) !== false){
32885             this.transId = Roo.Ajax.request({
32886                 method:this.requestMethod,
32887                 url: this.dataUrl||this.url,
32888                 success: this.handleResponse,
32889                 failure: this.handleFailure,
32890                 scope: this,
32891                 argument: {callback: callback, node: node},
32892                 params: this.getParams(node)
32893             });
32894         }else{
32895             // if the load is cancelled, make sure we notify
32896             // the node that we are done
32897             if(typeof callback == "function"){
32898                 callback();
32899             }
32900         }
32901     },
32902
32903     isLoading : function(){
32904         return this.transId ? true : false;
32905     },
32906
32907     abort : function(){
32908         if(this.isLoading()){
32909             Roo.Ajax.abort(this.transId);
32910         }
32911     },
32912
32913     // private
32914     createNode : function(attr){
32915         // apply baseAttrs, nice idea Corey!
32916         if(this.baseAttrs){
32917             Roo.applyIf(attr, this.baseAttrs);
32918         }
32919         if(this.applyLoader !== false){
32920             attr.loader = this;
32921         }
32922         // uiProvider = depreciated..
32923         
32924         if(typeof(attr.uiProvider) == 'string'){
32925            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32926                 /**  eval:var:attr */ eval(attr.uiProvider);
32927         }
32928         if(typeof(this.uiProviders['default']) != 'undefined') {
32929             attr.uiProvider = this.uiProviders['default'];
32930         }
32931         
32932         this.fireEvent('create', this, attr);
32933         
32934         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32935         return(attr.leaf ?
32936                         new Roo.tree.TreeNode(attr) :
32937                         new Roo.tree.AsyncTreeNode(attr));
32938     },
32939
32940     processResponse : function(response, node, callback){
32941         var json = response.responseText;
32942         try {
32943             
32944             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32945             if (this.root !== false) {
32946                 o = o[this.root];
32947             }
32948             
32949             for(var i = 0, len = o.length; i < len; i++){
32950                 var n = this.createNode(o[i]);
32951                 if(n){
32952                     node.appendChild(n);
32953                 }
32954             }
32955             if(typeof callback == "function"){
32956                 callback(this, node);
32957             }
32958         }catch(e){
32959             this.handleFailure(response);
32960         }
32961     },
32962
32963     handleResponse : function(response){
32964         this.transId = false;
32965         var a = response.argument;
32966         this.processResponse(response, a.node, a.callback);
32967         this.fireEvent("load", this, a.node, response);
32968     },
32969
32970     handleFailure : function(response){
32971         this.transId = false;
32972         var a = response.argument;
32973         this.fireEvent("loadexception", this, a.node, response);
32974         if(typeof a.callback == "function"){
32975             a.callback(this, a.node);
32976         }
32977     }
32978 });/*
32979  * Based on:
32980  * Ext JS Library 1.1.1
32981  * Copyright(c) 2006-2007, Ext JS, LLC.
32982  *
32983  * Originally Released Under LGPL - original licence link has changed is not relivant.
32984  *
32985  * Fork - LGPL
32986  * <script type="text/javascript">
32987  */
32988
32989 /**
32990 * @class Roo.tree.TreeFilter
32991 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32992 * @param {TreePanel} tree
32993 * @param {Object} config (optional)
32994  */
32995 Roo.tree.TreeFilter = function(tree, config){
32996     this.tree = tree;
32997     this.filtered = {};
32998     Roo.apply(this, config);
32999 };
33000
33001 Roo.tree.TreeFilter.prototype = {
33002     clearBlank:false,
33003     reverse:false,
33004     autoClear:false,
33005     remove:false,
33006
33007      /**
33008      * Filter the data by a specific attribute.
33009      * @param {String/RegExp} value Either string that the attribute value
33010      * should start with or a RegExp to test against the attribute
33011      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33012      * @param {TreeNode} startNode (optional) The node to start the filter at.
33013      */
33014     filter : function(value, attr, startNode){
33015         attr = attr || "text";
33016         var f;
33017         if(typeof value == "string"){
33018             var vlen = value.length;
33019             // auto clear empty filter
33020             if(vlen == 0 && this.clearBlank){
33021                 this.clear();
33022                 return;
33023             }
33024             value = value.toLowerCase();
33025             f = function(n){
33026                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33027             };
33028         }else if(value.exec){ // regex?
33029             f = function(n){
33030                 return value.test(n.attributes[attr]);
33031             };
33032         }else{
33033             throw 'Illegal filter type, must be string or regex';
33034         }
33035         this.filterBy(f, null, startNode);
33036         },
33037
33038     /**
33039      * Filter by a function. The passed function will be called with each
33040      * node in the tree (or from the startNode). If the function returns true, the node is kept
33041      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33042      * @param {Function} fn The filter function
33043      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33044      */
33045     filterBy : function(fn, scope, startNode){
33046         startNode = startNode || this.tree.root;
33047         if(this.autoClear){
33048             this.clear();
33049         }
33050         var af = this.filtered, rv = this.reverse;
33051         var f = function(n){
33052             if(n == startNode){
33053                 return true;
33054             }
33055             if(af[n.id]){
33056                 return false;
33057             }
33058             var m = fn.call(scope || n, n);
33059             if(!m || rv){
33060                 af[n.id] = n;
33061                 n.ui.hide();
33062                 return false;
33063             }
33064             return true;
33065         };
33066         startNode.cascade(f);
33067         if(this.remove){
33068            for(var id in af){
33069                if(typeof id != "function"){
33070                    var n = af[id];
33071                    if(n && n.parentNode){
33072                        n.parentNode.removeChild(n);
33073                    }
33074                }
33075            }
33076         }
33077     },
33078
33079     /**
33080      * Clears the current filter. Note: with the "remove" option
33081      * set a filter cannot be cleared.
33082      */
33083     clear : function(){
33084         var t = this.tree;
33085         var af = this.filtered;
33086         for(var id in af){
33087             if(typeof id != "function"){
33088                 var n = af[id];
33089                 if(n){
33090                     n.ui.show();
33091                 }
33092             }
33093         }
33094         this.filtered = {};
33095     }
33096 };
33097 /*
33098  * Based on:
33099  * Ext JS Library 1.1.1
33100  * Copyright(c) 2006-2007, Ext JS, LLC.
33101  *
33102  * Originally Released Under LGPL - original licence link has changed is not relivant.
33103  *
33104  * Fork - LGPL
33105  * <script type="text/javascript">
33106  */
33107  
33108
33109 /**
33110  * @class Roo.tree.TreeSorter
33111  * Provides sorting of nodes in a TreePanel
33112  * 
33113  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33114  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33115  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33116  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33117  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33118  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33119  * @constructor
33120  * @param {TreePanel} tree
33121  * @param {Object} config
33122  */
33123 Roo.tree.TreeSorter = function(tree, config){
33124     Roo.apply(this, config);
33125     tree.on("beforechildrenrendered", this.doSort, this);
33126     tree.on("append", this.updateSort, this);
33127     tree.on("insert", this.updateSort, this);
33128     
33129     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33130     var p = this.property || "text";
33131     var sortType = this.sortType;
33132     var fs = this.folderSort;
33133     var cs = this.caseSensitive === true;
33134     var leafAttr = this.leafAttr || 'leaf';
33135
33136     this.sortFn = function(n1, n2){
33137         if(fs){
33138             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33139                 return 1;
33140             }
33141             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33142                 return -1;
33143             }
33144         }
33145         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33146         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33147         if(v1 < v2){
33148                         return dsc ? +1 : -1;
33149                 }else if(v1 > v2){
33150                         return dsc ? -1 : +1;
33151         }else{
33152                 return 0;
33153         }
33154     };
33155 };
33156
33157 Roo.tree.TreeSorter.prototype = {
33158     doSort : function(node){
33159         node.sort(this.sortFn);
33160     },
33161     
33162     compareNodes : function(n1, n2){
33163         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33164     },
33165     
33166     updateSort : function(tree, node){
33167         if(node.childrenRendered){
33168             this.doSort.defer(1, this, [node]);
33169         }
33170     }
33171 };/*
33172  * Based on:
33173  * Ext JS Library 1.1.1
33174  * Copyright(c) 2006-2007, Ext JS, LLC.
33175  *
33176  * Originally Released Under LGPL - original licence link has changed is not relivant.
33177  *
33178  * Fork - LGPL
33179  * <script type="text/javascript">
33180  */
33181
33182 if(Roo.dd.DropZone){
33183     
33184 Roo.tree.TreeDropZone = function(tree, config){
33185     this.allowParentInsert = false;
33186     this.allowContainerDrop = false;
33187     this.appendOnly = false;
33188     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33189     this.tree = tree;
33190     this.lastInsertClass = "x-tree-no-status";
33191     this.dragOverData = {};
33192 };
33193
33194 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33195     ddGroup : "TreeDD",
33196     
33197     expandDelay : 1000,
33198     
33199     expandNode : function(node){
33200         if(node.hasChildNodes() && !node.isExpanded()){
33201             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33202         }
33203     },
33204     
33205     queueExpand : function(node){
33206         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33207     },
33208     
33209     cancelExpand : function(){
33210         if(this.expandProcId){
33211             clearTimeout(this.expandProcId);
33212             this.expandProcId = false;
33213         }
33214     },
33215     
33216     isValidDropPoint : function(n, pt, dd, e, data){
33217         if(!n || !data){ return false; }
33218         var targetNode = n.node;
33219         var dropNode = data.node;
33220         // default drop rules
33221         if(!(targetNode && targetNode.isTarget && pt)){
33222             return false;
33223         }
33224         if(pt == "append" && targetNode.allowChildren === false){
33225             return false;
33226         }
33227         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33228             return false;
33229         }
33230         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33231             return false;
33232         }
33233         // reuse the object
33234         var overEvent = this.dragOverData;
33235         overEvent.tree = this.tree;
33236         overEvent.target = targetNode;
33237         overEvent.data = data;
33238         overEvent.point = pt;
33239         overEvent.source = dd;
33240         overEvent.rawEvent = e;
33241         overEvent.dropNode = dropNode;
33242         overEvent.cancel = false;  
33243         var result = this.tree.fireEvent("nodedragover", overEvent);
33244         return overEvent.cancel === false && result !== false;
33245     },
33246     
33247     getDropPoint : function(e, n, dd){
33248         var tn = n.node;
33249         if(tn.isRoot){
33250             return tn.allowChildren !== false ? "append" : false; // always append for root
33251         }
33252         var dragEl = n.ddel;
33253         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33254         var y = Roo.lib.Event.getPageY(e);
33255         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33256         
33257         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33258         var noAppend = tn.allowChildren === false;
33259         if(this.appendOnly || tn.parentNode.allowChildren === false){
33260             return noAppend ? false : "append";
33261         }
33262         var noBelow = false;
33263         if(!this.allowParentInsert){
33264             noBelow = tn.hasChildNodes() && tn.isExpanded();
33265         }
33266         var q = (b - t) / (noAppend ? 2 : 3);
33267         if(y >= t && y < (t + q)){
33268             return "above";
33269         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33270             return "below";
33271         }else{
33272             return "append";
33273         }
33274     },
33275     
33276     onNodeEnter : function(n, dd, e, data){
33277         this.cancelExpand();
33278     },
33279     
33280     onNodeOver : function(n, dd, e, data){
33281         var pt = this.getDropPoint(e, n, dd);
33282         var node = n.node;
33283         
33284         // auto node expand check
33285         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33286             this.queueExpand(node);
33287         }else if(pt != "append"){
33288             this.cancelExpand();
33289         }
33290         
33291         // set the insert point style on the target node
33292         var returnCls = this.dropNotAllowed;
33293         if(this.isValidDropPoint(n, pt, dd, e, data)){
33294            if(pt){
33295                var el = n.ddel;
33296                var cls;
33297                if(pt == "above"){
33298                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33299                    cls = "x-tree-drag-insert-above";
33300                }else if(pt == "below"){
33301                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33302                    cls = "x-tree-drag-insert-below";
33303                }else{
33304                    returnCls = "x-tree-drop-ok-append";
33305                    cls = "x-tree-drag-append";
33306                }
33307                if(this.lastInsertClass != cls){
33308                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33309                    this.lastInsertClass = cls;
33310                }
33311            }
33312        }
33313        return returnCls;
33314     },
33315     
33316     onNodeOut : function(n, dd, e, data){
33317         this.cancelExpand();
33318         this.removeDropIndicators(n);
33319     },
33320     
33321     onNodeDrop : function(n, dd, e, data){
33322         var point = this.getDropPoint(e, n, dd);
33323         var targetNode = n.node;
33324         targetNode.ui.startDrop();
33325         if(!this.isValidDropPoint(n, point, dd, e, data)){
33326             targetNode.ui.endDrop();
33327             return false;
33328         }
33329         // first try to find the drop node
33330         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33331         var dropEvent = {
33332             tree : this.tree,
33333             target: targetNode,
33334             data: data,
33335             point: point,
33336             source: dd,
33337             rawEvent: e,
33338             dropNode: dropNode,
33339             cancel: !dropNode   
33340         };
33341         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33342         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33343             targetNode.ui.endDrop();
33344             return false;
33345         }
33346         // allow target changing
33347         targetNode = dropEvent.target;
33348         if(point == "append" && !targetNode.isExpanded()){
33349             targetNode.expand(false, null, function(){
33350                 this.completeDrop(dropEvent);
33351             }.createDelegate(this));
33352         }else{
33353             this.completeDrop(dropEvent);
33354         }
33355         return true;
33356     },
33357     
33358     completeDrop : function(de){
33359         var ns = de.dropNode, p = de.point, t = de.target;
33360         if(!(ns instanceof Array)){
33361             ns = [ns];
33362         }
33363         var n;
33364         for(var i = 0, len = ns.length; i < len; i++){
33365             n = ns[i];
33366             if(p == "above"){
33367                 t.parentNode.insertBefore(n, t);
33368             }else if(p == "below"){
33369                 t.parentNode.insertBefore(n, t.nextSibling);
33370             }else{
33371                 t.appendChild(n);
33372             }
33373         }
33374         n.ui.focus();
33375         if(this.tree.hlDrop){
33376             n.ui.highlight();
33377         }
33378         t.ui.endDrop();
33379         this.tree.fireEvent("nodedrop", de);
33380     },
33381     
33382     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33383         if(this.tree.hlDrop){
33384             dropNode.ui.focus();
33385             dropNode.ui.highlight();
33386         }
33387         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33388     },
33389     
33390     getTree : function(){
33391         return this.tree;
33392     },
33393     
33394     removeDropIndicators : function(n){
33395         if(n && n.ddel){
33396             var el = n.ddel;
33397             Roo.fly(el).removeClass([
33398                     "x-tree-drag-insert-above",
33399                     "x-tree-drag-insert-below",
33400                     "x-tree-drag-append"]);
33401             this.lastInsertClass = "_noclass";
33402         }
33403     },
33404     
33405     beforeDragDrop : function(target, e, id){
33406         this.cancelExpand();
33407         return true;
33408     },
33409     
33410     afterRepair : function(data){
33411         if(data && Roo.enableFx){
33412             data.node.ui.highlight();
33413         }
33414         this.hideProxy();
33415     }    
33416 });
33417
33418 }
33419 /*
33420  * Based on:
33421  * Ext JS Library 1.1.1
33422  * Copyright(c) 2006-2007, Ext JS, LLC.
33423  *
33424  * Originally Released Under LGPL - original licence link has changed is not relivant.
33425  *
33426  * Fork - LGPL
33427  * <script type="text/javascript">
33428  */
33429  
33430
33431 if(Roo.dd.DragZone){
33432 Roo.tree.TreeDragZone = function(tree, config){
33433     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33434     this.tree = tree;
33435 };
33436
33437 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33438     ddGroup : "TreeDD",
33439     
33440     onBeforeDrag : function(data, e){
33441         var n = data.node;
33442         return n && n.draggable && !n.disabled;
33443     },
33444     
33445     onInitDrag : function(e){
33446         var data = this.dragData;
33447         this.tree.getSelectionModel().select(data.node);
33448         this.proxy.update("");
33449         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33450         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33451     },
33452     
33453     getRepairXY : function(e, data){
33454         return data.node.ui.getDDRepairXY();
33455     },
33456     
33457     onEndDrag : function(data, e){
33458         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33459     },
33460     
33461     onValidDrop : function(dd, e, id){
33462         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33463         this.hideProxy();
33464     },
33465     
33466     beforeInvalidDrop : function(e, id){
33467         // this scrolls the original position back into view
33468         var sm = this.tree.getSelectionModel();
33469         sm.clearSelections();
33470         sm.select(this.dragData.node);
33471     }
33472 });
33473 }/*
33474  * Based on:
33475  * Ext JS Library 1.1.1
33476  * Copyright(c) 2006-2007, Ext JS, LLC.
33477  *
33478  * Originally Released Under LGPL - original licence link has changed is not relivant.
33479  *
33480  * Fork - LGPL
33481  * <script type="text/javascript">
33482  */
33483 /**
33484  * @class Roo.tree.TreeEditor
33485  * @extends Roo.Editor
33486  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33487  * as the editor field.
33488  * @constructor
33489  * @param {TreePanel} tree
33490  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33491  */
33492 Roo.tree.TreeEditor = function(tree, config){
33493     config = config || {};
33494     var field = config.events ? config : new Roo.form.TextField(config);
33495     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33496
33497     this.tree = tree;
33498
33499     tree.on('beforeclick', this.beforeNodeClick, this);
33500     tree.getTreeEl().on('mousedown', this.hide, this);
33501     this.on('complete', this.updateNode, this);
33502     this.on('beforestartedit', this.fitToTree, this);
33503     this.on('startedit', this.bindScroll, this, {delay:10});
33504     this.on('specialkey', this.onSpecialKey, this);
33505 };
33506
33507 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33508     /**
33509      * @cfg {String} alignment
33510      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33511      */
33512     alignment: "l-l",
33513     // inherit
33514     autoSize: false,
33515     /**
33516      * @cfg {Boolean} hideEl
33517      * True to hide the bound element while the editor is displayed (defaults to false)
33518      */
33519     hideEl : false,
33520     /**
33521      * @cfg {String} cls
33522      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33523      */
33524     cls: "x-small-editor x-tree-editor",
33525     /**
33526      * @cfg {Boolean} shim
33527      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33528      */
33529     shim:false,
33530     // inherit
33531     shadow:"frame",
33532     /**
33533      * @cfg {Number} maxWidth
33534      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33535      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33536      * scroll and client offsets into account prior to each edit.
33537      */
33538     maxWidth: 250,
33539
33540     editDelay : 350,
33541
33542     // private
33543     fitToTree : function(ed, el){
33544         var td = this.tree.getTreeEl().dom, nd = el.dom;
33545         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33546             td.scrollLeft = nd.offsetLeft;
33547         }
33548         var w = Math.min(
33549                 this.maxWidth,
33550                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33551         this.setSize(w, '');
33552     },
33553
33554     // private
33555     triggerEdit : function(node){
33556         this.completeEdit();
33557         this.editNode = node;
33558         this.startEdit(node.ui.textNode, node.text);
33559     },
33560
33561     // private
33562     bindScroll : function(){
33563         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33564     },
33565
33566     // private
33567     beforeNodeClick : function(node, e){
33568         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33569         this.lastClick = new Date();
33570         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33571             e.stopEvent();
33572             this.triggerEdit(node);
33573             return false;
33574         }
33575     },
33576
33577     // private
33578     updateNode : function(ed, value){
33579         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33580         this.editNode.setText(value);
33581     },
33582
33583     // private
33584     onHide : function(){
33585         Roo.tree.TreeEditor.superclass.onHide.call(this);
33586         if(this.editNode){
33587             this.editNode.ui.focus();
33588         }
33589     },
33590
33591     // private
33592     onSpecialKey : function(field, e){
33593         var k = e.getKey();
33594         if(k == e.ESC){
33595             e.stopEvent();
33596             this.cancelEdit();
33597         }else if(k == e.ENTER && !e.hasModifier()){
33598             e.stopEvent();
33599             this.completeEdit();
33600         }
33601     }
33602 });//<Script type="text/javascript">
33603 /*
33604  * Based on:
33605  * Ext JS Library 1.1.1
33606  * Copyright(c) 2006-2007, Ext JS, LLC.
33607  *
33608  * Originally Released Under LGPL - original licence link has changed is not relivant.
33609  *
33610  * Fork - LGPL
33611  * <script type="text/javascript">
33612  */
33613  
33614 /**
33615  * Not documented??? - probably should be...
33616  */
33617
33618 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33619     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33620     
33621     renderElements : function(n, a, targetNode, bulkRender){
33622         //consel.log("renderElements?");
33623         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33624
33625         var t = n.getOwnerTree();
33626         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33627         
33628         var cols = t.columns;
33629         var bw = t.borderWidth;
33630         var c = cols[0];
33631         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33632          var cb = typeof a.checked == "boolean";
33633         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33634         var colcls = 'x-t-' + tid + '-c0';
33635         var buf = [
33636             '<li class="x-tree-node">',
33637             
33638                 
33639                 '<div class="x-tree-node-el ', a.cls,'">',
33640                     // extran...
33641                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33642                 
33643                 
33644                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33645                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33646                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33647                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33648                            (a.iconCls ? ' '+a.iconCls : ''),
33649                            '" unselectable="on" />',
33650                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33651                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33652                              
33653                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33654                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33655                             '<span unselectable="on" qtip="' + tx + '">',
33656                              tx,
33657                              '</span></a>' ,
33658                     '</div>',
33659                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33660                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33661                  ];
33662         for(var i = 1, len = cols.length; i < len; i++){
33663             c = cols[i];
33664             colcls = 'x-t-' + tid + '-c' +i;
33665             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33666             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33667                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33668                       "</div>");
33669          }
33670          
33671          buf.push(
33672             '</a>',
33673             '<div class="x-clear"></div></div>',
33674             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33675             "</li>");
33676         
33677         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33678             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33679                                 n.nextSibling.ui.getEl(), buf.join(""));
33680         }else{
33681             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33682         }
33683         var el = this.wrap.firstChild;
33684         this.elRow = el;
33685         this.elNode = el.firstChild;
33686         this.ranchor = el.childNodes[1];
33687         this.ctNode = this.wrap.childNodes[1];
33688         var cs = el.firstChild.childNodes;
33689         this.indentNode = cs[0];
33690         this.ecNode = cs[1];
33691         this.iconNode = cs[2];
33692         var index = 3;
33693         if(cb){
33694             this.checkbox = cs[3];
33695             index++;
33696         }
33697         this.anchor = cs[index];
33698         
33699         this.textNode = cs[index].firstChild;
33700         
33701         //el.on("click", this.onClick, this);
33702         //el.on("dblclick", this.onDblClick, this);
33703         
33704         
33705        // console.log(this);
33706     },
33707     initEvents : function(){
33708         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33709         
33710             
33711         var a = this.ranchor;
33712
33713         var el = Roo.get(a);
33714
33715         if(Roo.isOpera){ // opera render bug ignores the CSS
33716             el.setStyle("text-decoration", "none");
33717         }
33718
33719         el.on("click", this.onClick, this);
33720         el.on("dblclick", this.onDblClick, this);
33721         el.on("contextmenu", this.onContextMenu, this);
33722         
33723     },
33724     
33725     /*onSelectedChange : function(state){
33726         if(state){
33727             this.focus();
33728             this.addClass("x-tree-selected");
33729         }else{
33730             //this.blur();
33731             this.removeClass("x-tree-selected");
33732         }
33733     },*/
33734     addClass : function(cls){
33735         if(this.elRow){
33736             Roo.fly(this.elRow).addClass(cls);
33737         }
33738         
33739     },
33740     
33741     
33742     removeClass : function(cls){
33743         if(this.elRow){
33744             Roo.fly(this.elRow).removeClass(cls);
33745         }
33746     }
33747
33748     
33749     
33750 });//<Script type="text/javascript">
33751
33752 /*
33753  * Based on:
33754  * Ext JS Library 1.1.1
33755  * Copyright(c) 2006-2007, Ext JS, LLC.
33756  *
33757  * Originally Released Under LGPL - original licence link has changed is not relivant.
33758  *
33759  * Fork - LGPL
33760  * <script type="text/javascript">
33761  */
33762  
33763
33764 /**
33765  * @class Roo.tree.ColumnTree
33766  * @extends Roo.data.TreePanel
33767  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33768  * @cfg {int} borderWidth  compined right/left border allowance
33769  * @constructor
33770  * @param {String/HTMLElement/Element} el The container element
33771  * @param {Object} config
33772  */
33773 Roo.tree.ColumnTree =  function(el, config)
33774 {
33775    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33776    this.addEvents({
33777         /**
33778         * @event resize
33779         * Fire this event on a container when it resizes
33780         * @param {int} w Width
33781         * @param {int} h Height
33782         */
33783        "resize" : true
33784     });
33785     this.on('resize', this.onResize, this);
33786 };
33787
33788 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33789     //lines:false,
33790     
33791     
33792     borderWidth: Roo.isBorderBox ? 0 : 2, 
33793     headEls : false,
33794     
33795     render : function(){
33796         // add the header.....
33797        
33798         Roo.tree.ColumnTree.superclass.render.apply(this);
33799         
33800         this.el.addClass('x-column-tree');
33801         
33802         this.headers = this.el.createChild(
33803             {cls:'x-tree-headers'},this.innerCt.dom);
33804    
33805         var cols = this.columns, c;
33806         var totalWidth = 0;
33807         this.headEls = [];
33808         var  len = cols.length;
33809         for(var i = 0; i < len; i++){
33810              c = cols[i];
33811              totalWidth += c.width;
33812             this.headEls.push(this.headers.createChild({
33813                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33814                  cn: {
33815                      cls:'x-tree-hd-text',
33816                      html: c.header
33817                  },
33818                  style:'width:'+(c.width-this.borderWidth)+'px;'
33819              }));
33820         }
33821         this.headers.createChild({cls:'x-clear'});
33822         // prevent floats from wrapping when clipped
33823         this.headers.setWidth(totalWidth);
33824         //this.innerCt.setWidth(totalWidth);
33825         this.innerCt.setStyle({ overflow: 'auto' });
33826         this.onResize(this.width, this.height);
33827              
33828         
33829     },
33830     onResize : function(w,h)
33831     {
33832         this.height = h;
33833         this.width = w;
33834         // resize cols..
33835         this.innerCt.setWidth(this.width);
33836         this.innerCt.setHeight(this.height-20);
33837         
33838         // headers...
33839         var cols = this.columns, c;
33840         var totalWidth = 0;
33841         var expEl = false;
33842         var len = cols.length;
33843         for(var i = 0; i < len; i++){
33844             c = cols[i];
33845             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33846                 // it's the expander..
33847                 expEl  = this.headEls[i];
33848                 continue;
33849             }
33850             totalWidth += c.width;
33851             
33852         }
33853         if (expEl) {
33854             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33855         }
33856         this.headers.setWidth(w-20);
33857
33858         
33859         
33860         
33861     }
33862 });
33863 /*
33864  * Based on:
33865  * Ext JS Library 1.1.1
33866  * Copyright(c) 2006-2007, Ext JS, LLC.
33867  *
33868  * Originally Released Under LGPL - original licence link has changed is not relivant.
33869  *
33870  * Fork - LGPL
33871  * <script type="text/javascript">
33872  */
33873  
33874 /**
33875  * @class Roo.menu.Menu
33876  * @extends Roo.util.Observable
33877  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33878  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33879  * @constructor
33880  * Creates a new Menu
33881  * @param {Object} config Configuration options
33882  */
33883 Roo.menu.Menu = function(config){
33884     Roo.apply(this, config);
33885     this.id = this.id || Roo.id();
33886     this.addEvents({
33887         /**
33888          * @event beforeshow
33889          * Fires before this menu is displayed
33890          * @param {Roo.menu.Menu} this
33891          */
33892         beforeshow : true,
33893         /**
33894          * @event beforehide
33895          * Fires before this menu is hidden
33896          * @param {Roo.menu.Menu} this
33897          */
33898         beforehide : true,
33899         /**
33900          * @event show
33901          * Fires after this menu is displayed
33902          * @param {Roo.menu.Menu} this
33903          */
33904         show : true,
33905         /**
33906          * @event hide
33907          * Fires after this menu is hidden
33908          * @param {Roo.menu.Menu} this
33909          */
33910         hide : true,
33911         /**
33912          * @event click
33913          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33914          * @param {Roo.menu.Menu} this
33915          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33916          * @param {Roo.EventObject} e
33917          */
33918         click : true,
33919         /**
33920          * @event mouseover
33921          * Fires when the mouse is hovering over this menu
33922          * @param {Roo.menu.Menu} this
33923          * @param {Roo.EventObject} e
33924          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33925          */
33926         mouseover : true,
33927         /**
33928          * @event mouseout
33929          * Fires when the mouse exits this menu
33930          * @param {Roo.menu.Menu} this
33931          * @param {Roo.EventObject} e
33932          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33933          */
33934         mouseout : true,
33935         /**
33936          * @event itemclick
33937          * Fires when a menu item contained in this menu is clicked
33938          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33939          * @param {Roo.EventObject} e
33940          */
33941         itemclick: true
33942     });
33943     if (this.registerMenu) {
33944         Roo.menu.MenuMgr.register(this);
33945     }
33946     
33947     var mis = this.items;
33948     this.items = new Roo.util.MixedCollection();
33949     if(mis){
33950         this.add.apply(this, mis);
33951     }
33952 };
33953
33954 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33955     /**
33956      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33957      */
33958     minWidth : 120,
33959     /**
33960      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33961      * for bottom-right shadow (defaults to "sides")
33962      */
33963     shadow : "sides",
33964     /**
33965      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33966      * this menu (defaults to "tl-tr?")
33967      */
33968     subMenuAlign : "tl-tr?",
33969     /**
33970      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33971      * relative to its element of origin (defaults to "tl-bl?")
33972      */
33973     defaultAlign : "tl-bl?",
33974     /**
33975      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33976      */
33977     allowOtherMenus : false,
33978     /**
33979      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33980      */
33981     registerMenu : true,
33982
33983     hidden:true,
33984
33985     // private
33986     render : function(){
33987         if(this.el){
33988             return;
33989         }
33990         var el = this.el = new Roo.Layer({
33991             cls: "x-menu",
33992             shadow:this.shadow,
33993             constrain: false,
33994             parentEl: this.parentEl || document.body,
33995             zindex:15000
33996         });
33997
33998         this.keyNav = new Roo.menu.MenuNav(this);
33999
34000         if(this.plain){
34001             el.addClass("x-menu-plain");
34002         }
34003         if(this.cls){
34004             el.addClass(this.cls);
34005         }
34006         // generic focus element
34007         this.focusEl = el.createChild({
34008             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34009         });
34010         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34011         ul.on("click", this.onClick, this);
34012         ul.on("mouseover", this.onMouseOver, this);
34013         ul.on("mouseout", this.onMouseOut, this);
34014         this.items.each(function(item){
34015             var li = document.createElement("li");
34016             li.className = "x-menu-list-item";
34017             ul.dom.appendChild(li);
34018             item.render(li, this);
34019         }, this);
34020         this.ul = ul;
34021         this.autoWidth();
34022     },
34023
34024     // private
34025     autoWidth : function(){
34026         var el = this.el, ul = this.ul;
34027         if(!el){
34028             return;
34029         }
34030         var w = this.width;
34031         if(w){
34032             el.setWidth(w);
34033         }else if(Roo.isIE){
34034             el.setWidth(this.minWidth);
34035             var t = el.dom.offsetWidth; // force recalc
34036             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34037         }
34038     },
34039
34040     // private
34041     delayAutoWidth : function(){
34042         if(this.rendered){
34043             if(!this.awTask){
34044                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34045             }
34046             this.awTask.delay(20);
34047         }
34048     },
34049
34050     // private
34051     findTargetItem : function(e){
34052         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34053         if(t && t.menuItemId){
34054             return this.items.get(t.menuItemId);
34055         }
34056     },
34057
34058     // private
34059     onClick : function(e){
34060         var t;
34061         if(t = this.findTargetItem(e)){
34062             t.onClick(e);
34063             this.fireEvent("click", this, t, e);
34064         }
34065     },
34066
34067     // private
34068     setActiveItem : function(item, autoExpand){
34069         if(item != this.activeItem){
34070             if(this.activeItem){
34071                 this.activeItem.deactivate();
34072             }
34073             this.activeItem = item;
34074             item.activate(autoExpand);
34075         }else if(autoExpand){
34076             item.expandMenu();
34077         }
34078     },
34079
34080     // private
34081     tryActivate : function(start, step){
34082         var items = this.items;
34083         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34084             var item = items.get(i);
34085             if(!item.disabled && item.canActivate){
34086                 this.setActiveItem(item, false);
34087                 return item;
34088             }
34089         }
34090         return false;
34091     },
34092
34093     // private
34094     onMouseOver : function(e){
34095         var t;
34096         if(t = this.findTargetItem(e)){
34097             if(t.canActivate && !t.disabled){
34098                 this.setActiveItem(t, true);
34099             }
34100         }
34101         this.fireEvent("mouseover", this, e, t);
34102     },
34103
34104     // private
34105     onMouseOut : function(e){
34106         var t;
34107         if(t = this.findTargetItem(e)){
34108             if(t == this.activeItem && t.shouldDeactivate(e)){
34109                 this.activeItem.deactivate();
34110                 delete this.activeItem;
34111             }
34112         }
34113         this.fireEvent("mouseout", this, e, t);
34114     },
34115
34116     /**
34117      * Read-only.  Returns true if the menu is currently displayed, else false.
34118      * @type Boolean
34119      */
34120     isVisible : function(){
34121         return this.el && !this.hidden;
34122     },
34123
34124     /**
34125      * Displays this menu relative to another element
34126      * @param {String/HTMLElement/Roo.Element} element The element to align to
34127      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34128      * the element (defaults to this.defaultAlign)
34129      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34130      */
34131     show : function(el, pos, parentMenu){
34132         this.parentMenu = parentMenu;
34133         if(!this.el){
34134             this.render();
34135         }
34136         this.fireEvent("beforeshow", this);
34137         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34138     },
34139
34140     /**
34141      * Displays this menu at a specific xy position
34142      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34143      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34144      */
34145     showAt : function(xy, parentMenu, /* private: */_e){
34146         this.parentMenu = parentMenu;
34147         if(!this.el){
34148             this.render();
34149         }
34150         if(_e !== false){
34151             this.fireEvent("beforeshow", this);
34152             xy = this.el.adjustForConstraints(xy);
34153         }
34154         this.el.setXY(xy);
34155         this.el.show();
34156         this.hidden = false;
34157         this.focus();
34158         this.fireEvent("show", this);
34159     },
34160
34161     focus : function(){
34162         if(!this.hidden){
34163             this.doFocus.defer(50, this);
34164         }
34165     },
34166
34167     doFocus : function(){
34168         if(!this.hidden){
34169             this.focusEl.focus();
34170         }
34171     },
34172
34173     /**
34174      * Hides this menu and optionally all parent menus
34175      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34176      */
34177     hide : function(deep){
34178         if(this.el && this.isVisible()){
34179             this.fireEvent("beforehide", this);
34180             if(this.activeItem){
34181                 this.activeItem.deactivate();
34182                 this.activeItem = null;
34183             }
34184             this.el.hide();
34185             this.hidden = true;
34186             this.fireEvent("hide", this);
34187         }
34188         if(deep === true && this.parentMenu){
34189             this.parentMenu.hide(true);
34190         }
34191     },
34192
34193     /**
34194      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34195      * Any of the following are valid:
34196      * <ul>
34197      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34198      * <li>An HTMLElement object which will be converted to a menu item</li>
34199      * <li>A menu item config object that will be created as a new menu item</li>
34200      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34201      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34202      * </ul>
34203      * Usage:
34204      * <pre><code>
34205 // Create the menu
34206 var menu = new Roo.menu.Menu();
34207
34208 // Create a menu item to add by reference
34209 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34210
34211 // Add a bunch of items at once using different methods.
34212 // Only the last item added will be returned.
34213 var item = menu.add(
34214     menuItem,                // add existing item by ref
34215     'Dynamic Item',          // new TextItem
34216     '-',                     // new separator
34217     { text: 'Config Item' }  // new item by config
34218 );
34219 </code></pre>
34220      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34221      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34222      */
34223     add : function(){
34224         var a = arguments, l = a.length, item;
34225         for(var i = 0; i < l; i++){
34226             var el = a[i];
34227             if ((typeof(el) == "object") && el.xtype && el.xns) {
34228                 el = Roo.factory(el, Roo.menu);
34229             }
34230             
34231             if(el.render){ // some kind of Item
34232                 item = this.addItem(el);
34233             }else if(typeof el == "string"){ // string
34234                 if(el == "separator" || el == "-"){
34235                     item = this.addSeparator();
34236                 }else{
34237                     item = this.addText(el);
34238                 }
34239             }else if(el.tagName || el.el){ // element
34240                 item = this.addElement(el);
34241             }else if(typeof el == "object"){ // must be menu item config?
34242                 item = this.addMenuItem(el);
34243             }
34244         }
34245         return item;
34246     },
34247
34248     /**
34249      * Returns this menu's underlying {@link Roo.Element} object
34250      * @return {Roo.Element} The element
34251      */
34252     getEl : function(){
34253         if(!this.el){
34254             this.render();
34255         }
34256         return this.el;
34257     },
34258
34259     /**
34260      * Adds a separator bar to the menu
34261      * @return {Roo.menu.Item} The menu item that was added
34262      */
34263     addSeparator : function(){
34264         return this.addItem(new Roo.menu.Separator());
34265     },
34266
34267     /**
34268      * Adds an {@link Roo.Element} object to the menu
34269      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34270      * @return {Roo.menu.Item} The menu item that was added
34271      */
34272     addElement : function(el){
34273         return this.addItem(new Roo.menu.BaseItem(el));
34274     },
34275
34276     /**
34277      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34278      * @param {Roo.menu.Item} item The menu item to add
34279      * @return {Roo.menu.Item} The menu item that was added
34280      */
34281     addItem : function(item){
34282         this.items.add(item);
34283         if(this.ul){
34284             var li = document.createElement("li");
34285             li.className = "x-menu-list-item";
34286             this.ul.dom.appendChild(li);
34287             item.render(li, this);
34288             this.delayAutoWidth();
34289         }
34290         return item;
34291     },
34292
34293     /**
34294      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34295      * @param {Object} config A MenuItem config object
34296      * @return {Roo.menu.Item} The menu item that was added
34297      */
34298     addMenuItem : function(config){
34299         if(!(config instanceof Roo.menu.Item)){
34300             if(typeof config.checked == "boolean"){ // must be check menu item config?
34301                 config = new Roo.menu.CheckItem(config);
34302             }else{
34303                 config = new Roo.menu.Item(config);
34304             }
34305         }
34306         return this.addItem(config);
34307     },
34308
34309     /**
34310      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34311      * @param {String} text The text to display in the menu item
34312      * @return {Roo.menu.Item} The menu item that was added
34313      */
34314     addText : function(text){
34315         return this.addItem(new Roo.menu.TextItem({ text : text }));
34316     },
34317
34318     /**
34319      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34320      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34321      * @param {Roo.menu.Item} item The menu item to add
34322      * @return {Roo.menu.Item} The menu item that was added
34323      */
34324     insert : function(index, item){
34325         this.items.insert(index, item);
34326         if(this.ul){
34327             var li = document.createElement("li");
34328             li.className = "x-menu-list-item";
34329             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34330             item.render(li, this);
34331             this.delayAutoWidth();
34332         }
34333         return item;
34334     },
34335
34336     /**
34337      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34338      * @param {Roo.menu.Item} item The menu item to remove
34339      */
34340     remove : function(item){
34341         this.items.removeKey(item.id);
34342         item.destroy();
34343     },
34344
34345     /**
34346      * Removes and destroys all items in the menu
34347      */
34348     removeAll : function(){
34349         var f;
34350         while(f = this.items.first()){
34351             this.remove(f);
34352         }
34353     }
34354 });
34355
34356 // MenuNav is a private utility class used internally by the Menu
34357 Roo.menu.MenuNav = function(menu){
34358     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34359     this.scope = this.menu = menu;
34360 };
34361
34362 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34363     doRelay : function(e, h){
34364         var k = e.getKey();
34365         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34366             this.menu.tryActivate(0, 1);
34367             return false;
34368         }
34369         return h.call(this.scope || this, e, this.menu);
34370     },
34371
34372     up : function(e, m){
34373         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34374             m.tryActivate(m.items.length-1, -1);
34375         }
34376     },
34377
34378     down : function(e, m){
34379         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34380             m.tryActivate(0, 1);
34381         }
34382     },
34383
34384     right : function(e, m){
34385         if(m.activeItem){
34386             m.activeItem.expandMenu(true);
34387         }
34388     },
34389
34390     left : function(e, m){
34391         m.hide();
34392         if(m.parentMenu && m.parentMenu.activeItem){
34393             m.parentMenu.activeItem.activate();
34394         }
34395     },
34396
34397     enter : function(e, m){
34398         if(m.activeItem){
34399             e.stopPropagation();
34400             m.activeItem.onClick(e);
34401             m.fireEvent("click", this, m.activeItem);
34402             return true;
34403         }
34404     }
34405 });/*
34406  * Based on:
34407  * Ext JS Library 1.1.1
34408  * Copyright(c) 2006-2007, Ext JS, LLC.
34409  *
34410  * Originally Released Under LGPL - original licence link has changed is not relivant.
34411  *
34412  * Fork - LGPL
34413  * <script type="text/javascript">
34414  */
34415  
34416 /**
34417  * @class Roo.menu.MenuMgr
34418  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34419  * @singleton
34420  */
34421 Roo.menu.MenuMgr = function(){
34422    var menus, active, groups = {}, attached = false, lastShow = new Date();
34423
34424    // private - called when first menu is created
34425    function init(){
34426        menus = {};
34427        active = new Roo.util.MixedCollection();
34428        Roo.get(document).addKeyListener(27, function(){
34429            if(active.length > 0){
34430                hideAll();
34431            }
34432        });
34433    }
34434
34435    // private
34436    function hideAll(){
34437        if(active && active.length > 0){
34438            var c = active.clone();
34439            c.each(function(m){
34440                m.hide();
34441            });
34442        }
34443    }
34444
34445    // private
34446    function onHide(m){
34447        active.remove(m);
34448        if(active.length < 1){
34449            Roo.get(document).un("mousedown", onMouseDown);
34450            attached = false;
34451        }
34452    }
34453
34454    // private
34455    function onShow(m){
34456        var last = active.last();
34457        lastShow = new Date();
34458        active.add(m);
34459        if(!attached){
34460            Roo.get(document).on("mousedown", onMouseDown);
34461            attached = true;
34462        }
34463        if(m.parentMenu){
34464           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34465           m.parentMenu.activeChild = m;
34466        }else if(last && last.isVisible()){
34467           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34468        }
34469    }
34470
34471    // private
34472    function onBeforeHide(m){
34473        if(m.activeChild){
34474            m.activeChild.hide();
34475        }
34476        if(m.autoHideTimer){
34477            clearTimeout(m.autoHideTimer);
34478            delete m.autoHideTimer;
34479        }
34480    }
34481
34482    // private
34483    function onBeforeShow(m){
34484        var pm = m.parentMenu;
34485        if(!pm && !m.allowOtherMenus){
34486            hideAll();
34487        }else if(pm && pm.activeChild && active != m){
34488            pm.activeChild.hide();
34489        }
34490    }
34491
34492    // private
34493    function onMouseDown(e){
34494        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34495            hideAll();
34496        }
34497    }
34498
34499    // private
34500    function onBeforeCheck(mi, state){
34501        if(state){
34502            var g = groups[mi.group];
34503            for(var i = 0, l = g.length; i < l; i++){
34504                if(g[i] != mi){
34505                    g[i].setChecked(false);
34506                }
34507            }
34508        }
34509    }
34510
34511    return {
34512
34513        /**
34514         * Hides all menus that are currently visible
34515         */
34516        hideAll : function(){
34517             hideAll();  
34518        },
34519
34520        // private
34521        register : function(menu){
34522            if(!menus){
34523                init();
34524            }
34525            menus[menu.id] = menu;
34526            menu.on("beforehide", onBeforeHide);
34527            menu.on("hide", onHide);
34528            menu.on("beforeshow", onBeforeShow);
34529            menu.on("show", onShow);
34530            var g = menu.group;
34531            if(g && menu.events["checkchange"]){
34532                if(!groups[g]){
34533                    groups[g] = [];
34534                }
34535                groups[g].push(menu);
34536                menu.on("checkchange", onCheck);
34537            }
34538        },
34539
34540         /**
34541          * Returns a {@link Roo.menu.Menu} object
34542          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34543          * be used to generate and return a new Menu instance.
34544          */
34545        get : function(menu){
34546            if(typeof menu == "string"){ // menu id
34547                return menus[menu];
34548            }else if(menu.events){  // menu instance
34549                return menu;
34550            }else if(typeof menu.length == 'number'){ // array of menu items?
34551                return new Roo.menu.Menu({items:menu});
34552            }else{ // otherwise, must be a config
34553                return new Roo.menu.Menu(menu);
34554            }
34555        },
34556
34557        // private
34558        unregister : function(menu){
34559            delete menus[menu.id];
34560            menu.un("beforehide", onBeforeHide);
34561            menu.un("hide", onHide);
34562            menu.un("beforeshow", onBeforeShow);
34563            menu.un("show", onShow);
34564            var g = menu.group;
34565            if(g && menu.events["checkchange"]){
34566                groups[g].remove(menu);
34567                menu.un("checkchange", onCheck);
34568            }
34569        },
34570
34571        // private
34572        registerCheckable : function(menuItem){
34573            var g = menuItem.group;
34574            if(g){
34575                if(!groups[g]){
34576                    groups[g] = [];
34577                }
34578                groups[g].push(menuItem);
34579                menuItem.on("beforecheckchange", onBeforeCheck);
34580            }
34581        },
34582
34583        // private
34584        unregisterCheckable : function(menuItem){
34585            var g = menuItem.group;
34586            if(g){
34587                groups[g].remove(menuItem);
34588                menuItem.un("beforecheckchange", onBeforeCheck);
34589            }
34590        }
34591    };
34592 }();/*
34593  * Based on:
34594  * Ext JS Library 1.1.1
34595  * Copyright(c) 2006-2007, Ext JS, LLC.
34596  *
34597  * Originally Released Under LGPL - original licence link has changed is not relivant.
34598  *
34599  * Fork - LGPL
34600  * <script type="text/javascript">
34601  */
34602  
34603
34604 /**
34605  * @class Roo.menu.BaseItem
34606  * @extends Roo.Component
34607  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34608  * management and base configuration options shared by all menu components.
34609  * @constructor
34610  * Creates a new BaseItem
34611  * @param {Object} config Configuration options
34612  */
34613 Roo.menu.BaseItem = function(config){
34614     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34615
34616     this.addEvents({
34617         /**
34618          * @event click
34619          * Fires when this item is clicked
34620          * @param {Roo.menu.BaseItem} this
34621          * @param {Roo.EventObject} e
34622          */
34623         click: true,
34624         /**
34625          * @event activate
34626          * Fires when this item is activated
34627          * @param {Roo.menu.BaseItem} this
34628          */
34629         activate : true,
34630         /**
34631          * @event deactivate
34632          * Fires when this item is deactivated
34633          * @param {Roo.menu.BaseItem} this
34634          */
34635         deactivate : true
34636     });
34637
34638     if(this.handler){
34639         this.on("click", this.handler, this.scope, true);
34640     }
34641 };
34642
34643 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34644     /**
34645      * @cfg {Function} handler
34646      * A function that will handle the click event of this menu item (defaults to undefined)
34647      */
34648     /**
34649      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34650      */
34651     canActivate : false,
34652     /**
34653      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34654      */
34655     activeClass : "x-menu-item-active",
34656     /**
34657      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34658      */
34659     hideOnClick : true,
34660     /**
34661      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34662      */
34663     hideDelay : 100,
34664
34665     // private
34666     ctype: "Roo.menu.BaseItem",
34667
34668     // private
34669     actionMode : "container",
34670
34671     // private
34672     render : function(container, parentMenu){
34673         this.parentMenu = parentMenu;
34674         Roo.menu.BaseItem.superclass.render.call(this, container);
34675         this.container.menuItemId = this.id;
34676     },
34677
34678     // private
34679     onRender : function(container, position){
34680         this.el = Roo.get(this.el);
34681         container.dom.appendChild(this.el.dom);
34682     },
34683
34684     // private
34685     onClick : function(e){
34686         if(!this.disabled && this.fireEvent("click", this, e) !== false
34687                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34688             this.handleClick(e);
34689         }else{
34690             e.stopEvent();
34691         }
34692     },
34693
34694     // private
34695     activate : function(){
34696         if(this.disabled){
34697             return false;
34698         }
34699         var li = this.container;
34700         li.addClass(this.activeClass);
34701         this.region = li.getRegion().adjust(2, 2, -2, -2);
34702         this.fireEvent("activate", this);
34703         return true;
34704     },
34705
34706     // private
34707     deactivate : function(){
34708         this.container.removeClass(this.activeClass);
34709         this.fireEvent("deactivate", this);
34710     },
34711
34712     // private
34713     shouldDeactivate : function(e){
34714         return !this.region || !this.region.contains(e.getPoint());
34715     },
34716
34717     // private
34718     handleClick : function(e){
34719         if(this.hideOnClick){
34720             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34721         }
34722     },
34723
34724     // private
34725     expandMenu : function(autoActivate){
34726         // do nothing
34727     },
34728
34729     // private
34730     hideMenu : function(){
34731         // do nothing
34732     }
34733 });/*
34734  * Based on:
34735  * Ext JS Library 1.1.1
34736  * Copyright(c) 2006-2007, Ext JS, LLC.
34737  *
34738  * Originally Released Under LGPL - original licence link has changed is not relivant.
34739  *
34740  * Fork - LGPL
34741  * <script type="text/javascript">
34742  */
34743  
34744 /**
34745  * @class Roo.menu.Adapter
34746  * @extends Roo.menu.BaseItem
34747  * 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.
34748  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34749  * @constructor
34750  * Creates a new Adapter
34751  * @param {Object} config Configuration options
34752  */
34753 Roo.menu.Adapter = function(component, config){
34754     Roo.menu.Adapter.superclass.constructor.call(this, config);
34755     this.component = component;
34756 };
34757 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34758     // private
34759     canActivate : true,
34760
34761     // private
34762     onRender : function(container, position){
34763         this.component.render(container);
34764         this.el = this.component.getEl();
34765     },
34766
34767     // private
34768     activate : function(){
34769         if(this.disabled){
34770             return false;
34771         }
34772         this.component.focus();
34773         this.fireEvent("activate", this);
34774         return true;
34775     },
34776
34777     // private
34778     deactivate : function(){
34779         this.fireEvent("deactivate", this);
34780     },
34781
34782     // private
34783     disable : function(){
34784         this.component.disable();
34785         Roo.menu.Adapter.superclass.disable.call(this);
34786     },
34787
34788     // private
34789     enable : function(){
34790         this.component.enable();
34791         Roo.menu.Adapter.superclass.enable.call(this);
34792     }
34793 });/*
34794  * Based on:
34795  * Ext JS Library 1.1.1
34796  * Copyright(c) 2006-2007, Ext JS, LLC.
34797  *
34798  * Originally Released Under LGPL - original licence link has changed is not relivant.
34799  *
34800  * Fork - LGPL
34801  * <script type="text/javascript">
34802  */
34803
34804 /**
34805  * @class Roo.menu.TextItem
34806  * @extends Roo.menu.BaseItem
34807  * Adds a static text string to a menu, usually used as either a heading or group separator.
34808  * Note: old style constructor with text is still supported.
34809  * 
34810  * @constructor
34811  * Creates a new TextItem
34812  * @param {Object} cfg Configuration
34813  */
34814 Roo.menu.TextItem = function(cfg){
34815     if (typeof(cfg) == 'string') {
34816         this.text = cfg;
34817     } else {
34818         Roo.apply(this,cfg);
34819     }
34820     
34821     Roo.menu.TextItem.superclass.constructor.call(this);
34822 };
34823
34824 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34825     /**
34826      * @cfg {Boolean} text Text to show on item.
34827      */
34828     text : '',
34829     
34830     /**
34831      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34832      */
34833     hideOnClick : false,
34834     /**
34835      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34836      */
34837     itemCls : "x-menu-text",
34838
34839     // private
34840     onRender : function(){
34841         var s = document.createElement("span");
34842         s.className = this.itemCls;
34843         s.innerHTML = this.text;
34844         this.el = s;
34845         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34846     }
34847 });/*
34848  * Based on:
34849  * Ext JS Library 1.1.1
34850  * Copyright(c) 2006-2007, Ext JS, LLC.
34851  *
34852  * Originally Released Under LGPL - original licence link has changed is not relivant.
34853  *
34854  * Fork - LGPL
34855  * <script type="text/javascript">
34856  */
34857
34858 /**
34859  * @class Roo.menu.Separator
34860  * @extends Roo.menu.BaseItem
34861  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34862  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34863  * @constructor
34864  * @param {Object} config Configuration options
34865  */
34866 Roo.menu.Separator = function(config){
34867     Roo.menu.Separator.superclass.constructor.call(this, config);
34868 };
34869
34870 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34871     /**
34872      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34873      */
34874     itemCls : "x-menu-sep",
34875     /**
34876      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34877      */
34878     hideOnClick : false,
34879
34880     // private
34881     onRender : function(li){
34882         var s = document.createElement("span");
34883         s.className = this.itemCls;
34884         s.innerHTML = "&#160;";
34885         this.el = s;
34886         li.addClass("x-menu-sep-li");
34887         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34888     }
34889 });/*
34890  * Based on:
34891  * Ext JS Library 1.1.1
34892  * Copyright(c) 2006-2007, Ext JS, LLC.
34893  *
34894  * Originally Released Under LGPL - original licence link has changed is not relivant.
34895  *
34896  * Fork - LGPL
34897  * <script type="text/javascript">
34898  */
34899 /**
34900  * @class Roo.menu.Item
34901  * @extends Roo.menu.BaseItem
34902  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34903  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34904  * activation and click handling.
34905  * @constructor
34906  * Creates a new Item
34907  * @param {Object} config Configuration options
34908  */
34909 Roo.menu.Item = function(config){
34910     Roo.menu.Item.superclass.constructor.call(this, config);
34911     if(this.menu){
34912         this.menu = Roo.menu.MenuMgr.get(this.menu);
34913     }
34914 };
34915 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34916     
34917     /**
34918      * @cfg {String} text
34919      * The text to show on the menu item.
34920      */
34921     text: '',
34922      /**
34923      * @cfg {String} HTML to render in menu
34924      * The text to show on the menu item (HTML version).
34925      */
34926     html: '',
34927     /**
34928      * @cfg {String} icon
34929      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34930      */
34931     icon: undefined,
34932     /**
34933      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34934      */
34935     itemCls : "x-menu-item",
34936     /**
34937      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34938      */
34939     canActivate : true,
34940     /**
34941      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34942      */
34943     showDelay: 200,
34944     // doc'd in BaseItem
34945     hideDelay: 200,
34946
34947     // private
34948     ctype: "Roo.menu.Item",
34949     
34950     // private
34951     onRender : function(container, position){
34952         var el = document.createElement("a");
34953         el.hideFocus = true;
34954         el.unselectable = "on";
34955         el.href = this.href || "#";
34956         if(this.hrefTarget){
34957             el.target = this.hrefTarget;
34958         }
34959         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34960         
34961         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34962         
34963         el.innerHTML = String.format(
34964                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34965                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34966         this.el = el;
34967         Roo.menu.Item.superclass.onRender.call(this, container, position);
34968     },
34969
34970     /**
34971      * Sets the text to display in this menu item
34972      * @param {String} text The text to display
34973      * @param {Boolean} isHTML true to indicate text is pure html.
34974      */
34975     setText : function(text, isHTML){
34976         if (isHTML) {
34977             this.html = text;
34978         } else {
34979             this.text = text;
34980             this.html = '';
34981         }
34982         if(this.rendered){
34983             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34984      
34985             this.el.update(String.format(
34986                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34987                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34988             this.parentMenu.autoWidth();
34989         }
34990     },
34991
34992     // private
34993     handleClick : function(e){
34994         if(!this.href){ // if no link defined, stop the event automatically
34995             e.stopEvent();
34996         }
34997         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34998     },
34999
35000     // private
35001     activate : function(autoExpand){
35002         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35003             this.focus();
35004             if(autoExpand){
35005                 this.expandMenu();
35006             }
35007         }
35008         return true;
35009     },
35010
35011     // private
35012     shouldDeactivate : function(e){
35013         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35014             if(this.menu && this.menu.isVisible()){
35015                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35016             }
35017             return true;
35018         }
35019         return false;
35020     },
35021
35022     // private
35023     deactivate : function(){
35024         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35025         this.hideMenu();
35026     },
35027
35028     // private
35029     expandMenu : function(autoActivate){
35030         if(!this.disabled && this.menu){
35031             clearTimeout(this.hideTimer);
35032             delete this.hideTimer;
35033             if(!this.menu.isVisible() && !this.showTimer){
35034                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35035             }else if (this.menu.isVisible() && autoActivate){
35036                 this.menu.tryActivate(0, 1);
35037             }
35038         }
35039     },
35040
35041     // private
35042     deferExpand : function(autoActivate){
35043         delete this.showTimer;
35044         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35045         if(autoActivate){
35046             this.menu.tryActivate(0, 1);
35047         }
35048     },
35049
35050     // private
35051     hideMenu : function(){
35052         clearTimeout(this.showTimer);
35053         delete this.showTimer;
35054         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35055             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35056         }
35057     },
35058
35059     // private
35060     deferHide : function(){
35061         delete this.hideTimer;
35062         this.menu.hide();
35063     }
35064 });/*
35065  * Based on:
35066  * Ext JS Library 1.1.1
35067  * Copyright(c) 2006-2007, Ext JS, LLC.
35068  *
35069  * Originally Released Under LGPL - original licence link has changed is not relivant.
35070  *
35071  * Fork - LGPL
35072  * <script type="text/javascript">
35073  */
35074  
35075 /**
35076  * @class Roo.menu.CheckItem
35077  * @extends Roo.menu.Item
35078  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35079  * @constructor
35080  * Creates a new CheckItem
35081  * @param {Object} config Configuration options
35082  */
35083 Roo.menu.CheckItem = function(config){
35084     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35085     this.addEvents({
35086         /**
35087          * @event beforecheckchange
35088          * Fires before the checked value is set, providing an opportunity to cancel if needed
35089          * @param {Roo.menu.CheckItem} this
35090          * @param {Boolean} checked The new checked value that will be set
35091          */
35092         "beforecheckchange" : true,
35093         /**
35094          * @event checkchange
35095          * Fires after the checked value has been set
35096          * @param {Roo.menu.CheckItem} this
35097          * @param {Boolean} checked The checked value that was set
35098          */
35099         "checkchange" : true
35100     });
35101     if(this.checkHandler){
35102         this.on('checkchange', this.checkHandler, this.scope);
35103     }
35104 };
35105 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35106     /**
35107      * @cfg {String} group
35108      * All check items with the same group name will automatically be grouped into a single-select
35109      * radio button group (defaults to '')
35110      */
35111     /**
35112      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35113      */
35114     itemCls : "x-menu-item x-menu-check-item",
35115     /**
35116      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35117      */
35118     groupClass : "x-menu-group-item",
35119
35120     /**
35121      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35122      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35123      * initialized with checked = true will be rendered as checked.
35124      */
35125     checked: false,
35126
35127     // private
35128     ctype: "Roo.menu.CheckItem",
35129
35130     // private
35131     onRender : function(c){
35132         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35133         if(this.group){
35134             this.el.addClass(this.groupClass);
35135         }
35136         Roo.menu.MenuMgr.registerCheckable(this);
35137         if(this.checked){
35138             this.checked = false;
35139             this.setChecked(true, true);
35140         }
35141     },
35142
35143     // private
35144     destroy : function(){
35145         if(this.rendered){
35146             Roo.menu.MenuMgr.unregisterCheckable(this);
35147         }
35148         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35149     },
35150
35151     /**
35152      * Set the checked state of this item
35153      * @param {Boolean} checked The new checked value
35154      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35155      */
35156     setChecked : function(state, suppressEvent){
35157         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35158             if(this.container){
35159                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35160             }
35161             this.checked = state;
35162             if(suppressEvent !== true){
35163                 this.fireEvent("checkchange", this, state);
35164             }
35165         }
35166     },
35167
35168     // private
35169     handleClick : function(e){
35170        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35171            this.setChecked(!this.checked);
35172        }
35173        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35174     }
35175 });/*
35176  * Based on:
35177  * Ext JS Library 1.1.1
35178  * Copyright(c) 2006-2007, Ext JS, LLC.
35179  *
35180  * Originally Released Under LGPL - original licence link has changed is not relivant.
35181  *
35182  * Fork - LGPL
35183  * <script type="text/javascript">
35184  */
35185  
35186 /**
35187  * @class Roo.menu.DateItem
35188  * @extends Roo.menu.Adapter
35189  * A menu item that wraps the {@link Roo.DatPicker} component.
35190  * @constructor
35191  * Creates a new DateItem
35192  * @param {Object} config Configuration options
35193  */
35194 Roo.menu.DateItem = function(config){
35195     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35196     /** The Roo.DatePicker object @type Roo.DatePicker */
35197     this.picker = this.component;
35198     this.addEvents({select: true});
35199     
35200     this.picker.on("render", function(picker){
35201         picker.getEl().swallowEvent("click");
35202         picker.container.addClass("x-menu-date-item");
35203     });
35204
35205     this.picker.on("select", this.onSelect, this);
35206 };
35207
35208 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35209     // private
35210     onSelect : function(picker, date){
35211         this.fireEvent("select", this, date, picker);
35212         Roo.menu.DateItem.superclass.handleClick.call(this);
35213     }
35214 });/*
35215  * Based on:
35216  * Ext JS Library 1.1.1
35217  * Copyright(c) 2006-2007, Ext JS, LLC.
35218  *
35219  * Originally Released Under LGPL - original licence link has changed is not relivant.
35220  *
35221  * Fork - LGPL
35222  * <script type="text/javascript">
35223  */
35224  
35225 /**
35226  * @class Roo.menu.ColorItem
35227  * @extends Roo.menu.Adapter
35228  * A menu item that wraps the {@link Roo.ColorPalette} component.
35229  * @constructor
35230  * Creates a new ColorItem
35231  * @param {Object} config Configuration options
35232  */
35233 Roo.menu.ColorItem = function(config){
35234     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35235     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35236     this.palette = this.component;
35237     this.relayEvents(this.palette, ["select"]);
35238     if(this.selectHandler){
35239         this.on('select', this.selectHandler, this.scope);
35240     }
35241 };
35242 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35243  * Based on:
35244  * Ext JS Library 1.1.1
35245  * Copyright(c) 2006-2007, Ext JS, LLC.
35246  *
35247  * Originally Released Under LGPL - original licence link has changed is not relivant.
35248  *
35249  * Fork - LGPL
35250  * <script type="text/javascript">
35251  */
35252  
35253
35254 /**
35255  * @class Roo.menu.DateMenu
35256  * @extends Roo.menu.Menu
35257  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35258  * @constructor
35259  * Creates a new DateMenu
35260  * @param {Object} config Configuration options
35261  */
35262 Roo.menu.DateMenu = function(config){
35263     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35264     this.plain = true;
35265     var di = new Roo.menu.DateItem(config);
35266     this.add(di);
35267     /**
35268      * The {@link Roo.DatePicker} instance for this DateMenu
35269      * @type DatePicker
35270      */
35271     this.picker = di.picker;
35272     /**
35273      * @event select
35274      * @param {DatePicker} picker
35275      * @param {Date} date
35276      */
35277     this.relayEvents(di, ["select"]);
35278
35279     this.on('beforeshow', function(){
35280         if(this.picker){
35281             this.picker.hideMonthPicker(true);
35282         }
35283     }, this);
35284 };
35285 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35286     cls:'x-date-menu'
35287 });/*
35288  * Based on:
35289  * Ext JS Library 1.1.1
35290  * Copyright(c) 2006-2007, Ext JS, LLC.
35291  *
35292  * Originally Released Under LGPL - original licence link has changed is not relivant.
35293  *
35294  * Fork - LGPL
35295  * <script type="text/javascript">
35296  */
35297  
35298
35299 /**
35300  * @class Roo.menu.ColorMenu
35301  * @extends Roo.menu.Menu
35302  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35303  * @constructor
35304  * Creates a new ColorMenu
35305  * @param {Object} config Configuration options
35306  */
35307 Roo.menu.ColorMenu = function(config){
35308     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35309     this.plain = true;
35310     var ci = new Roo.menu.ColorItem(config);
35311     this.add(ci);
35312     /**
35313      * The {@link Roo.ColorPalette} instance for this ColorMenu
35314      * @type ColorPalette
35315      */
35316     this.palette = ci.palette;
35317     /**
35318      * @event select
35319      * @param {ColorPalette} palette
35320      * @param {String} color
35321      */
35322     this.relayEvents(ci, ["select"]);
35323 };
35324 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.Field
35337  * @extends Roo.BoxComponent
35338  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35339  * @constructor
35340  * Creates a new Field
35341  * @param {Object} config Configuration options
35342  */
35343 Roo.form.Field = function(config){
35344     Roo.form.Field.superclass.constructor.call(this, config);
35345 };
35346
35347 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35348     /**
35349      * @cfg {String} fieldLabel Label to use when rendering a form.
35350      */
35351        /**
35352      * @cfg {String} qtip Mouse over tip
35353      */
35354      
35355     /**
35356      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35357      */
35358     invalidClass : "x-form-invalid",
35359     /**
35360      * @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")
35361      */
35362     invalidText : "The value in this field is invalid",
35363     /**
35364      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35365      */
35366     focusClass : "x-form-focus",
35367     /**
35368      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35369       automatic validation (defaults to "keyup").
35370      */
35371     validationEvent : "keyup",
35372     /**
35373      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35374      */
35375     validateOnBlur : true,
35376     /**
35377      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35378      */
35379     validationDelay : 250,
35380     /**
35381      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35382      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35383      */
35384     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35385     /**
35386      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35387      */
35388     fieldClass : "x-form-field",
35389     /**
35390      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35391      *<pre>
35392 Value         Description
35393 -----------   ----------------------------------------------------------------------
35394 qtip          Display a quick tip when the user hovers over the field
35395 title         Display a default browser title attribute popup
35396 under         Add a block div beneath the field containing the error text
35397 side          Add an error icon to the right of the field with a popup on hover
35398 [element id]  Add the error text directly to the innerHTML of the specified element
35399 </pre>
35400      */
35401     msgTarget : 'qtip',
35402     /**
35403      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35404      */
35405     msgFx : 'normal',
35406
35407     /**
35408      * @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.
35409      */
35410     readOnly : false,
35411
35412     /**
35413      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35414      */
35415     disabled : false,
35416
35417     /**
35418      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35419      */
35420     inputType : undefined,
35421     
35422     /**
35423      * @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).
35424          */
35425         tabIndex : undefined,
35426         
35427     // private
35428     isFormField : true,
35429
35430     // private
35431     hasFocus : false,
35432     /**
35433      * @property {Roo.Element} fieldEl
35434      * Element Containing the rendered Field (with label etc.)
35435      */
35436     /**
35437      * @cfg {Mixed} value A value to initialize this field with.
35438      */
35439     value : undefined,
35440
35441     /**
35442      * @cfg {String} name The field's HTML name attribute.
35443      */
35444     /**
35445      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35446      */
35447
35448         // private ??
35449         initComponent : function(){
35450         Roo.form.Field.superclass.initComponent.call(this);
35451         this.addEvents({
35452             /**
35453              * @event focus
35454              * Fires when this field receives input focus.
35455              * @param {Roo.form.Field} this
35456              */
35457             focus : true,
35458             /**
35459              * @event blur
35460              * Fires when this field loses input focus.
35461              * @param {Roo.form.Field} this
35462              */
35463             blur : true,
35464             /**
35465              * @event specialkey
35466              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35467              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35468              * @param {Roo.form.Field} this
35469              * @param {Roo.EventObject} e The event object
35470              */
35471             specialkey : true,
35472             /**
35473              * @event change
35474              * Fires just before the field blurs if the field value has changed.
35475              * @param {Roo.form.Field} this
35476              * @param {Mixed} newValue The new value
35477              * @param {Mixed} oldValue The original value
35478              */
35479             change : true,
35480             /**
35481              * @event invalid
35482              * Fires after the field has been marked as invalid.
35483              * @param {Roo.form.Field} this
35484              * @param {String} msg The validation message
35485              */
35486             invalid : true,
35487             /**
35488              * @event valid
35489              * Fires after the field has been validated with no errors.
35490              * @param {Roo.form.Field} this
35491              */
35492             valid : true,
35493              /**
35494              * @event keyup
35495              * Fires after the key up
35496              * @param {Roo.form.Field} this
35497              * @param {Roo.EventObject}  e The event Object
35498              */
35499             keyup : true
35500         });
35501     },
35502
35503     /**
35504      * Returns the name attribute of the field if available
35505      * @return {String} name The field name
35506      */
35507     getName: function(){
35508          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35509     },
35510
35511     // private
35512     onRender : function(ct, position){
35513         Roo.form.Field.superclass.onRender.call(this, ct, position);
35514         if(!this.el){
35515             var cfg = this.getAutoCreate();
35516             if(!cfg.name){
35517                 cfg.name = this.name || this.id;
35518             }
35519             if(this.inputType){
35520                 cfg.type = this.inputType;
35521             }
35522             this.el = ct.createChild(cfg, position);
35523         }
35524         var type = this.el.dom.type;
35525         if(type){
35526             if(type == 'password'){
35527                 type = 'text';
35528             }
35529             this.el.addClass('x-form-'+type);
35530         }
35531         if(this.readOnly){
35532             this.el.dom.readOnly = true;
35533         }
35534         if(this.tabIndex !== undefined){
35535             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35536         }
35537
35538         this.el.addClass([this.fieldClass, this.cls]);
35539         this.initValue();
35540     },
35541
35542     /**
35543      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35544      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35545      * @return {Roo.form.Field} this
35546      */
35547     applyTo : function(target){
35548         this.allowDomMove = false;
35549         this.el = Roo.get(target);
35550         this.render(this.el.dom.parentNode);
35551         return this;
35552     },
35553
35554     // private
35555     initValue : function(){
35556         if(this.value !== undefined){
35557             this.setValue(this.value);
35558         }else if(this.el.dom.value.length > 0){
35559             this.setValue(this.el.dom.value);
35560         }
35561     },
35562
35563     /**
35564      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35565      */
35566     isDirty : function() {
35567         if(this.disabled) {
35568             return false;
35569         }
35570         return String(this.getValue()) !== String(this.originalValue);
35571     },
35572
35573     // private
35574     afterRender : function(){
35575         Roo.form.Field.superclass.afterRender.call(this);
35576         this.initEvents();
35577     },
35578
35579     // private
35580     fireKey : function(e){
35581         //Roo.log('field ' + e.getKey());
35582         if(e.isNavKeyPress()){
35583             this.fireEvent("specialkey", this, e);
35584         }
35585     },
35586
35587     /**
35588      * Resets the current field value to the originally loaded value and clears any validation messages
35589      */
35590     reset : function(){
35591         this.setValue(this.originalValue);
35592         this.clearInvalid();
35593     },
35594
35595     // private
35596     initEvents : function(){
35597         // safari killled keypress - so keydown is now used..
35598         this.el.on("keydown" , this.fireKey,  this);
35599         this.el.on("focus", this.onFocus,  this);
35600         this.el.on("blur", this.onBlur,  this);
35601         this.el.relayEvent('keyup', this);
35602
35603         // reference to original value for reset
35604         this.originalValue = this.getValue();
35605     },
35606
35607     // private
35608     onFocus : function(){
35609         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35610             this.el.addClass(this.focusClass);
35611         }
35612         if(!this.hasFocus){
35613             this.hasFocus = true;
35614             this.startValue = this.getValue();
35615             this.fireEvent("focus", this);
35616         }
35617     },
35618
35619     beforeBlur : Roo.emptyFn,
35620
35621     // private
35622     onBlur : function(){
35623         this.beforeBlur();
35624         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35625             this.el.removeClass(this.focusClass);
35626         }
35627         this.hasFocus = false;
35628         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35629             this.validate();
35630         }
35631         var v = this.getValue();
35632         if(String(v) !== String(this.startValue)){
35633             this.fireEvent('change', this, v, this.startValue);
35634         }
35635         this.fireEvent("blur", this);
35636     },
35637
35638     /**
35639      * Returns whether or not the field value is currently valid
35640      * @param {Boolean} preventMark True to disable marking the field invalid
35641      * @return {Boolean} True if the value is valid, else false
35642      */
35643     isValid : function(preventMark){
35644         if(this.disabled){
35645             return true;
35646         }
35647         var restore = this.preventMark;
35648         this.preventMark = preventMark === true;
35649         var v = this.validateValue(this.processValue(this.getRawValue()));
35650         this.preventMark = restore;
35651         return v;
35652     },
35653
35654     /**
35655      * Validates the field value
35656      * @return {Boolean} True if the value is valid, else false
35657      */
35658     validate : function(){
35659         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35660             this.clearInvalid();
35661             return true;
35662         }
35663         return false;
35664     },
35665
35666     processValue : function(value){
35667         return value;
35668     },
35669
35670     // private
35671     // Subclasses should provide the validation implementation by overriding this
35672     validateValue : function(value){
35673         return true;
35674     },
35675
35676     /**
35677      * Mark this field as invalid
35678      * @param {String} msg The validation message
35679      */
35680     markInvalid : function(msg){
35681         if(!this.rendered || this.preventMark){ // not rendered
35682             return;
35683         }
35684         this.el.addClass(this.invalidClass);
35685         msg = msg || this.invalidText;
35686         switch(this.msgTarget){
35687             case 'qtip':
35688                 this.el.dom.qtip = msg;
35689                 this.el.dom.qclass = 'x-form-invalid-tip';
35690                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35691                     Roo.QuickTips.enable();
35692                 }
35693                 break;
35694             case 'title':
35695                 this.el.dom.title = msg;
35696                 break;
35697             case 'under':
35698                 if(!this.errorEl){
35699                     var elp = this.el.findParent('.x-form-element', 5, true);
35700                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35701                     this.errorEl.setWidth(elp.getWidth(true)-20);
35702                 }
35703                 this.errorEl.update(msg);
35704                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35705                 break;
35706             case 'side':
35707                 if(!this.errorIcon){
35708                     var elp = this.el.findParent('.x-form-element', 5, true);
35709                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35710                 }
35711                 this.alignErrorIcon();
35712                 this.errorIcon.dom.qtip = msg;
35713                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35714                 this.errorIcon.show();
35715                 this.on('resize', this.alignErrorIcon, this);
35716                 break;
35717             default:
35718                 var t = Roo.getDom(this.msgTarget);
35719                 t.innerHTML = msg;
35720                 t.style.display = this.msgDisplay;
35721                 break;
35722         }
35723         this.fireEvent('invalid', this, msg);
35724     },
35725
35726     // private
35727     alignErrorIcon : function(){
35728         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35729     },
35730
35731     /**
35732      * Clear any invalid styles/messages for this field
35733      */
35734     clearInvalid : function(){
35735         if(!this.rendered || this.preventMark){ // not rendered
35736             return;
35737         }
35738         this.el.removeClass(this.invalidClass);
35739         switch(this.msgTarget){
35740             case 'qtip':
35741                 this.el.dom.qtip = '';
35742                 break;
35743             case 'title':
35744                 this.el.dom.title = '';
35745                 break;
35746             case 'under':
35747                 if(this.errorEl){
35748                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35749                 }
35750                 break;
35751             case 'side':
35752                 if(this.errorIcon){
35753                     this.errorIcon.dom.qtip = '';
35754                     this.errorIcon.hide();
35755                     this.un('resize', this.alignErrorIcon, this);
35756                 }
35757                 break;
35758             default:
35759                 var t = Roo.getDom(this.msgTarget);
35760                 t.innerHTML = '';
35761                 t.style.display = 'none';
35762                 break;
35763         }
35764         this.fireEvent('valid', this);
35765     },
35766
35767     /**
35768      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35769      * @return {Mixed} value The field value
35770      */
35771     getRawValue : function(){
35772         var v = this.el.getValue();
35773         if(v === this.emptyText){
35774             v = '';
35775         }
35776         return v;
35777     },
35778
35779     /**
35780      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35781      * @return {Mixed} value The field value
35782      */
35783     getValue : function(){
35784         var v = this.el.getValue();
35785         if(v === this.emptyText || v === undefined){
35786             v = '';
35787         }
35788         return v;
35789     },
35790
35791     /**
35792      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35793      * @param {Mixed} value The value to set
35794      */
35795     setRawValue : function(v){
35796         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35797     },
35798
35799     /**
35800      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35801      * @param {Mixed} value The value to set
35802      */
35803     setValue : function(v){
35804         this.value = v;
35805         if(this.rendered){
35806             this.el.dom.value = (v === null || v === undefined ? '' : v);
35807             this.validate();
35808         }
35809     },
35810
35811     adjustSize : function(w, h){
35812         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35813         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35814         return s;
35815     },
35816
35817     adjustWidth : function(tag, w){
35818         tag = tag.toLowerCase();
35819         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35820             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35821                 if(tag == 'input'){
35822                     return w + 2;
35823                 }
35824                 if(tag = 'textarea'){
35825                     return w-2;
35826                 }
35827             }else if(Roo.isOpera){
35828                 if(tag == 'input'){
35829                     return w + 2;
35830                 }
35831                 if(tag = 'textarea'){
35832                     return w-2;
35833                 }
35834             }
35835         }
35836         return w;
35837     }
35838 });
35839
35840
35841 // anything other than normal should be considered experimental
35842 Roo.form.Field.msgFx = {
35843     normal : {
35844         show: function(msgEl, f){
35845             msgEl.setDisplayed('block');
35846         },
35847
35848         hide : function(msgEl, f){
35849             msgEl.setDisplayed(false).update('');
35850         }
35851     },
35852
35853     slide : {
35854         show: function(msgEl, f){
35855             msgEl.slideIn('t', {stopFx:true});
35856         },
35857
35858         hide : function(msgEl, f){
35859             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35860         }
35861     },
35862
35863     slideRight : {
35864         show: function(msgEl, f){
35865             msgEl.fixDisplay();
35866             msgEl.alignTo(f.el, 'tl-tr');
35867             msgEl.slideIn('l', {stopFx:true});
35868         },
35869
35870         hide : function(msgEl, f){
35871             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35872         }
35873     }
35874 };/*
35875  * Based on:
35876  * Ext JS Library 1.1.1
35877  * Copyright(c) 2006-2007, Ext JS, LLC.
35878  *
35879  * Originally Released Under LGPL - original licence link has changed is not relivant.
35880  *
35881  * Fork - LGPL
35882  * <script type="text/javascript">
35883  */
35884  
35885
35886 /**
35887  * @class Roo.form.TextField
35888  * @extends Roo.form.Field
35889  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35890  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35891  * @constructor
35892  * Creates a new TextField
35893  * @param {Object} config Configuration options
35894  */
35895 Roo.form.TextField = function(config){
35896     Roo.form.TextField.superclass.constructor.call(this, config);
35897     this.addEvents({
35898         /**
35899          * @event autosize
35900          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35901          * according to the default logic, but this event provides a hook for the developer to apply additional
35902          * logic at runtime to resize the field if needed.
35903              * @param {Roo.form.Field} this This text field
35904              * @param {Number} width The new field width
35905              */
35906         autosize : true
35907     });
35908 };
35909
35910 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35911     /**
35912      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35913      */
35914     grow : false,
35915     /**
35916      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35917      */
35918     growMin : 30,
35919     /**
35920      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35921      */
35922     growMax : 800,
35923     /**
35924      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35925      */
35926     vtype : null,
35927     /**
35928      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35929      */
35930     maskRe : null,
35931     /**
35932      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35933      */
35934     disableKeyFilter : false,
35935     /**
35936      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35937      */
35938     allowBlank : true,
35939     /**
35940      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35941      */
35942     minLength : 0,
35943     /**
35944      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35945      */
35946     maxLength : Number.MAX_VALUE,
35947     /**
35948      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35949      */
35950     minLengthText : "The minimum length for this field is {0}",
35951     /**
35952      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35953      */
35954     maxLengthText : "The maximum length for this field is {0}",
35955     /**
35956      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35957      */
35958     selectOnFocus : false,
35959     /**
35960      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35961      */
35962     blankText : "This field is required",
35963     /**
35964      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35965      * If available, this function will be called only after the basic validators all return true, and will be passed the
35966      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35967      */
35968     validator : null,
35969     /**
35970      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35971      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35972      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35973      */
35974     regex : null,
35975     /**
35976      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35977      */
35978     regexText : "",
35979     /**
35980      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35981      */
35982     emptyText : null,
35983     /**
35984      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35985      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35986      */
35987     emptyClass : 'x-form-empty-field',
35988
35989     // private
35990     initEvents : function(){
35991         Roo.form.TextField.superclass.initEvents.call(this);
35992         if(this.validationEvent == 'keyup'){
35993             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35994             this.el.on('keyup', this.filterValidation, this);
35995         }
35996         else if(this.validationEvent !== false){
35997             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35998         }
35999         if(this.selectOnFocus || this.emptyText){
36000             this.on("focus", this.preFocus, this);
36001             if(this.emptyText){
36002                 this.on('blur', this.postBlur, this);
36003                 this.applyEmptyText();
36004             }
36005         }
36006         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36007             this.el.on("keypress", this.filterKeys, this);
36008         }
36009         if(this.grow){
36010             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36011             this.el.on("click", this.autoSize,  this);
36012         }
36013     },
36014
36015     processValue : function(value){
36016         if(this.stripCharsRe){
36017             var newValue = value.replace(this.stripCharsRe, '');
36018             if(newValue !== value){
36019                 this.setRawValue(newValue);
36020                 return newValue;
36021             }
36022         }
36023         return value;
36024     },
36025
36026     filterValidation : function(e){
36027         if(!e.isNavKeyPress()){
36028             this.validationTask.delay(this.validationDelay);
36029         }
36030     },
36031
36032     // private
36033     onKeyUp : function(e){
36034         if(!e.isNavKeyPress()){
36035             this.autoSize();
36036         }
36037     },
36038
36039     /**
36040      * Resets the current field value to the originally-loaded value and clears any validation messages.
36041      * Also adds emptyText and emptyClass if the original value was blank.
36042      */
36043     reset : function(){
36044         Roo.form.TextField.superclass.reset.call(this);
36045         this.applyEmptyText();
36046     },
36047
36048     applyEmptyText : function(){
36049         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36050             this.setRawValue(this.emptyText);
36051             this.el.addClass(this.emptyClass);
36052         }
36053     },
36054
36055     // private
36056     preFocus : function(){
36057         if(this.emptyText){
36058             if(this.el.dom.value == this.emptyText){
36059                 this.setRawValue('');
36060             }
36061             this.el.removeClass(this.emptyClass);
36062         }
36063         if(this.selectOnFocus){
36064             this.el.dom.select();
36065         }
36066     },
36067
36068     // private
36069     postBlur : function(){
36070         this.applyEmptyText();
36071     },
36072
36073     // private
36074     filterKeys : function(e){
36075         var k = e.getKey();
36076         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36077             return;
36078         }
36079         var c = e.getCharCode(), cc = String.fromCharCode(c);
36080         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36081             return;
36082         }
36083         if(!this.maskRe.test(cc)){
36084             e.stopEvent();
36085         }
36086     },
36087
36088     setValue : function(v){
36089         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36090             this.el.removeClass(this.emptyClass);
36091         }
36092         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36093         this.applyEmptyText();
36094         this.autoSize();
36095     },
36096
36097     /**
36098      * Validates a value according to the field's validation rules and marks the field as invalid
36099      * if the validation fails
36100      * @param {Mixed} value The value to validate
36101      * @return {Boolean} True if the value is valid, else false
36102      */
36103     validateValue : function(value){
36104         if(value.length < 1 || value === this.emptyText){ // if it's blank
36105              if(this.allowBlank){
36106                 this.clearInvalid();
36107                 return true;
36108              }else{
36109                 this.markInvalid(this.blankText);
36110                 return false;
36111              }
36112         }
36113         if(value.length < this.minLength){
36114             this.markInvalid(String.format(this.minLengthText, this.minLength));
36115             return false;
36116         }
36117         if(value.length > this.maxLength){
36118             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36119             return false;
36120         }
36121         if(this.vtype){
36122             var vt = Roo.form.VTypes;
36123             if(!vt[this.vtype](value, this)){
36124                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36125                 return false;
36126             }
36127         }
36128         if(typeof this.validator == "function"){
36129             var msg = this.validator(value);
36130             if(msg !== true){
36131                 this.markInvalid(msg);
36132                 return false;
36133             }
36134         }
36135         if(this.regex && !this.regex.test(value)){
36136             this.markInvalid(this.regexText);
36137             return false;
36138         }
36139         return true;
36140     },
36141
36142     /**
36143      * Selects text in this field
36144      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36145      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36146      */
36147     selectText : function(start, end){
36148         var v = this.getRawValue();
36149         if(v.length > 0){
36150             start = start === undefined ? 0 : start;
36151             end = end === undefined ? v.length : end;
36152             var d = this.el.dom;
36153             if(d.setSelectionRange){
36154                 d.setSelectionRange(start, end);
36155             }else if(d.createTextRange){
36156                 var range = d.createTextRange();
36157                 range.moveStart("character", start);
36158                 range.moveEnd("character", v.length-end);
36159                 range.select();
36160             }
36161         }
36162     },
36163
36164     /**
36165      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36166      * This only takes effect if grow = true, and fires the autosize event.
36167      */
36168     autoSize : function(){
36169         if(!this.grow || !this.rendered){
36170             return;
36171         }
36172         if(!this.metrics){
36173             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36174         }
36175         var el = this.el;
36176         var v = el.dom.value;
36177         var d = document.createElement('div');
36178         d.appendChild(document.createTextNode(v));
36179         v = d.innerHTML;
36180         d = null;
36181         v += "&#160;";
36182         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36183         this.el.setWidth(w);
36184         this.fireEvent("autosize", this, w);
36185     }
36186 });/*
36187  * Based on:
36188  * Ext JS Library 1.1.1
36189  * Copyright(c) 2006-2007, Ext JS, LLC.
36190  *
36191  * Originally Released Under LGPL - original licence link has changed is not relivant.
36192  *
36193  * Fork - LGPL
36194  * <script type="text/javascript">
36195  */
36196  
36197 /**
36198  * @class Roo.form.Hidden
36199  * @extends Roo.form.TextField
36200  * Simple Hidden element used on forms 
36201  * 
36202  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36203  * 
36204  * @constructor
36205  * Creates a new Hidden form element.
36206  * @param {Object} config Configuration options
36207  */
36208
36209
36210
36211 // easy hidden field...
36212 Roo.form.Hidden = function(config){
36213     Roo.form.Hidden.superclass.constructor.call(this, config);
36214 };
36215   
36216 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36217     fieldLabel:      '',
36218     inputType:      'hidden',
36219     width:          50,
36220     allowBlank:     true,
36221     labelSeparator: '',
36222     hidden:         true,
36223     itemCls :       'x-form-item-display-none'
36224
36225
36226 });
36227
36228
36229 /*
36230  * Based on:
36231  * Ext JS Library 1.1.1
36232  * Copyright(c) 2006-2007, Ext JS, LLC.
36233  *
36234  * Originally Released Under LGPL - original licence link has changed is not relivant.
36235  *
36236  * Fork - LGPL
36237  * <script type="text/javascript">
36238  */
36239  
36240 /**
36241  * @class Roo.form.TriggerField
36242  * @extends Roo.form.TextField
36243  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36244  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36245  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36246  * for which you can provide a custom implementation.  For example:
36247  * <pre><code>
36248 var trigger = new Roo.form.TriggerField();
36249 trigger.onTriggerClick = myTriggerFn;
36250 trigger.applyTo('my-field');
36251 </code></pre>
36252  *
36253  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36254  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36255  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36256  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36257  * @constructor
36258  * Create a new TriggerField.
36259  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36260  * to the base TextField)
36261  */
36262 Roo.form.TriggerField = function(config){
36263     this.mimicing = false;
36264     Roo.form.TriggerField.superclass.constructor.call(this, config);
36265 };
36266
36267 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36268     /**
36269      * @cfg {String} triggerClass A CSS class to apply to the trigger
36270      */
36271     /**
36272      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36273      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36274      */
36275     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36276     /**
36277      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36278      */
36279     hideTrigger:false,
36280
36281     /** @cfg {Boolean} grow @hide */
36282     /** @cfg {Number} growMin @hide */
36283     /** @cfg {Number} growMax @hide */
36284
36285     /**
36286      * @hide 
36287      * @method
36288      */
36289     autoSize: Roo.emptyFn,
36290     // private
36291     monitorTab : true,
36292     // private
36293     deferHeight : true,
36294
36295     
36296     actionMode : 'wrap',
36297     // private
36298     onResize : function(w, h){
36299         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36300         if(typeof w == 'number'){
36301             var x = w - this.trigger.getWidth();
36302             this.el.setWidth(this.adjustWidth('input', x));
36303             this.trigger.setStyle('left', x+'px');
36304         }
36305     },
36306
36307     // private
36308     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36309
36310     // private
36311     getResizeEl : function(){
36312         return this.wrap;
36313     },
36314
36315     // private
36316     getPositionEl : function(){
36317         return this.wrap;
36318     },
36319
36320     // private
36321     alignErrorIcon : function(){
36322         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36323     },
36324
36325     // private
36326     onRender : function(ct, position){
36327         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36328         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36329         this.trigger = this.wrap.createChild(this.triggerConfig ||
36330                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36331         if(this.hideTrigger){
36332             this.trigger.setDisplayed(false);
36333         }
36334         this.initTrigger();
36335         if(!this.width){
36336             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36337         }
36338     },
36339
36340     // private
36341     initTrigger : function(){
36342         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36343         this.trigger.addClassOnOver('x-form-trigger-over');
36344         this.trigger.addClassOnClick('x-form-trigger-click');
36345     },
36346
36347     // private
36348     onDestroy : function(){
36349         if(this.trigger){
36350             this.trigger.removeAllListeners();
36351             this.trigger.remove();
36352         }
36353         if(this.wrap){
36354             this.wrap.remove();
36355         }
36356         Roo.form.TriggerField.superclass.onDestroy.call(this);
36357     },
36358
36359     // private
36360     onFocus : function(){
36361         Roo.form.TriggerField.superclass.onFocus.call(this);
36362         if(!this.mimicing){
36363             this.wrap.addClass('x-trigger-wrap-focus');
36364             this.mimicing = true;
36365             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36366             if(this.monitorTab){
36367                 this.el.on("keydown", this.checkTab, this);
36368             }
36369         }
36370     },
36371
36372     // private
36373     checkTab : function(e){
36374         if(e.getKey() == e.TAB){
36375             this.triggerBlur();
36376         }
36377     },
36378
36379     // private
36380     onBlur : function(){
36381         // do nothing
36382     },
36383
36384     // private
36385     mimicBlur : function(e, t){
36386         if(!this.wrap.contains(t) && this.validateBlur()){
36387             this.triggerBlur();
36388         }
36389     },
36390
36391     // private
36392     triggerBlur : function(){
36393         this.mimicing = false;
36394         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36395         if(this.monitorTab){
36396             this.el.un("keydown", this.checkTab, this);
36397         }
36398         this.wrap.removeClass('x-trigger-wrap-focus');
36399         Roo.form.TriggerField.superclass.onBlur.call(this);
36400     },
36401
36402     // private
36403     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36404     validateBlur : function(e, t){
36405         return true;
36406     },
36407
36408     // private
36409     onDisable : function(){
36410         Roo.form.TriggerField.superclass.onDisable.call(this);
36411         if(this.wrap){
36412             this.wrap.addClass('x-item-disabled');
36413         }
36414     },
36415
36416     // private
36417     onEnable : function(){
36418         Roo.form.TriggerField.superclass.onEnable.call(this);
36419         if(this.wrap){
36420             this.wrap.removeClass('x-item-disabled');
36421         }
36422     },
36423
36424     // private
36425     onShow : function(){
36426         var ae = this.getActionEl();
36427         
36428         if(ae){
36429             ae.dom.style.display = '';
36430             ae.dom.style.visibility = 'visible';
36431         }
36432     },
36433
36434     // private
36435     
36436     onHide : function(){
36437         var ae = this.getActionEl();
36438         ae.dom.style.display = 'none';
36439     },
36440
36441     /**
36442      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36443      * by an implementing function.
36444      * @method
36445      * @param {EventObject} e
36446      */
36447     onTriggerClick : Roo.emptyFn
36448 });
36449
36450 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36451 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36452 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36453 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36454     initComponent : function(){
36455         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36456
36457         this.triggerConfig = {
36458             tag:'span', cls:'x-form-twin-triggers', cn:[
36459             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36460             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36461         ]};
36462     },
36463
36464     getTrigger : function(index){
36465         return this.triggers[index];
36466     },
36467
36468     initTrigger : function(){
36469         var ts = this.trigger.select('.x-form-trigger', true);
36470         this.wrap.setStyle('overflow', 'hidden');
36471         var triggerField = this;
36472         ts.each(function(t, all, index){
36473             t.hide = function(){
36474                 var w = triggerField.wrap.getWidth();
36475                 this.dom.style.display = 'none';
36476                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36477             };
36478             t.show = function(){
36479                 var w = triggerField.wrap.getWidth();
36480                 this.dom.style.display = '';
36481                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36482             };
36483             var triggerIndex = 'Trigger'+(index+1);
36484
36485             if(this['hide'+triggerIndex]){
36486                 t.dom.style.display = 'none';
36487             }
36488             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36489             t.addClassOnOver('x-form-trigger-over');
36490             t.addClassOnClick('x-form-trigger-click');
36491         }, this);
36492         this.triggers = ts.elements;
36493     },
36494
36495     onTrigger1Click : Roo.emptyFn,
36496     onTrigger2Click : Roo.emptyFn
36497 });/*
36498  * Based on:
36499  * Ext JS Library 1.1.1
36500  * Copyright(c) 2006-2007, Ext JS, LLC.
36501  *
36502  * Originally Released Under LGPL - original licence link has changed is not relivant.
36503  *
36504  * Fork - LGPL
36505  * <script type="text/javascript">
36506  */
36507  
36508 /**
36509  * @class Roo.form.TextArea
36510  * @extends Roo.form.TextField
36511  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36512  * support for auto-sizing.
36513  * @constructor
36514  * Creates a new TextArea
36515  * @param {Object} config Configuration options
36516  */
36517 Roo.form.TextArea = function(config){
36518     Roo.form.TextArea.superclass.constructor.call(this, config);
36519     // these are provided exchanges for backwards compat
36520     // minHeight/maxHeight were replaced by growMin/growMax to be
36521     // compatible with TextField growing config values
36522     if(this.minHeight !== undefined){
36523         this.growMin = this.minHeight;
36524     }
36525     if(this.maxHeight !== undefined){
36526         this.growMax = this.maxHeight;
36527     }
36528 };
36529
36530 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36531     /**
36532      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36533      */
36534     growMin : 60,
36535     /**
36536      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36537      */
36538     growMax: 1000,
36539     /**
36540      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36541      * in the field (equivalent to setting overflow: hidden, defaults to false)
36542      */
36543     preventScrollbars: false,
36544     /**
36545      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36546      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36547      */
36548
36549     // private
36550     onRender : function(ct, position){
36551         if(!this.el){
36552             this.defaultAutoCreate = {
36553                 tag: "textarea",
36554                 style:"width:300px;height:60px;",
36555                 autocomplete: "off"
36556             };
36557         }
36558         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36559         if(this.grow){
36560             this.textSizeEl = Roo.DomHelper.append(document.body, {
36561                 tag: "pre", cls: "x-form-grow-sizer"
36562             });
36563             if(this.preventScrollbars){
36564                 this.el.setStyle("overflow", "hidden");
36565             }
36566             this.el.setHeight(this.growMin);
36567         }
36568     },
36569
36570     onDestroy : function(){
36571         if(this.textSizeEl){
36572             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36573         }
36574         Roo.form.TextArea.superclass.onDestroy.call(this);
36575     },
36576
36577     // private
36578     onKeyUp : function(e){
36579         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36580             this.autoSize();
36581         }
36582     },
36583
36584     /**
36585      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36586      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36587      */
36588     autoSize : function(){
36589         if(!this.grow || !this.textSizeEl){
36590             return;
36591         }
36592         var el = this.el;
36593         var v = el.dom.value;
36594         var ts = this.textSizeEl;
36595
36596         ts.innerHTML = '';
36597         ts.appendChild(document.createTextNode(v));
36598         v = ts.innerHTML;
36599
36600         Roo.fly(ts).setWidth(this.el.getWidth());
36601         if(v.length < 1){
36602             v = "&#160;&#160;";
36603         }else{
36604             if(Roo.isIE){
36605                 v = v.replace(/\n/g, '<p>&#160;</p>');
36606             }
36607             v += "&#160;\n&#160;";
36608         }
36609         ts.innerHTML = v;
36610         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36611         if(h != this.lastHeight){
36612             this.lastHeight = h;
36613             this.el.setHeight(h);
36614             this.fireEvent("autosize", this, h);
36615         }
36616     }
36617 });/*
36618  * Based on:
36619  * Ext JS Library 1.1.1
36620  * Copyright(c) 2006-2007, Ext JS, LLC.
36621  *
36622  * Originally Released Under LGPL - original licence link has changed is not relivant.
36623  *
36624  * Fork - LGPL
36625  * <script type="text/javascript">
36626  */
36627  
36628
36629 /**
36630  * @class Roo.form.NumberField
36631  * @extends Roo.form.TextField
36632  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36633  * @constructor
36634  * Creates a new NumberField
36635  * @param {Object} config Configuration options
36636  */
36637 Roo.form.NumberField = function(config){
36638     Roo.form.NumberField.superclass.constructor.call(this, config);
36639 };
36640
36641 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36642     /**
36643      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36644      */
36645     fieldClass: "x-form-field x-form-num-field",
36646     /**
36647      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36648      */
36649     allowDecimals : true,
36650     /**
36651      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36652      */
36653     decimalSeparator : ".",
36654     /**
36655      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36656      */
36657     decimalPrecision : 2,
36658     /**
36659      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36660      */
36661     allowNegative : true,
36662     /**
36663      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36664      */
36665     minValue : Number.NEGATIVE_INFINITY,
36666     /**
36667      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36668      */
36669     maxValue : Number.MAX_VALUE,
36670     /**
36671      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36672      */
36673     minText : "The minimum value for this field is {0}",
36674     /**
36675      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36676      */
36677     maxText : "The maximum value for this field is {0}",
36678     /**
36679      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36680      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36681      */
36682     nanText : "{0} is not a valid number",
36683
36684     // private
36685     initEvents : function(){
36686         Roo.form.NumberField.superclass.initEvents.call(this);
36687         var allowed = "0123456789";
36688         if(this.allowDecimals){
36689             allowed += this.decimalSeparator;
36690         }
36691         if(this.allowNegative){
36692             allowed += "-";
36693         }
36694         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36695         var keyPress = function(e){
36696             var k = e.getKey();
36697             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36698                 return;
36699             }
36700             var c = e.getCharCode();
36701             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36702                 e.stopEvent();
36703             }
36704         };
36705         this.el.on("keypress", keyPress, this);
36706     },
36707
36708     // private
36709     validateValue : function(value){
36710         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36711             return false;
36712         }
36713         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36714              return true;
36715         }
36716         var num = this.parseValue(value);
36717         if(isNaN(num)){
36718             this.markInvalid(String.format(this.nanText, value));
36719             return false;
36720         }
36721         if(num < this.minValue){
36722             this.markInvalid(String.format(this.minText, this.minValue));
36723             return false;
36724         }
36725         if(num > this.maxValue){
36726             this.markInvalid(String.format(this.maxText, this.maxValue));
36727             return false;
36728         }
36729         return true;
36730     },
36731
36732     getValue : function(){
36733         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36734     },
36735
36736     // private
36737     parseValue : function(value){
36738         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36739         return isNaN(value) ? '' : value;
36740     },
36741
36742     // private
36743     fixPrecision : function(value){
36744         var nan = isNaN(value);
36745         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36746             return nan ? '' : value;
36747         }
36748         return parseFloat(value).toFixed(this.decimalPrecision);
36749     },
36750
36751     setValue : function(v){
36752         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36753     },
36754
36755     // private
36756     decimalPrecisionFcn : function(v){
36757         return Math.floor(v);
36758     },
36759
36760     beforeBlur : function(){
36761         var v = this.parseValue(this.getRawValue());
36762         if(v){
36763             this.setValue(this.fixPrecision(v));
36764         }
36765     }
36766 });/*
36767  * Based on:
36768  * Ext JS Library 1.1.1
36769  * Copyright(c) 2006-2007, Ext JS, LLC.
36770  *
36771  * Originally Released Under LGPL - original licence link has changed is not relivant.
36772  *
36773  * Fork - LGPL
36774  * <script type="text/javascript">
36775  */
36776  
36777 /**
36778  * @class Roo.form.DateField
36779  * @extends Roo.form.TriggerField
36780  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36781 * @constructor
36782 * Create a new DateField
36783 * @param {Object} config
36784  */
36785 Roo.form.DateField = function(config){
36786     Roo.form.DateField.superclass.constructor.call(this, config);
36787     
36788       this.addEvents({
36789          
36790         /**
36791          * @event select
36792          * Fires when a date is selected
36793              * @param {Roo.form.DateField} combo This combo box
36794              * @param {Date} date The date selected
36795              */
36796         'select' : true
36797          
36798     });
36799     
36800     
36801     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36802     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36803     this.ddMatch = null;
36804     if(this.disabledDates){
36805         var dd = this.disabledDates;
36806         var re = "(?:";
36807         for(var i = 0; i < dd.length; i++){
36808             re += dd[i];
36809             if(i != dd.length-1) re += "|";
36810         }
36811         this.ddMatch = new RegExp(re + ")");
36812     }
36813 };
36814
36815 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36816     /**
36817      * @cfg {String} format
36818      * The default date format string which can be overriden for localization support.  The format must be
36819      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36820      */
36821     format : "m/d/y",
36822     /**
36823      * @cfg {String} altFormats
36824      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36825      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36826      */
36827     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36828     /**
36829      * @cfg {Array} disabledDays
36830      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36831      */
36832     disabledDays : null,
36833     /**
36834      * @cfg {String} disabledDaysText
36835      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36836      */
36837     disabledDaysText : "Disabled",
36838     /**
36839      * @cfg {Array} disabledDates
36840      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36841      * expression so they are very powerful. Some examples:
36842      * <ul>
36843      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36844      * <li>["03/08", "09/16"] would disable those days for every year</li>
36845      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36846      * <li>["03/../2006"] would disable every day in March 2006</li>
36847      * <li>["^03"] would disable every day in every March</li>
36848      * </ul>
36849      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36850      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36851      */
36852     disabledDates : null,
36853     /**
36854      * @cfg {String} disabledDatesText
36855      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36856      */
36857     disabledDatesText : "Disabled",
36858     /**
36859      * @cfg {Date/String} minValue
36860      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36861      * valid format (defaults to null).
36862      */
36863     minValue : null,
36864     /**
36865      * @cfg {Date/String} maxValue
36866      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36867      * valid format (defaults to null).
36868      */
36869     maxValue : null,
36870     /**
36871      * @cfg {String} minText
36872      * The error text to display when the date in the cell is before minValue (defaults to
36873      * 'The date in this field must be after {minValue}').
36874      */
36875     minText : "The date in this field must be equal to or after {0}",
36876     /**
36877      * @cfg {String} maxText
36878      * The error text to display when the date in the cell is after maxValue (defaults to
36879      * 'The date in this field must be before {maxValue}').
36880      */
36881     maxText : "The date in this field must be equal to or before {0}",
36882     /**
36883      * @cfg {String} invalidText
36884      * The error text to display when the date in the field is invalid (defaults to
36885      * '{value} is not a valid date - it must be in the format {format}').
36886      */
36887     invalidText : "{0} is not a valid date - it must be in the format {1}",
36888     /**
36889      * @cfg {String} triggerClass
36890      * An additional CSS class used to style the trigger button.  The trigger will always get the
36891      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36892      * which displays a calendar icon).
36893      */
36894     triggerClass : 'x-form-date-trigger',
36895     
36896
36897     /**
36898      * @cfg {bool} useIso
36899      * if enabled, then the date field will use a hidden field to store the 
36900      * real value as iso formated date. default (false)
36901      */ 
36902     useIso : false,
36903     /**
36904      * @cfg {String/Object} autoCreate
36905      * A DomHelper element spec, or true for a default element spec (defaults to
36906      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36907      */ 
36908     // private
36909     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36910     
36911     // private
36912     hiddenField: false,
36913     
36914     onRender : function(ct, position)
36915     {
36916         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36917         if (this.useIso) {
36918             this.el.dom.removeAttribute('name'); 
36919             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36920                     'before', true);
36921             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36922             // prevent input submission
36923             this.hiddenName = this.name;
36924         }
36925             
36926             
36927     },
36928     
36929     // private
36930     validateValue : function(value)
36931     {
36932         value = this.formatDate(value);
36933         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36934             return false;
36935         }
36936         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36937              return true;
36938         }
36939         var svalue = value;
36940         value = this.parseDate(value);
36941         if(!value){
36942             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36943             return false;
36944         }
36945         var time = value.getTime();
36946         if(this.minValue && time < this.minValue.getTime()){
36947             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36948             return false;
36949         }
36950         if(this.maxValue && time > this.maxValue.getTime()){
36951             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36952             return false;
36953         }
36954         if(this.disabledDays){
36955             var day = value.getDay();
36956             for(var i = 0; i < this.disabledDays.length; i++) {
36957                 if(day === this.disabledDays[i]){
36958                     this.markInvalid(this.disabledDaysText);
36959                     return false;
36960                 }
36961             }
36962         }
36963         var fvalue = this.formatDate(value);
36964         if(this.ddMatch && this.ddMatch.test(fvalue)){
36965             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36966             return false;
36967         }
36968         return true;
36969     },
36970
36971     // private
36972     // Provides logic to override the default TriggerField.validateBlur which just returns true
36973     validateBlur : function(){
36974         return !this.menu || !this.menu.isVisible();
36975     },
36976
36977     /**
36978      * Returns the current date value of the date field.
36979      * @return {Date} The date value
36980      */
36981     getValue : function(){
36982         
36983         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36984     },
36985
36986     /**
36987      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36988      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36989      * (the default format used is "m/d/y").
36990      * <br />Usage:
36991      * <pre><code>
36992 //All of these calls set the same date value (May 4, 2006)
36993
36994 //Pass a date object:
36995 var dt = new Date('5/4/06');
36996 dateField.setValue(dt);
36997
36998 //Pass a date string (default format):
36999 dateField.setValue('5/4/06');
37000
37001 //Pass a date string (custom format):
37002 dateField.format = 'Y-m-d';
37003 dateField.setValue('2006-5-4');
37004 </code></pre>
37005      * @param {String/Date} date The date or valid date string
37006      */
37007     setValue : function(date){
37008         if (this.hiddenField) {
37009             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37010         }
37011         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37012     },
37013
37014     // private
37015     parseDate : function(value){
37016         if(!value || value instanceof Date){
37017             return value;
37018         }
37019         var v = Date.parseDate(value, this.format);
37020         if(!v && this.altFormats){
37021             if(!this.altFormatsArray){
37022                 this.altFormatsArray = this.altFormats.split("|");
37023             }
37024             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37025                 v = Date.parseDate(value, this.altFormatsArray[i]);
37026             }
37027         }
37028         return v;
37029     },
37030
37031     // private
37032     formatDate : function(date, fmt){
37033         return (!date || !(date instanceof Date)) ?
37034                date : date.dateFormat(fmt || this.format);
37035     },
37036
37037     // private
37038     menuListeners : {
37039         select: function(m, d){
37040             this.setValue(d);
37041             this.fireEvent('select', this, d);
37042         },
37043         show : function(){ // retain focus styling
37044             this.onFocus();
37045         },
37046         hide : function(){
37047             this.focus.defer(10, this);
37048             var ml = this.menuListeners;
37049             this.menu.un("select", ml.select,  this);
37050             this.menu.un("show", ml.show,  this);
37051             this.menu.un("hide", ml.hide,  this);
37052         }
37053     },
37054
37055     // private
37056     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37057     onTriggerClick : function(){
37058         if(this.disabled){
37059             return;
37060         }
37061         if(this.menu == null){
37062             this.menu = new Roo.menu.DateMenu();
37063         }
37064         Roo.apply(this.menu.picker,  {
37065             showClear: this.allowBlank,
37066             minDate : this.minValue,
37067             maxDate : this.maxValue,
37068             disabledDatesRE : this.ddMatch,
37069             disabledDatesText : this.disabledDatesText,
37070             disabledDays : this.disabledDays,
37071             disabledDaysText : this.disabledDaysText,
37072             format : this.format,
37073             minText : String.format(this.minText, this.formatDate(this.minValue)),
37074             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37075         });
37076         this.menu.on(Roo.apply({}, this.menuListeners, {
37077             scope:this
37078         }));
37079         this.menu.picker.setValue(this.getValue() || new Date());
37080         this.menu.show(this.el, "tl-bl?");
37081     },
37082
37083     beforeBlur : function(){
37084         var v = this.parseDate(this.getRawValue());
37085         if(v){
37086             this.setValue(v);
37087         }
37088     }
37089
37090     /** @cfg {Boolean} grow @hide */
37091     /** @cfg {Number} growMin @hide */
37092     /** @cfg {Number} growMax @hide */
37093     /**
37094      * @hide
37095      * @method autoSize
37096      */
37097 });/*
37098  * Based on:
37099  * Ext JS Library 1.1.1
37100  * Copyright(c) 2006-2007, Ext JS, LLC.
37101  *
37102  * Originally Released Under LGPL - original licence link has changed is not relivant.
37103  *
37104  * Fork - LGPL
37105  * <script type="text/javascript">
37106  */
37107  
37108
37109 /**
37110  * @class Roo.form.ComboBox
37111  * @extends Roo.form.TriggerField
37112  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37113  * @constructor
37114  * Create a new ComboBox.
37115  * @param {Object} config Configuration options
37116  */
37117 Roo.form.ComboBox = function(config){
37118     Roo.form.ComboBox.superclass.constructor.call(this, config);
37119     this.addEvents({
37120         /**
37121          * @event expand
37122          * Fires when the dropdown list is expanded
37123              * @param {Roo.form.ComboBox} combo This combo box
37124              */
37125         'expand' : true,
37126         /**
37127          * @event collapse
37128          * Fires when the dropdown list is collapsed
37129              * @param {Roo.form.ComboBox} combo This combo box
37130              */
37131         'collapse' : true,
37132         /**
37133          * @event beforeselect
37134          * Fires before a list item is selected. Return false to cancel the selection.
37135              * @param {Roo.form.ComboBox} combo This combo box
37136              * @param {Roo.data.Record} record The data record returned from the underlying store
37137              * @param {Number} index The index of the selected item in the dropdown list
37138              */
37139         'beforeselect' : true,
37140         /**
37141          * @event select
37142          * Fires when a list item is selected
37143              * @param {Roo.form.ComboBox} combo This combo box
37144              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37145              * @param {Number} index The index of the selected item in the dropdown list
37146              */
37147         'select' : true,
37148         /**
37149          * @event beforequery
37150          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37151          * The event object passed has these properties:
37152              * @param {Roo.form.ComboBox} combo This combo box
37153              * @param {String} query The query
37154              * @param {Boolean} forceAll true to force "all" query
37155              * @param {Boolean} cancel true to cancel the query
37156              * @param {Object} e The query event object
37157              */
37158         'beforequery': true,
37159          /**
37160          * @event add
37161          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37162              * @param {Roo.form.ComboBox} combo This combo box
37163              */
37164         'add' : true,
37165         /**
37166          * @event edit
37167          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37168              * @param {Roo.form.ComboBox} combo This combo box
37169              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37170              */
37171         'edit' : true
37172         
37173         
37174     });
37175     if(this.transform){
37176         this.allowDomMove = false;
37177         var s = Roo.getDom(this.transform);
37178         if(!this.hiddenName){
37179             this.hiddenName = s.name;
37180         }
37181         if(!this.store){
37182             this.mode = 'local';
37183             var d = [], opts = s.options;
37184             for(var i = 0, len = opts.length;i < len; i++){
37185                 var o = opts[i];
37186                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37187                 if(o.selected) {
37188                     this.value = value;
37189                 }
37190                 d.push([value, o.text]);
37191             }
37192             this.store = new Roo.data.SimpleStore({
37193                 'id': 0,
37194                 fields: ['value', 'text'],
37195                 data : d
37196             });
37197             this.valueField = 'value';
37198             this.displayField = 'text';
37199         }
37200         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37201         if(!this.lazyRender){
37202             this.target = true;
37203             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37204             s.parentNode.removeChild(s); // remove it
37205             this.render(this.el.parentNode);
37206         }else{
37207             s.parentNode.removeChild(s); // remove it
37208         }
37209
37210     }
37211     if (this.store) {
37212         this.store = Roo.factory(this.store, Roo.data);
37213     }
37214     
37215     this.selectedIndex = -1;
37216     if(this.mode == 'local'){
37217         if(config.queryDelay === undefined){
37218             this.queryDelay = 10;
37219         }
37220         if(config.minChars === undefined){
37221             this.minChars = 0;
37222         }
37223     }
37224 };
37225
37226 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37227     /**
37228      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37229      */
37230     /**
37231      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37232      * rendering into an Roo.Editor, defaults to false)
37233      */
37234     /**
37235      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37236      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37237      */
37238     /**
37239      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37240      */
37241     /**
37242      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37243      * the dropdown list (defaults to undefined, with no header element)
37244      */
37245
37246      /**
37247      * @cfg {String/Roo.Template} tpl The template to use to render the output
37248      */
37249      
37250     // private
37251     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37252     /**
37253      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37254      */
37255     listWidth: undefined,
37256     /**
37257      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37258      * mode = 'remote' or 'text' if mode = 'local')
37259      */
37260     displayField: undefined,
37261     /**
37262      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37263      * mode = 'remote' or 'value' if mode = 'local'). 
37264      * Note: use of a valueField requires the user make a selection
37265      * in order for a value to be mapped.
37266      */
37267     valueField: undefined,
37268     /**
37269      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37270      * field's data value (defaults to the underlying DOM element's name)
37271      */
37272     hiddenName: undefined,
37273     /**
37274      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37275      */
37276     listClass: '',
37277     /**
37278      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37279      */
37280     selectedClass: 'x-combo-selected',
37281     /**
37282      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37283      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37284      * which displays a downward arrow icon).
37285      */
37286     triggerClass : 'x-form-arrow-trigger',
37287     /**
37288      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37289      */
37290     shadow:'sides',
37291     /**
37292      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37293      * anchor positions (defaults to 'tl-bl')
37294      */
37295     listAlign: 'tl-bl?',
37296     /**
37297      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37298      */
37299     maxHeight: 300,
37300     /**
37301      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37302      * query specified by the allQuery config option (defaults to 'query')
37303      */
37304     triggerAction: 'query',
37305     /**
37306      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37307      * (defaults to 4, does not apply if editable = false)
37308      */
37309     minChars : 4,
37310     /**
37311      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37312      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37313      */
37314     typeAhead: false,
37315     /**
37316      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37317      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37318      */
37319     queryDelay: 500,
37320     /**
37321      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37322      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37323      */
37324     pageSize: 0,
37325     /**
37326      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37327      * when editable = true (defaults to false)
37328      */
37329     selectOnFocus:false,
37330     /**
37331      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37332      */
37333     queryParam: 'query',
37334     /**
37335      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37336      * when mode = 'remote' (defaults to 'Loading...')
37337      */
37338     loadingText: 'Loading...',
37339     /**
37340      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37341      */
37342     resizable: false,
37343     /**
37344      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37345      */
37346     handleHeight : 8,
37347     /**
37348      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37349      * traditional select (defaults to true)
37350      */
37351     editable: true,
37352     /**
37353      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37354      */
37355     allQuery: '',
37356     /**
37357      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37358      */
37359     mode: 'remote',
37360     /**
37361      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37362      * listWidth has a higher value)
37363      */
37364     minListWidth : 70,
37365     /**
37366      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37367      * allow the user to set arbitrary text into the field (defaults to false)
37368      */
37369     forceSelection:false,
37370     /**
37371      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37372      * if typeAhead = true (defaults to 250)
37373      */
37374     typeAheadDelay : 250,
37375     /**
37376      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37377      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37378      */
37379     valueNotFoundText : undefined,
37380     /**
37381      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37382      */
37383     blockFocus : false,
37384     
37385     /**
37386      * @cfg {Boolean} disableClear Disable showing of clear button.
37387      */
37388     disableClear : false,
37389     /**
37390      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37391      */
37392     alwaysQuery : false,
37393     
37394     //private
37395     addicon : false,
37396     editicon: false,
37397     
37398     
37399     // private
37400     onRender : function(ct, position){
37401         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37402         if(this.hiddenName){
37403             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37404                     'before', true);
37405             this.hiddenField.value =
37406                 this.hiddenValue !== undefined ? this.hiddenValue :
37407                 this.value !== undefined ? this.value : '';
37408
37409             // prevent input submission
37410             this.el.dom.removeAttribute('name');
37411         }
37412         if(Roo.isGecko){
37413             this.el.dom.setAttribute('autocomplete', 'off');
37414         }
37415
37416         var cls = 'x-combo-list';
37417
37418         this.list = new Roo.Layer({
37419             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37420         });
37421
37422         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37423         this.list.setWidth(lw);
37424         this.list.swallowEvent('mousewheel');
37425         this.assetHeight = 0;
37426
37427         if(this.title){
37428             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37429             this.assetHeight += this.header.getHeight();
37430         }
37431
37432         this.innerList = this.list.createChild({cls:cls+'-inner'});
37433         this.innerList.on('mouseover', this.onViewOver, this);
37434         this.innerList.on('mousemove', this.onViewMove, this);
37435         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37436         
37437         if(this.allowBlank && !this.pageSize && !this.disableClear){
37438             this.footer = this.list.createChild({cls:cls+'-ft'});
37439             this.pageTb = new Roo.Toolbar(this.footer);
37440            
37441         }
37442         if(this.pageSize){
37443             this.footer = this.list.createChild({cls:cls+'-ft'});
37444             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37445                     {pageSize: this.pageSize});
37446             
37447         }
37448         
37449         if (this.pageTb && this.allowBlank && !this.disableClear) {
37450             var _this = this;
37451             this.pageTb.add(new Roo.Toolbar.Fill(), {
37452                 cls: 'x-btn-icon x-btn-clear',
37453                 text: '&#160;',
37454                 handler: function()
37455                 {
37456                     _this.collapse();
37457                     _this.clearValue();
37458                     _this.onSelect(false, -1);
37459                 }
37460             });
37461         }
37462         if (this.footer) {
37463             this.assetHeight += this.footer.getHeight();
37464         }
37465         
37466
37467         if(!this.tpl){
37468             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37469         }
37470
37471         this.view = new Roo.View(this.innerList, this.tpl, {
37472             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37473         });
37474
37475         this.view.on('click', this.onViewClick, this);
37476
37477         this.store.on('beforeload', this.onBeforeLoad, this);
37478         this.store.on('load', this.onLoad, this);
37479         this.store.on('loadexception', this.collapse, this);
37480
37481         if(this.resizable){
37482             this.resizer = new Roo.Resizable(this.list,  {
37483                pinned:true, handles:'se'
37484             });
37485             this.resizer.on('resize', function(r, w, h){
37486                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37487                 this.listWidth = w;
37488                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37489                 this.restrictHeight();
37490             }, this);
37491             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37492         }
37493         if(!this.editable){
37494             this.editable = true;
37495             this.setEditable(false);
37496         }  
37497         
37498         
37499         if (typeof(this.events.add.listeners) != 'undefined') {
37500             
37501             this.addicon = this.wrap.createChild(
37502                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37503        
37504             this.addicon.on('click', function(e) {
37505                 this.fireEvent('add', this);
37506             }, this);
37507         }
37508         if (typeof(this.events.edit.listeners) != 'undefined') {
37509             
37510             this.editicon = this.wrap.createChild(
37511                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37512             if (this.addicon) {
37513                 this.editicon.setStyle('margin-left', '40px');
37514             }
37515             this.editicon.on('click', function(e) {
37516                 
37517                 // we fire even  if inothing is selected..
37518                 this.fireEvent('edit', this, this.lastData );
37519                 
37520             }, this);
37521         }
37522         
37523         
37524         
37525     },
37526
37527     // private
37528     initEvents : function(){
37529         Roo.form.ComboBox.superclass.initEvents.call(this);
37530
37531         this.keyNav = new Roo.KeyNav(this.el, {
37532             "up" : function(e){
37533                 this.inKeyMode = true;
37534                 this.selectPrev();
37535             },
37536
37537             "down" : function(e){
37538                 if(!this.isExpanded()){
37539                     this.onTriggerClick();
37540                 }else{
37541                     this.inKeyMode = true;
37542                     this.selectNext();
37543                 }
37544             },
37545
37546             "enter" : function(e){
37547                 this.onViewClick();
37548                 //return true;
37549             },
37550
37551             "esc" : function(e){
37552                 this.collapse();
37553             },
37554
37555             "tab" : function(e){
37556                 this.onViewClick(false);
37557                 return true;
37558             },
37559
37560             scope : this,
37561
37562             doRelay : function(foo, bar, hname){
37563                 if(hname == 'down' || this.scope.isExpanded()){
37564                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37565                 }
37566                 return true;
37567             },
37568
37569             forceKeyDown: true
37570         });
37571         this.queryDelay = Math.max(this.queryDelay || 10,
37572                 this.mode == 'local' ? 10 : 250);
37573         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37574         if(this.typeAhead){
37575             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37576         }
37577         if(this.editable !== false){
37578             this.el.on("keyup", this.onKeyUp, this);
37579         }
37580         if(this.forceSelection){
37581             this.on('blur', this.doForce, this);
37582         }
37583     },
37584
37585     onDestroy : function(){
37586         if(this.view){
37587             this.view.setStore(null);
37588             this.view.el.removeAllListeners();
37589             this.view.el.remove();
37590             this.view.purgeListeners();
37591         }
37592         if(this.list){
37593             this.list.destroy();
37594         }
37595         if(this.store){
37596             this.store.un('beforeload', this.onBeforeLoad, this);
37597             this.store.un('load', this.onLoad, this);
37598             this.store.un('loadexception', this.collapse, this);
37599         }
37600         Roo.form.ComboBox.superclass.onDestroy.call(this);
37601     },
37602
37603     // private
37604     fireKey : function(e){
37605         if(e.isNavKeyPress() && !this.list.isVisible()){
37606             this.fireEvent("specialkey", this, e);
37607         }
37608     },
37609
37610     // private
37611     onResize: function(w, h){
37612         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37613         
37614         if(typeof w != 'number'){
37615             // we do not handle it!?!?
37616             return;
37617         }
37618         var tw = this.trigger.getWidth();
37619         tw += this.addicon ? this.addicon.getWidth() : 0;
37620         tw += this.editicon ? this.editicon.getWidth() : 0;
37621         var x = w - tw;
37622         this.el.setWidth( this.adjustWidth('input', x));
37623             
37624         this.trigger.setStyle('left', x+'px');
37625         
37626         if(this.list && this.listWidth === undefined){
37627             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37628             this.list.setWidth(lw);
37629             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37630         }
37631         
37632     
37633         
37634     },
37635
37636     /**
37637      * Allow or prevent the user from directly editing the field text.  If false is passed,
37638      * the user will only be able to select from the items defined in the dropdown list.  This method
37639      * is the runtime equivalent of setting the 'editable' config option at config time.
37640      * @param {Boolean} value True to allow the user to directly edit the field text
37641      */
37642     setEditable : function(value){
37643         if(value == this.editable){
37644             return;
37645         }
37646         this.editable = value;
37647         if(!value){
37648             this.el.dom.setAttribute('readOnly', true);
37649             this.el.on('mousedown', this.onTriggerClick,  this);
37650             this.el.addClass('x-combo-noedit');
37651         }else{
37652             this.el.dom.setAttribute('readOnly', false);
37653             this.el.un('mousedown', this.onTriggerClick,  this);
37654             this.el.removeClass('x-combo-noedit');
37655         }
37656     },
37657
37658     // private
37659     onBeforeLoad : function(){
37660         if(!this.hasFocus){
37661             return;
37662         }
37663         this.innerList.update(this.loadingText ?
37664                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37665         this.restrictHeight();
37666         this.selectedIndex = -1;
37667     },
37668
37669     // private
37670     onLoad : function(){
37671         if(!this.hasFocus){
37672             return;
37673         }
37674         if(this.store.getCount() > 0){
37675             this.expand();
37676             this.restrictHeight();
37677             if(this.lastQuery == this.allQuery){
37678                 if(this.editable){
37679                     this.el.dom.select();
37680                 }
37681                 if(!this.selectByValue(this.value, true)){
37682                     this.select(0, true);
37683                 }
37684             }else{
37685                 this.selectNext();
37686                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37687                     this.taTask.delay(this.typeAheadDelay);
37688                 }
37689             }
37690         }else{
37691             this.onEmptyResults();
37692         }
37693         //this.el.focus();
37694     },
37695
37696     // private
37697     onTypeAhead : function(){
37698         if(this.store.getCount() > 0){
37699             var r = this.store.getAt(0);
37700             var newValue = r.data[this.displayField];
37701             var len = newValue.length;
37702             var selStart = this.getRawValue().length;
37703             if(selStart != len){
37704                 this.setRawValue(newValue);
37705                 this.selectText(selStart, newValue.length);
37706             }
37707         }
37708     },
37709
37710     // private
37711     onSelect : function(record, index){
37712         if(this.fireEvent('beforeselect', this, record, index) !== false){
37713             this.setFromData(index > -1 ? record.data : false);
37714             this.collapse();
37715             this.fireEvent('select', this, record, index);
37716         }
37717     },
37718
37719     /**
37720      * Returns the currently selected field value or empty string if no value is set.
37721      * @return {String} value The selected value
37722      */
37723     getValue : function(){
37724         if(this.valueField){
37725             return typeof this.value != 'undefined' ? this.value : '';
37726         }else{
37727             return Roo.form.ComboBox.superclass.getValue.call(this);
37728         }
37729     },
37730
37731     /**
37732      * Clears any text/value currently set in the field
37733      */
37734     clearValue : function(){
37735         if(this.hiddenField){
37736             this.hiddenField.value = '';
37737         }
37738         this.value = '';
37739         this.setRawValue('');
37740         this.lastSelectionText = '';
37741         this.applyEmptyText();
37742     },
37743
37744     /**
37745      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37746      * will be displayed in the field.  If the value does not match the data value of an existing item,
37747      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37748      * Otherwise the field will be blank (although the value will still be set).
37749      * @param {String} value The value to match
37750      */
37751     setValue : function(v){
37752         var text = v;
37753         if(this.valueField){
37754             var r = this.findRecord(this.valueField, v);
37755             if(r){
37756                 text = r.data[this.displayField];
37757             }else if(this.valueNotFoundText !== undefined){
37758                 text = this.valueNotFoundText;
37759             }
37760         }
37761         this.lastSelectionText = text;
37762         if(this.hiddenField){
37763             this.hiddenField.value = v;
37764         }
37765         Roo.form.ComboBox.superclass.setValue.call(this, text);
37766         this.value = v;
37767     },
37768     /**
37769      * @property {Object} the last set data for the element
37770      */
37771     
37772     lastData : false,
37773     /**
37774      * Sets the value of the field based on a object which is related to the record format for the store.
37775      * @param {Object} value the value to set as. or false on reset?
37776      */
37777     setFromData : function(o){
37778         var dv = ''; // display value
37779         var vv = ''; // value value..
37780         this.lastData = o;
37781         if (this.displayField) {
37782             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37783         } else {
37784             // this is an error condition!!!
37785             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37786         }
37787         
37788         if(this.valueField){
37789             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37790         }
37791         if(this.hiddenField){
37792             this.hiddenField.value = vv;
37793             
37794             this.lastSelectionText = dv;
37795             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37796             this.value = vv;
37797             return;
37798         }
37799         // no hidden field.. - we store the value in 'value', but still display
37800         // display field!!!!
37801         this.lastSelectionText = dv;
37802         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37803         this.value = vv;
37804         
37805         
37806     },
37807     // private
37808     reset : function(){
37809         // overridden so that last data is reset..
37810         this.setValue(this.originalValue);
37811         this.clearInvalid();
37812         this.lastData = false;
37813     },
37814     // private
37815     findRecord : function(prop, value){
37816         var record;
37817         if(this.store.getCount() > 0){
37818             this.store.each(function(r){
37819                 if(r.data[prop] == value){
37820                     record = r;
37821                     return false;
37822                 }
37823             });
37824         }
37825         return record;
37826     },
37827
37828     // private
37829     onViewMove : function(e, t){
37830         this.inKeyMode = false;
37831     },
37832
37833     // private
37834     onViewOver : function(e, t){
37835         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37836             return;
37837         }
37838         var item = this.view.findItemFromChild(t);
37839         if(item){
37840             var index = this.view.indexOf(item);
37841             this.select(index, false);
37842         }
37843     },
37844
37845     // private
37846     onViewClick : function(doFocus){
37847         var index = this.view.getSelectedIndexes()[0];
37848         var r = this.store.getAt(index);
37849         if(r){
37850             this.onSelect(r, index);
37851         }
37852         if(doFocus !== false && !this.blockFocus){
37853             this.el.focus();
37854         }
37855     },
37856
37857     // private
37858     restrictHeight : function(){
37859         this.innerList.dom.style.height = '';
37860         var inner = this.innerList.dom;
37861         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37862         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37863         this.list.beginUpdate();
37864         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37865         this.list.alignTo(this.el, this.listAlign);
37866         this.list.endUpdate();
37867     },
37868
37869     // private
37870     onEmptyResults : function(){
37871         this.collapse();
37872     },
37873
37874     /**
37875      * Returns true if the dropdown list is expanded, else false.
37876      */
37877     isExpanded : function(){
37878         return this.list.isVisible();
37879     },
37880
37881     /**
37882      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37883      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37884      * @param {String} value The data value of the item to select
37885      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37886      * selected item if it is not currently in view (defaults to true)
37887      * @return {Boolean} True if the value matched an item in the list, else false
37888      */
37889     selectByValue : function(v, scrollIntoView){
37890         if(v !== undefined && v !== null){
37891             var r = this.findRecord(this.valueField || this.displayField, v);
37892             if(r){
37893                 this.select(this.store.indexOf(r), scrollIntoView);
37894                 return true;
37895             }
37896         }
37897         return false;
37898     },
37899
37900     /**
37901      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37902      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37903      * @param {Number} index The zero-based index of the list item to select
37904      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37905      * selected item if it is not currently in view (defaults to true)
37906      */
37907     select : function(index, scrollIntoView){
37908         this.selectedIndex = index;
37909         this.view.select(index);
37910         if(scrollIntoView !== false){
37911             var el = this.view.getNode(index);
37912             if(el){
37913                 this.innerList.scrollChildIntoView(el, false);
37914             }
37915         }
37916     },
37917
37918     // private
37919     selectNext : function(){
37920         var ct = this.store.getCount();
37921         if(ct > 0){
37922             if(this.selectedIndex == -1){
37923                 this.select(0);
37924             }else if(this.selectedIndex < ct-1){
37925                 this.select(this.selectedIndex+1);
37926             }
37927         }
37928     },
37929
37930     // private
37931     selectPrev : function(){
37932         var ct = this.store.getCount();
37933         if(ct > 0){
37934             if(this.selectedIndex == -1){
37935                 this.select(0);
37936             }else if(this.selectedIndex != 0){
37937                 this.select(this.selectedIndex-1);
37938             }
37939         }
37940     },
37941
37942     // private
37943     onKeyUp : function(e){
37944         if(this.editable !== false && !e.isSpecialKey()){
37945             this.lastKey = e.getKey();
37946             this.dqTask.delay(this.queryDelay);
37947         }
37948     },
37949
37950     // private
37951     validateBlur : function(){
37952         return !this.list || !this.list.isVisible();   
37953     },
37954
37955     // private
37956     initQuery : function(){
37957         this.doQuery(this.getRawValue());
37958     },
37959
37960     // private
37961     doForce : function(){
37962         if(this.el.dom.value.length > 0){
37963             this.el.dom.value =
37964                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37965             this.applyEmptyText();
37966         }
37967     },
37968
37969     /**
37970      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37971      * query allowing the query action to be canceled if needed.
37972      * @param {String} query The SQL query to execute
37973      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37974      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37975      * saved in the current store (defaults to false)
37976      */
37977     doQuery : function(q, forceAll){
37978         if(q === undefined || q === null){
37979             q = '';
37980         }
37981         var qe = {
37982             query: q,
37983             forceAll: forceAll,
37984             combo: this,
37985             cancel:false
37986         };
37987         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37988             return false;
37989         }
37990         q = qe.query;
37991         forceAll = qe.forceAll;
37992         if(forceAll === true || (q.length >= this.minChars)){
37993             if(this.lastQuery != q || this.alwaysQuery){
37994                 this.lastQuery = q;
37995                 if(this.mode == 'local'){
37996                     this.selectedIndex = -1;
37997                     if(forceAll){
37998                         this.store.clearFilter();
37999                     }else{
38000                         this.store.filter(this.displayField, q);
38001                     }
38002                     this.onLoad();
38003                 }else{
38004                     this.store.baseParams[this.queryParam] = q;
38005                     this.store.load({
38006                         params: this.getParams(q)
38007                     });
38008                     this.expand();
38009                 }
38010             }else{
38011                 this.selectedIndex = -1;
38012                 this.onLoad();   
38013             }
38014         }
38015     },
38016
38017     // private
38018     getParams : function(q){
38019         var p = {};
38020         //p[this.queryParam] = q;
38021         if(this.pageSize){
38022             p.start = 0;
38023             p.limit = this.pageSize;
38024         }
38025         return p;
38026     },
38027
38028     /**
38029      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38030      */
38031     collapse : function(){
38032         if(!this.isExpanded()){
38033             return;
38034         }
38035         this.list.hide();
38036         Roo.get(document).un('mousedown', this.collapseIf, this);
38037         Roo.get(document).un('mousewheel', this.collapseIf, this);
38038         if (!this.editable) {
38039             Roo.get(document).un('keydown', this.listKeyPress, this);
38040         }
38041         this.fireEvent('collapse', this);
38042     },
38043
38044     // private
38045     collapseIf : function(e){
38046         if(!e.within(this.wrap) && !e.within(this.list)){
38047             this.collapse();
38048         }
38049     },
38050
38051     /**
38052      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38053      */
38054     expand : function(){
38055         if(this.isExpanded() || !this.hasFocus){
38056             return;
38057         }
38058         this.list.alignTo(this.el, this.listAlign);
38059         this.list.show();
38060         Roo.get(document).on('mousedown', this.collapseIf, this);
38061         Roo.get(document).on('mousewheel', this.collapseIf, this);
38062         if (!this.editable) {
38063             Roo.get(document).on('keydown', this.listKeyPress, this);
38064         }
38065         
38066         this.fireEvent('expand', this);
38067     },
38068
38069     // private
38070     // Implements the default empty TriggerField.onTriggerClick function
38071     onTriggerClick : function(){
38072         if(this.disabled){
38073             return;
38074         }
38075         if(this.isExpanded()){
38076             this.collapse();
38077             if (!this.blockFocus) {
38078                 this.el.focus();
38079             }
38080             
38081         }else {
38082             this.hasFocus = true;
38083             if(this.triggerAction == 'all') {
38084                 this.doQuery(this.allQuery, true);
38085             } else {
38086                 this.doQuery(this.getRawValue());
38087             }
38088             if (!this.blockFocus) {
38089                 this.el.focus();
38090             }
38091         }
38092     },
38093     listKeyPress : function(e)
38094     {
38095         //Roo.log('listkeypress');
38096         // scroll to first matching element based on key pres..
38097         if (e.isSpecialKey()) {
38098             return false;
38099         }
38100         var k = String.fromCharCode(e.getKey()).toUpperCase();
38101         //Roo.log(k);
38102         var match  = false;
38103         var csel = this.view.getSelectedNodes();
38104         var cselitem = false;
38105         if (csel.length) {
38106             var ix = this.view.indexOf(csel[0]);
38107             cselitem  = this.store.getAt(ix);
38108             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38109                 cselitem = false;
38110             }
38111             
38112         }
38113         
38114         this.store.each(function(v) { 
38115             if (cselitem) {
38116                 // start at existing selection.
38117                 if (cselitem.id == v.id) {
38118                     cselitem = false;
38119                 }
38120                 return;
38121             }
38122                 
38123             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38124                 match = this.store.indexOf(v);
38125                 return false;
38126             }
38127         }, this);
38128         
38129         if (match === false) {
38130             return true; // no more action?
38131         }
38132         // scroll to?
38133         this.view.select(match);
38134         var sn = Roo.get(this.view.getSelectedNodes()[0])
38135         sn.scrollIntoView(sn.dom.parentNode, false);
38136     }
38137
38138     /** 
38139     * @cfg {Boolean} grow 
38140     * @hide 
38141     */
38142     /** 
38143     * @cfg {Number} growMin 
38144     * @hide 
38145     */
38146     /** 
38147     * @cfg {Number} growMax 
38148     * @hide 
38149     */
38150     /**
38151      * @hide
38152      * @method autoSize
38153      */
38154 });/*
38155  * Based on:
38156  * Ext JS Library 1.1.1
38157  * Copyright(c) 2006-2007, Ext JS, LLC.
38158  *
38159  * Originally Released Under LGPL - original licence link has changed is not relivant.
38160  *
38161  * Fork - LGPL
38162  * <script type="text/javascript">
38163  */
38164 /**
38165  * @class Roo.form.Checkbox
38166  * @extends Roo.form.Field
38167  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38168  * @constructor
38169  * Creates a new Checkbox
38170  * @param {Object} config Configuration options
38171  */
38172 Roo.form.Checkbox = function(config){
38173     Roo.form.Checkbox.superclass.constructor.call(this, config);
38174     this.addEvents({
38175         /**
38176          * @event check
38177          * Fires when the checkbox is checked or unchecked.
38178              * @param {Roo.form.Checkbox} this This checkbox
38179              * @param {Boolean} checked The new checked value
38180              */
38181         check : true
38182     });
38183 };
38184
38185 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38186     /**
38187      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38188      */
38189     focusClass : undefined,
38190     /**
38191      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38192      */
38193     fieldClass: "x-form-field",
38194     /**
38195      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38196      */
38197     checked: false,
38198     /**
38199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38200      * {tag: "input", type: "checkbox", autocomplete: "off"})
38201      */
38202     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38203     /**
38204      * @cfg {String} boxLabel The text that appears beside the checkbox
38205      */
38206     boxLabel : "",
38207     /**
38208      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38209      */  
38210     inputValue : '1',
38211     /**
38212      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38213      */
38214      valueOff: '0', // value when not checked..
38215
38216     actionMode : 'viewEl', 
38217     //
38218     // private
38219     itemCls : 'x-menu-check-item x-form-item',
38220     groupClass : 'x-menu-group-item',
38221     inputType : 'hidden',
38222     
38223     
38224     inSetChecked: false, // check that we are not calling self...
38225     
38226     inputElement: false, // real input element?
38227     basedOn: false, // ????
38228     
38229     isFormField: true, // not sure where this is needed!!!!
38230
38231     onResize : function(){
38232         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38233         if(!this.boxLabel){
38234             this.el.alignTo(this.wrap, 'c-c');
38235         }
38236     },
38237
38238     initEvents : function(){
38239         Roo.form.Checkbox.superclass.initEvents.call(this);
38240         this.el.on("click", this.onClick,  this);
38241         this.el.on("change", this.onClick,  this);
38242     },
38243
38244
38245     getResizeEl : function(){
38246         return this.wrap;
38247     },
38248
38249     getPositionEl : function(){
38250         return this.wrap;
38251     },
38252
38253     // private
38254     onRender : function(ct, position){
38255         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38256         /*
38257         if(this.inputValue !== undefined){
38258             this.el.dom.value = this.inputValue;
38259         }
38260         */
38261         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38262         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38263         var viewEl = this.wrap.createChild({ 
38264             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38265         this.viewEl = viewEl;   
38266         this.wrap.on('click', this.onClick,  this); 
38267         
38268         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38269         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38270         
38271         
38272         
38273         if(this.boxLabel){
38274             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38275         //    viewEl.on('click', this.onClick,  this); 
38276         }
38277         //if(this.checked){
38278             this.setChecked(this.checked);
38279         //}else{
38280             //this.checked = this.el.dom;
38281         //}
38282
38283     },
38284
38285     // private
38286     initValue : Roo.emptyFn,
38287
38288     /**
38289      * Returns the checked state of the checkbox.
38290      * @return {Boolean} True if checked, else false
38291      */
38292     getValue : function(){
38293         if(this.el){
38294             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38295         }
38296         return this.valueOff;
38297         
38298     },
38299
38300         // private
38301     onClick : function(){ 
38302         this.setChecked(!this.checked);
38303
38304         //if(this.el.dom.checked != this.checked){
38305         //    this.setValue(this.el.dom.checked);
38306        // }
38307     },
38308
38309     /**
38310      * Sets the checked state of the checkbox.
38311      * On is always based on a string comparison between inputValue and the param.
38312      * @param {Boolean/String} value - the value to set 
38313      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38314      */
38315     setValue : function(v,suppressEvent){
38316         
38317         
38318         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38319         //if(this.el && this.el.dom){
38320         //    this.el.dom.checked = this.checked;
38321         //    this.el.dom.defaultChecked = this.checked;
38322         //}
38323         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38324         //this.fireEvent("check", this, this.checked);
38325     },
38326     // private..
38327     setChecked : function(state,suppressEvent)
38328     {
38329         if (this.inSetChecked) {
38330             this.checked = state;
38331             return;
38332         }
38333         
38334     
38335         if(this.wrap){
38336             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38337         }
38338         this.checked = state;
38339         if(suppressEvent !== true){
38340             this.fireEvent('check', this, state);
38341         }
38342         this.inSetChecked = true;
38343         this.el.dom.value = state ? this.inputValue : this.valueOff;
38344         this.inSetChecked = false;
38345         
38346     },
38347     // handle setting of hidden value by some other method!!?!?
38348     setFromHidden: function()
38349     {
38350         if(!this.el){
38351             return;
38352         }
38353         //console.log("SET FROM HIDDEN");
38354         //alert('setFrom hidden');
38355         this.setValue(this.el.dom.value);
38356     },
38357     
38358     onDestroy : function()
38359     {
38360         if(this.viewEl){
38361             Roo.get(this.viewEl).remove();
38362         }
38363          
38364         Roo.form.Checkbox.superclass.onDestroy.call(this);
38365     }
38366
38367 });/*
38368  * Based on:
38369  * Ext JS Library 1.1.1
38370  * Copyright(c) 2006-2007, Ext JS, LLC.
38371  *
38372  * Originally Released Under LGPL - original licence link has changed is not relivant.
38373  *
38374  * Fork - LGPL
38375  * <script type="text/javascript">
38376  */
38377  
38378 /**
38379  * @class Roo.form.Radio
38380  * @extends Roo.form.Checkbox
38381  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38382  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38383  * @constructor
38384  * Creates a new Radio
38385  * @param {Object} config Configuration options
38386  */
38387 Roo.form.Radio = function(){
38388     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38389 };
38390 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38391     inputType: 'radio',
38392
38393     /**
38394      * If this radio is part of a group, it will return the selected value
38395      * @return {String}
38396      */
38397     getGroupValue : function(){
38398         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38399     }
38400 });//<script type="text/javascript">
38401
38402 /*
38403  * Ext JS Library 1.1.1
38404  * Copyright(c) 2006-2007, Ext JS, LLC.
38405  * licensing@extjs.com
38406  * 
38407  * http://www.extjs.com/license
38408  */
38409  
38410  /*
38411   * 
38412   * Known bugs:
38413   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38414   * - IE ? - no idea how much works there.
38415   * 
38416   * 
38417   * 
38418   */
38419  
38420
38421 /**
38422  * @class Ext.form.HtmlEditor
38423  * @extends Ext.form.Field
38424  * Provides a lightweight HTML Editor component.
38425  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38426  * 
38427  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38428  * supported by this editor.</b><br/><br/>
38429  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38430  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38431  */
38432 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38433       /**
38434      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38435      */
38436     toolbars : false,
38437     /**
38438      * @cfg {String} createLinkText The default text for the create link prompt
38439      */
38440     createLinkText : 'Please enter the URL for the link:',
38441     /**
38442      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38443      */
38444     defaultLinkValue : 'http:/'+'/',
38445    
38446      /**
38447      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38448      *                        Roo.resizable.
38449      */
38450     resizable : false,
38451      /**
38452      * @cfg {Number} height (in pixels)
38453      */   
38454     height: 300,
38455    /**
38456      * @cfg {Number} width (in pixels)
38457      */   
38458     width: 500,
38459     
38460     /**
38461      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38462      * 
38463      */
38464     stylesheets: false,
38465     
38466     // id of frame..
38467     frameId: false,
38468     
38469     // private properties
38470     validationEvent : false,
38471     deferHeight: true,
38472     initialized : false,
38473     activated : false,
38474     sourceEditMode : false,
38475     onFocus : Roo.emptyFn,
38476     iframePad:3,
38477     hideMode:'offsets',
38478     
38479     defaultAutoCreate : { // modified by initCompnoent..
38480         tag: "textarea",
38481         style:"width:500px;height:300px;",
38482         autocomplete: "off"
38483     },
38484
38485     // private
38486     initComponent : function(){
38487         this.addEvents({
38488             /**
38489              * @event initialize
38490              * Fires when the editor is fully initialized (including the iframe)
38491              * @param {HtmlEditor} this
38492              */
38493             initialize: true,
38494             /**
38495              * @event activate
38496              * Fires when the editor is first receives the focus. Any insertion must wait
38497              * until after this event.
38498              * @param {HtmlEditor} this
38499              */
38500             activate: true,
38501              /**
38502              * @event beforesync
38503              * Fires before the textarea is updated with content from the editor iframe. Return false
38504              * to cancel the sync.
38505              * @param {HtmlEditor} this
38506              * @param {String} html
38507              */
38508             beforesync: true,
38509              /**
38510              * @event beforepush
38511              * Fires before the iframe editor is updated with content from the textarea. Return false
38512              * to cancel the push.
38513              * @param {HtmlEditor} this
38514              * @param {String} html
38515              */
38516             beforepush: true,
38517              /**
38518              * @event sync
38519              * Fires when the textarea is updated with content from the editor iframe.
38520              * @param {HtmlEditor} this
38521              * @param {String} html
38522              */
38523             sync: true,
38524              /**
38525              * @event push
38526              * Fires when the iframe editor is updated with content from the textarea.
38527              * @param {HtmlEditor} this
38528              * @param {String} html
38529              */
38530             push: true,
38531              /**
38532              * @event editmodechange
38533              * Fires when the editor switches edit modes
38534              * @param {HtmlEditor} this
38535              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38536              */
38537             editmodechange: true,
38538             /**
38539              * @event editorevent
38540              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38541              * @param {HtmlEditor} this
38542              */
38543             editorevent: true
38544         });
38545         this.defaultAutoCreate =  {
38546             tag: "textarea",
38547             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38548             autocomplete: "off"
38549         };
38550     },
38551
38552     /**
38553      * Protected method that will not generally be called directly. It
38554      * is called when the editor creates its toolbar. Override this method if you need to
38555      * add custom toolbar buttons.
38556      * @param {HtmlEditor} editor
38557      */
38558     createToolbar : function(editor){
38559         if (!editor.toolbars || !editor.toolbars.length) {
38560             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38561         }
38562         
38563         for (var i =0 ; i < editor.toolbars.length;i++) {
38564             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38565             editor.toolbars[i].init(editor);
38566         }
38567          
38568         
38569     },
38570
38571     /**
38572      * Protected method that will not generally be called directly. It
38573      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38574      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38575      */
38576     getDocMarkup : function(){
38577         // body styles..
38578         var st = '';
38579         if (this.stylesheets === false) {
38580             
38581             Roo.get(document.head).select('style').each(function(node) {
38582                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38583             });
38584             
38585             Roo.get(document.head).select('link').each(function(node) { 
38586                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38587             });
38588             
38589         } else if (!this.stylesheet.length) {
38590                 // simple..
38591                 st = '<style type="text/css">' +
38592                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38593                    '</style>';
38594         } else {
38595             Roo.each(this.stylesheets, function(s) {
38596                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38597             });
38598             
38599         }
38600         
38601         return '<html><head>' + st  +
38602             //<style type="text/css">' +
38603             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38604             //'</style>' +
38605             ' </head><body></body></html>';
38606     },
38607
38608     // private
38609     onRender : function(ct, position)
38610     {
38611         var _t = this;
38612         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38613         this.el.dom.style.border = '0 none';
38614         this.el.dom.setAttribute('tabIndex', -1);
38615         this.el.addClass('x-hidden');
38616         if(Roo.isIE){ // fix IE 1px bogus margin
38617             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38618         }
38619         this.wrap = this.el.wrap({
38620             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38621         });
38622         
38623         if (this.resizable) {
38624             this.resizeEl = new Roo.Resizable(this.wrap, {
38625                 pinned : true,
38626                 wrap: true,
38627                 dynamic : true,
38628                 minHeight : this.height,
38629                 height: this.height,
38630                 handles : this.resizable,
38631                 width: this.width,
38632                 listeners : {
38633                     resize : function(r, w, h) {
38634                         _t.onResize(w,h); // -something
38635                     }
38636                 }
38637             });
38638             
38639         }
38640
38641         this.frameId = Roo.id();
38642         
38643         this.createToolbar(this);
38644         
38645       
38646         
38647         var iframe = this.wrap.createChild({
38648             tag: 'iframe',
38649             id: this.frameId,
38650             name: this.frameId,
38651             frameBorder : 'no',
38652             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38653         }, this.el
38654         );
38655         
38656        // console.log(iframe);
38657         //this.wrap.dom.appendChild(iframe);
38658
38659         this.iframe = iframe.dom;
38660
38661          this.assignDocWin();
38662         
38663         this.doc.designMode = 'on';
38664        
38665         this.doc.open();
38666         this.doc.write(this.getDocMarkup());
38667         this.doc.close();
38668
38669         
38670         var task = { // must defer to wait for browser to be ready
38671             run : function(){
38672                 //console.log("run task?" + this.doc.readyState);
38673                 this.assignDocWin();
38674                 if(this.doc.body || this.doc.readyState == 'complete'){
38675                     try {
38676                         this.doc.designMode="on";
38677                     } catch (e) {
38678                         return;
38679                     }
38680                     Roo.TaskMgr.stop(task);
38681                     this.initEditor.defer(10, this);
38682                 }
38683             },
38684             interval : 10,
38685             duration:10000,
38686             scope: this
38687         };
38688         Roo.TaskMgr.start(task);
38689
38690         if(!this.width){
38691             this.setSize(this.wrap.getSize());
38692         }
38693         if (this.resizeEl) {
38694             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38695             // should trigger onReize..
38696         }
38697     },
38698
38699     // private
38700     onResize : function(w, h)
38701     {
38702         //Roo.log('resize: ' +w + ',' + h );
38703         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38704         if(this.el && this.iframe){
38705             if(typeof w == 'number'){
38706                 var aw = w - this.wrap.getFrameWidth('lr');
38707                 this.el.setWidth(this.adjustWidth('textarea', aw));
38708                 this.iframe.style.width = aw + 'px';
38709             }
38710             if(typeof h == 'number'){
38711                 var tbh = 0;
38712                 for (var i =0; i < this.toolbars.length;i++) {
38713                     // fixme - ask toolbars for heights?
38714                     tbh += this.toolbars[i].tb.el.getHeight();
38715                     if (this.toolbars[i].footer) {
38716                         tbh += this.toolbars[i].footer.el.getHeight();
38717                     }
38718                 }
38719                 
38720                 
38721                 
38722                 
38723                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38724                 ah -= 5; // knock a few pixes off for look..
38725                 this.el.setHeight(this.adjustWidth('textarea', ah));
38726                 this.iframe.style.height = ah + 'px';
38727                 if(this.doc){
38728                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38729                 }
38730             }
38731         }
38732     },
38733
38734     /**
38735      * Toggles the editor between standard and source edit mode.
38736      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38737      */
38738     toggleSourceEdit : function(sourceEditMode){
38739         
38740         this.sourceEditMode = sourceEditMode === true;
38741         
38742         if(this.sourceEditMode){
38743           
38744             this.syncValue();
38745             this.iframe.className = 'x-hidden';
38746             this.el.removeClass('x-hidden');
38747             this.el.dom.removeAttribute('tabIndex');
38748             this.el.focus();
38749         }else{
38750              
38751             this.pushValue();
38752             this.iframe.className = '';
38753             this.el.addClass('x-hidden');
38754             this.el.dom.setAttribute('tabIndex', -1);
38755             this.deferFocus();
38756         }
38757         this.setSize(this.wrap.getSize());
38758         this.fireEvent('editmodechange', this, this.sourceEditMode);
38759     },
38760
38761     // private used internally
38762     createLink : function(){
38763         var url = prompt(this.createLinkText, this.defaultLinkValue);
38764         if(url && url != 'http:/'+'/'){
38765             this.relayCmd('createlink', url);
38766         }
38767     },
38768
38769     // private (for BoxComponent)
38770     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38771
38772     // private (for BoxComponent)
38773     getResizeEl : function(){
38774         return this.wrap;
38775     },
38776
38777     // private (for BoxComponent)
38778     getPositionEl : function(){
38779         return this.wrap;
38780     },
38781
38782     // private
38783     initEvents : function(){
38784         this.originalValue = this.getValue();
38785     },
38786
38787     /**
38788      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38789      * @method
38790      */
38791     markInvalid : Roo.emptyFn,
38792     /**
38793      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38794      * @method
38795      */
38796     clearInvalid : Roo.emptyFn,
38797
38798     setValue : function(v){
38799         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38800         this.pushValue();
38801     },
38802
38803     /**
38804      * Protected method that will not generally be called directly. If you need/want
38805      * custom HTML cleanup, this is the method you should override.
38806      * @param {String} html The HTML to be cleaned
38807      * return {String} The cleaned HTML
38808      */
38809     cleanHtml : function(html){
38810         html = String(html);
38811         if(html.length > 5){
38812             if(Roo.isSafari){ // strip safari nonsense
38813                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38814             }
38815         }
38816         if(html == '&nbsp;'){
38817             html = '';
38818         }
38819         return html;
38820     },
38821
38822     /**
38823      * Protected method that will not generally be called directly. Syncs the contents
38824      * of the editor iframe with the textarea.
38825      */
38826     syncValue : function(){
38827         if(this.initialized){
38828             var bd = (this.doc.body || this.doc.documentElement);
38829             this.cleanUpPaste();
38830             var html = bd.innerHTML;
38831             if(Roo.isSafari){
38832                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38833                 var m = bs.match(/text-align:(.*?);/i);
38834                 if(m && m[1]){
38835                     html = '<div style="'+m[0]+'">' + html + '</div>';
38836                 }
38837             }
38838             html = this.cleanHtml(html);
38839             if(this.fireEvent('beforesync', this, html) !== false){
38840                 this.el.dom.value = html;
38841                 this.fireEvent('sync', this, html);
38842             }
38843         }
38844     },
38845
38846     /**
38847      * Protected method that will not generally be called directly. Pushes the value of the textarea
38848      * into the iframe editor.
38849      */
38850     pushValue : function(){
38851         if(this.initialized){
38852             var v = this.el.dom.value;
38853             if(v.length < 1){
38854                 v = '&#160;';
38855             }
38856             
38857             if(this.fireEvent('beforepush', this, v) !== false){
38858                 var d = (this.doc.body || this.doc.documentElement);
38859                 d.innerHTML = v;
38860                 this.cleanUpPaste();
38861                 this.el.dom.value = d.innerHTML;
38862                 this.fireEvent('push', this, v);
38863             }
38864         }
38865     },
38866
38867     // private
38868     deferFocus : function(){
38869         this.focus.defer(10, this);
38870     },
38871
38872     // doc'ed in Field
38873     focus : function(){
38874         if(this.win && !this.sourceEditMode){
38875             this.win.focus();
38876         }else{
38877             this.el.focus();
38878         }
38879     },
38880     
38881     assignDocWin: function()
38882     {
38883         var iframe = this.iframe;
38884         
38885          if(Roo.isIE){
38886             this.doc = iframe.contentWindow.document;
38887             this.win = iframe.contentWindow;
38888         } else {
38889             if (!Roo.get(this.frameId)) {
38890                 return;
38891             }
38892             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38893             this.win = Roo.get(this.frameId).dom.contentWindow;
38894         }
38895     },
38896     
38897     // private
38898     initEditor : function(){
38899         //console.log("INIT EDITOR");
38900         this.assignDocWin();
38901         
38902         
38903         
38904         this.doc.designMode="on";
38905         this.doc.open();
38906         this.doc.write(this.getDocMarkup());
38907         this.doc.close();
38908         
38909         var dbody = (this.doc.body || this.doc.documentElement);
38910         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38911         // this copies styles from the containing element into thsi one..
38912         // not sure why we need all of this..
38913         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38914         ss['background-attachment'] = 'fixed'; // w3c
38915         dbody.bgProperties = 'fixed'; // ie
38916         Roo.DomHelper.applyStyles(dbody, ss);
38917         Roo.EventManager.on(this.doc, {
38918             //'mousedown': this.onEditorEvent,
38919             'mouseup': this.onEditorEvent,
38920             'dblclick': this.onEditorEvent,
38921             'click': this.onEditorEvent,
38922             'keyup': this.onEditorEvent,
38923             buffer:100,
38924             scope: this
38925         });
38926         if(Roo.isGecko){
38927             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38928         }
38929         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38930             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38931         }
38932         this.initialized = true;
38933
38934         this.fireEvent('initialize', this);
38935         this.pushValue();
38936     },
38937
38938     // private
38939     onDestroy : function(){
38940         
38941         
38942         
38943         if(this.rendered){
38944             
38945             for (var i =0; i < this.toolbars.length;i++) {
38946                 // fixme - ask toolbars for heights?
38947                 this.toolbars[i].onDestroy();
38948             }
38949             
38950             this.wrap.dom.innerHTML = '';
38951             this.wrap.remove();
38952         }
38953     },
38954
38955     // private
38956     onFirstFocus : function(){
38957         
38958         this.assignDocWin();
38959         
38960         
38961         this.activated = true;
38962         for (var i =0; i < this.toolbars.length;i++) {
38963             this.toolbars[i].onFirstFocus();
38964         }
38965        
38966         if(Roo.isGecko){ // prevent silly gecko errors
38967             this.win.focus();
38968             var s = this.win.getSelection();
38969             if(!s.focusNode || s.focusNode.nodeType != 3){
38970                 var r = s.getRangeAt(0);
38971                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38972                 r.collapse(true);
38973                 this.deferFocus();
38974             }
38975             try{
38976                 this.execCmd('useCSS', true);
38977                 this.execCmd('styleWithCSS', false);
38978             }catch(e){}
38979         }
38980         this.fireEvent('activate', this);
38981     },
38982
38983     // private
38984     adjustFont: function(btn){
38985         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38986         //if(Roo.isSafari){ // safari
38987         //    adjust *= 2;
38988        // }
38989         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38990         if(Roo.isSafari){ // safari
38991             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38992             v =  (v < 10) ? 10 : v;
38993             v =  (v > 48) ? 48 : v;
38994             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38995             
38996         }
38997         
38998         
38999         v = Math.max(1, v+adjust);
39000         
39001         this.execCmd('FontSize', v  );
39002     },
39003
39004     onEditorEvent : function(e){
39005         this.fireEvent('editorevent', this, e);
39006       //  this.updateToolbar();
39007         this.syncValue();
39008     },
39009
39010     insertTag : function(tg)
39011     {
39012         // could be a bit smarter... -> wrap the current selected tRoo..
39013         
39014         this.execCmd("formatblock",   tg);
39015         
39016     },
39017     
39018     insertText : function(txt)
39019     {
39020         
39021         
39022         range = this.createRange();
39023         range.deleteContents();
39024                //alert(Sender.getAttribute('label'));
39025                
39026         range.insertNode(this.doc.createTextNode(txt));
39027     } ,
39028     
39029     // private
39030     relayBtnCmd : function(btn){
39031         this.relayCmd(btn.cmd);
39032     },
39033
39034     /**
39035      * Executes a Midas editor command on the editor document and performs necessary focus and
39036      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39037      * @param {String} cmd The Midas command
39038      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39039      */
39040     relayCmd : function(cmd, value){
39041         this.win.focus();
39042         this.execCmd(cmd, value);
39043         this.fireEvent('editorevent', this);
39044         //this.updateToolbar();
39045         this.deferFocus();
39046     },
39047
39048     /**
39049      * Executes a Midas editor command directly on the editor document.
39050      * For visual commands, you should use {@link #relayCmd} instead.
39051      * <b>This should only be called after the editor is initialized.</b>
39052      * @param {String} cmd The Midas command
39053      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39054      */
39055     execCmd : function(cmd, value){
39056         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39057         this.syncValue();
39058     },
39059
39060    
39061     /**
39062      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39063      * to insert tRoo.
39064      * @param {String} text
39065      */
39066     insertAtCursor : function(text){
39067         if(!this.activated){
39068             return;
39069         }
39070         if(Roo.isIE){
39071             this.win.focus();
39072             var r = this.doc.selection.createRange();
39073             if(r){
39074                 r.collapse(true);
39075                 r.pasteHTML(text);
39076                 this.syncValue();
39077                 this.deferFocus();
39078             }
39079         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39080             this.win.focus();
39081             this.execCmd('InsertHTML', text);
39082             this.deferFocus();
39083         }
39084     },
39085  // private
39086     mozKeyPress : function(e){
39087         if(e.ctrlKey){
39088             var c = e.getCharCode(), cmd;
39089           
39090             if(c > 0){
39091                 c = String.fromCharCode(c).toLowerCase();
39092                 switch(c){
39093                     case 'b':
39094                         cmd = 'bold';
39095                     break;
39096                     case 'i':
39097                         cmd = 'italic';
39098                     break;
39099                     case 'u':
39100                         cmd = 'underline';
39101                     case 'v':
39102                         this.cleanUpPaste.defer(100, this);
39103                         return;
39104                     break;
39105                 }
39106                 if(cmd){
39107                     this.win.focus();
39108                     this.execCmd(cmd);
39109                     this.deferFocus();
39110                     e.preventDefault();
39111                 }
39112                 
39113             }
39114         }
39115     },
39116
39117     // private
39118     fixKeys : function(){ // load time branching for fastest keydown performance
39119         if(Roo.isIE){
39120             return function(e){
39121                 var k = e.getKey(), r;
39122                 if(k == e.TAB){
39123                     e.stopEvent();
39124                     r = this.doc.selection.createRange();
39125                     if(r){
39126                         r.collapse(true);
39127                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39128                         this.deferFocus();
39129                     }
39130                     return;
39131                 }
39132                 
39133                 if(k == e.ENTER){
39134                     r = this.doc.selection.createRange();
39135                     if(r){
39136                         var target = r.parentElement();
39137                         if(!target || target.tagName.toLowerCase() != 'li'){
39138                             e.stopEvent();
39139                             r.pasteHTML('<br />');
39140                             r.collapse(false);
39141                             r.select();
39142                         }
39143                     }
39144                 }
39145                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39146                     this.cleanUpPaste.defer(100, this);
39147                     return;
39148                 }
39149                 
39150                 
39151             };
39152         }else if(Roo.isOpera){
39153             return function(e){
39154                 var k = e.getKey();
39155                 if(k == e.TAB){
39156                     e.stopEvent();
39157                     this.win.focus();
39158                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39159                     this.deferFocus();
39160                 }
39161                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39162                     this.cleanUpPaste.defer(100, this);
39163                     return;
39164                 }
39165                 
39166             };
39167         }else if(Roo.isSafari){
39168             return function(e){
39169                 var k = e.getKey();
39170                 
39171                 if(k == e.TAB){
39172                     e.stopEvent();
39173                     this.execCmd('InsertText','\t');
39174                     this.deferFocus();
39175                     return;
39176                 }
39177                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39178                     this.cleanUpPaste.defer(100, this);
39179                     return;
39180                 }
39181                 
39182              };
39183         }
39184     }(),
39185     
39186     getAllAncestors: function()
39187     {
39188         var p = this.getSelectedNode();
39189         var a = [];
39190         if (!p) {
39191             a.push(p); // push blank onto stack..
39192             p = this.getParentElement();
39193         }
39194         
39195         
39196         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39197             a.push(p);
39198             p = p.parentNode;
39199         }
39200         a.push(this.doc.body);
39201         return a;
39202     },
39203     lastSel : false,
39204     lastSelNode : false,
39205     
39206     
39207     getSelection : function() 
39208     {
39209         this.assignDocWin();
39210         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39211     },
39212     
39213     getSelectedNode: function() 
39214     {
39215         // this may only work on Gecko!!!
39216         
39217         // should we cache this!!!!
39218         
39219         
39220         
39221          
39222         var range = this.createRange(this.getSelection());
39223         
39224         if (Roo.isIE) {
39225             var parent = range.parentElement();
39226             while (true) {
39227                 var testRange = range.duplicate();
39228                 testRange.moveToElementText(parent);
39229                 if (testRange.inRange(range)) {
39230                     break;
39231                 }
39232                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39233                     break;
39234                 }
39235                 parent = parent.parentElement;
39236             }
39237             return parent;
39238         }
39239         
39240         // is ancestor a text element.
39241         var ac =  range.commonAncestorContainer;
39242         if (ac.nodeType == 3) {
39243             ac = ac.parentNode;
39244         }
39245         
39246         var ar = ac.childNodes;
39247          
39248         var nodes = [];
39249         var other_nodes = [];
39250         var has_other_nodes = false;
39251         for (var i=0;i<ar.length;i++) {
39252             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39253                 continue;
39254             }
39255             // fullly contained node.
39256             
39257             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39258                 nodes.push(ar[i]);
39259                 continue;
39260             }
39261             
39262             // probably selected..
39263             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39264                 other_nodes.push(ar[i]);
39265                 continue;
39266             }
39267             // outer..
39268             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39269                 continue;
39270             }
39271             
39272             
39273             has_other_nodes = true;
39274         }
39275         if (!nodes.length && other_nodes.length) {
39276             nodes= other_nodes;
39277         }
39278         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39279             return false;
39280         }
39281         
39282         return nodes[0];
39283     },
39284     createRange: function(sel)
39285     {
39286         // this has strange effects when using with 
39287         // top toolbar - not sure if it's a great idea.
39288         //this.editor.contentWindow.focus();
39289         if (typeof sel != "undefined") {
39290             try {
39291                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39292             } catch(e) {
39293                 return this.doc.createRange();
39294             }
39295         } else {
39296             return this.doc.createRange();
39297         }
39298     },
39299     getParentElement: function()
39300     {
39301         
39302         this.assignDocWin();
39303         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39304         
39305         var range = this.createRange(sel);
39306          
39307         try {
39308             var p = range.commonAncestorContainer;
39309             while (p.nodeType == 3) { // text node
39310                 p = p.parentNode;
39311             }
39312             return p;
39313         } catch (e) {
39314             return null;
39315         }
39316     
39317     },
39318     /***
39319      *
39320      * Range intersection.. the hard stuff...
39321      *  '-1' = before
39322      *  '0' = hits..
39323      *  '1' = after.
39324      *         [ -- selected range --- ]
39325      *   [fail]                        [fail]
39326      *
39327      *    basically..
39328      *      if end is before start or  hits it. fail.
39329      *      if start is after end or hits it fail.
39330      *
39331      *   if either hits (but other is outside. - then it's not 
39332      *   
39333      *    
39334      **/
39335     
39336     
39337     // @see http://www.thismuchiknow.co.uk/?p=64.
39338     rangeIntersectsNode : function(range, node)
39339     {
39340         var nodeRange = node.ownerDocument.createRange();
39341         try {
39342             nodeRange.selectNode(node);
39343         } catch (e) {
39344             nodeRange.selectNodeContents(node);
39345         }
39346     
39347         var rangeStartRange = range.cloneRange();
39348         rangeStartRange.collapse(true);
39349     
39350         var rangeEndRange = range.cloneRange();
39351         rangeEndRange.collapse(false);
39352     
39353         var nodeStartRange = nodeRange.cloneRange();
39354         nodeStartRange.collapse(true);
39355     
39356         var nodeEndRange = nodeRange.cloneRange();
39357         nodeEndRange.collapse(false);
39358     
39359         return rangeStartRange.compareBoundaryPoints(
39360                  Range.START_TO_START, nodeEndRange) == -1 &&
39361                rangeEndRange.compareBoundaryPoints(
39362                  Range.START_TO_START, nodeStartRange) == 1;
39363         
39364          
39365     },
39366     rangeCompareNode : function(range, node)
39367     {
39368         var nodeRange = node.ownerDocument.createRange();
39369         try {
39370             nodeRange.selectNode(node);
39371         } catch (e) {
39372             nodeRange.selectNodeContents(node);
39373         }
39374         
39375         
39376         range.collapse(true);
39377     
39378         nodeRange.collapse(true);
39379      
39380         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39381         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39382          
39383         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39384         
39385         var nodeIsBefore   =  ss == 1;
39386         var nodeIsAfter    = ee == -1;
39387         
39388         if (nodeIsBefore && nodeIsAfter)
39389             return 0; // outer
39390         if (!nodeIsBefore && nodeIsAfter)
39391             return 1; //right trailed.
39392         
39393         if (nodeIsBefore && !nodeIsAfter)
39394             return 2;  // left trailed.
39395         // fully contined.
39396         return 3;
39397     },
39398
39399     // private? - in a new class?
39400     cleanUpPaste :  function()
39401     {
39402         // cleans up the whole document..
39403       //  console.log('cleanuppaste');
39404         this.cleanUpChildren(this.doc.body);
39405         
39406         
39407     },
39408     cleanUpChildren : function (n)
39409     {
39410         if (!n.childNodes.length) {
39411             return;
39412         }
39413         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39414            this.cleanUpChild(n.childNodes[i]);
39415         }
39416     },
39417     
39418     
39419         
39420     
39421     cleanUpChild : function (node)
39422     {
39423         //console.log(node);
39424         if (node.nodeName == "#text") {
39425             // clean up silly Windows -- stuff?
39426             return; 
39427         }
39428         if (node.nodeName == "#comment") {
39429             node.parentNode.removeChild(node);
39430             // clean up silly Windows -- stuff?
39431             return; 
39432         }
39433         
39434         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39435             // remove node.
39436             node.parentNode.removeChild(node);
39437             return;
39438             
39439         }
39440         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39441             this.cleanUpChildren(node);
39442             // inserts everything just before this node...
39443             while (node.childNodes.length) {
39444                 var cn = node.childNodes[0];
39445                 node.removeChild(cn);
39446                 node.parentNode.insertBefore(cn, node);
39447             }
39448             node.parentNode.removeChild(node);
39449             return;
39450         }
39451         
39452         if (!node.attributes || !node.attributes.length) {
39453             this.cleanUpChildren(node);
39454             return;
39455         }
39456         
39457         function cleanAttr(n,v)
39458         {
39459             
39460             if (v.match(/^\./) || v.match(/^\//)) {
39461                 return;
39462             }
39463             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39464                 return;
39465             }
39466             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39467             node.removeAttribute(n);
39468             
39469         }
39470         
39471         function cleanStyle(n,v)
39472         {
39473             if (v.match(/expression/)) { //XSS?? should we even bother..
39474                 node.removeAttribute(n);
39475                 return;
39476             }
39477             
39478             
39479             var parts = v.split(/;/);
39480             Roo.each(parts, function(p) {
39481                 p = p.replace(/\s+/g,'');
39482                 if (!p.length) {
39483                     return true;
39484                 }
39485                 var l = p.split(':').shift().replace(/\s+/g,'');
39486                 
39487                 // only allow 'c whitelisted system attributes'
39488                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39489                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39490                     node.removeAttribute(n);
39491                     return false;
39492                 }
39493                 return true;
39494             });
39495             
39496             
39497         }
39498         
39499         
39500         for (var i = node.attributes.length-1; i > -1 ; i--) {
39501             var a = node.attributes[i];
39502             //console.log(a);
39503             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39504                 node.removeAttribute(a.name);
39505                 return;
39506             }
39507             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39508                 cleanAttr(a.name,a.value); // fixme..
39509                 return;
39510             }
39511             if (a.name == 'style') {
39512                 cleanStyle(a.name,a.value);
39513             }
39514             /// clean up MS crap..
39515             if (a.name == 'class') {
39516                 if (a.value.match(/^Mso/)) {
39517                     node.className = '';
39518                 }
39519             }
39520             
39521             // style cleanup!?
39522             // class cleanup?
39523             
39524         }
39525         
39526         
39527         this.cleanUpChildren(node);
39528         
39529         
39530     }
39531     
39532     
39533     // hide stuff that is not compatible
39534     /**
39535      * @event blur
39536      * @hide
39537      */
39538     /**
39539      * @event change
39540      * @hide
39541      */
39542     /**
39543      * @event focus
39544      * @hide
39545      */
39546     /**
39547      * @event specialkey
39548      * @hide
39549      */
39550     /**
39551      * @cfg {String} fieldClass @hide
39552      */
39553     /**
39554      * @cfg {String} focusClass @hide
39555      */
39556     /**
39557      * @cfg {String} autoCreate @hide
39558      */
39559     /**
39560      * @cfg {String} inputType @hide
39561      */
39562     /**
39563      * @cfg {String} invalidClass @hide
39564      */
39565     /**
39566      * @cfg {String} invalidText @hide
39567      */
39568     /**
39569      * @cfg {String} msgFx @hide
39570      */
39571     /**
39572      * @cfg {String} validateOnBlur @hide
39573      */
39574 });
39575
39576 Roo.form.HtmlEditor.white = [
39577         'area', 'br', 'img', 'input', 'hr', 'wbr',
39578         
39579        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39580        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39581        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39582        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39583        'table',   'ul',         'xmp', 
39584        
39585        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39586       'thead',   'tr', 
39587      
39588       'dir', 'menu', 'ol', 'ul', 'dl',
39589        
39590       'embed',  'object'
39591 ];
39592
39593
39594 Roo.form.HtmlEditor.black = [
39595     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39596         'applet', // 
39597         'base',   'basefont', 'bgsound', 'blink',  'body', 
39598         'frame',  'frameset', 'head',    'html',   'ilayer', 
39599         'iframe', 'layer',  'link',     'meta',    'object',   
39600         'script', 'style' ,'title',  'xml' // clean later..
39601 ];
39602 Roo.form.HtmlEditor.clean = [
39603     'script', 'style', 'title', 'xml'
39604 ];
39605 Roo.form.HtmlEditor.remove = [
39606     'font'
39607 ];
39608 // attributes..
39609
39610 Roo.form.HtmlEditor.ablack = [
39611     'on'
39612 ];
39613     
39614 Roo.form.HtmlEditor.aclean = [ 
39615     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39616 ];
39617
39618 // protocols..
39619 Roo.form.HtmlEditor.pwhite= [
39620         'http',  'https',  'mailto'
39621 ];
39622
39623 // white listed style attributes.
39624 Roo.form.HtmlEditor.cwhite= [
39625         'text-align',
39626         'font-size'
39627 ];
39628
39629 // <script type="text/javascript">
39630 /*
39631  * Based on
39632  * Ext JS Library 1.1.1
39633  * Copyright(c) 2006-2007, Ext JS, LLC.
39634  *  
39635  
39636  */
39637
39638 /**
39639  * @class Roo.form.HtmlEditorToolbar1
39640  * Basic Toolbar
39641  * 
39642  * Usage:
39643  *
39644  new Roo.form.HtmlEditor({
39645     ....
39646     toolbars : [
39647         new Roo.form.HtmlEditorToolbar1({
39648             disable : { fonts: 1 , format: 1, ..., ... , ...],
39649             btns : [ .... ]
39650         })
39651     }
39652      
39653  * 
39654  * @cfg {Object} disable List of elements to disable..
39655  * @cfg {Array} btns List of additional buttons.
39656  * 
39657  * 
39658  * NEEDS Extra CSS? 
39659  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39660  */
39661  
39662 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39663 {
39664     
39665     Roo.apply(this, config);
39666     
39667     // default disabled, based on 'good practice'..
39668     this.disable = this.disable || {};
39669     Roo.applyIf(this.disable, {
39670         fontSize : true,
39671         colors : true,
39672         specialElements : true
39673     });
39674     
39675     
39676     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39677     // dont call parent... till later.
39678 }
39679
39680 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39681     
39682     tb: false,
39683     
39684     rendered: false,
39685     
39686     editor : false,
39687     /**
39688      * @cfg {Object} disable  List of toolbar elements to disable
39689          
39690      */
39691     disable : false,
39692       /**
39693      * @cfg {Array} fontFamilies An array of available font families
39694      */
39695     fontFamilies : [
39696         'Arial',
39697         'Courier New',
39698         'Tahoma',
39699         'Times New Roman',
39700         'Verdana'
39701     ],
39702     
39703     specialChars : [
39704            "&#169;",
39705           "&#174;",     
39706           "&#8482;",    
39707           "&#163;" ,    
39708          // "&#8212;",    
39709           "&#8230;",    
39710           "&#247;" ,    
39711         //  "&#225;" ,     ?? a acute?
39712            "&#8364;"    , //Euro
39713        //   "&#8220;"    ,
39714         //  "&#8221;"    ,
39715         //  "&#8226;"    ,
39716           "&#176;"  //   , // degrees
39717
39718          // "&#233;"     , // e ecute
39719          // "&#250;"     , // u ecute?
39720     ],
39721     
39722     specialElements : [
39723         {
39724             text: "Insert Table",
39725             xtype: 'MenuItem',
39726             xns : Roo.Menu,
39727             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39728                 
39729         },
39730         {    
39731             text: "Insert Image",
39732             xtype: 'MenuItem',
39733             xns : Roo.Menu,
39734             ihtml : '<img src="about:blank"/>'
39735             
39736         }
39737         
39738          
39739     ],
39740     
39741     
39742     inputElements : [ 
39743             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39744             "input:submit", "input:button", "select", "textarea", "label" ],
39745     formats : [
39746         ["p"] ,  
39747         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39748         ["pre"],[ "code"], 
39749         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39750     ],
39751      /**
39752      * @cfg {String} defaultFont default font to use.
39753      */
39754     defaultFont: 'tahoma',
39755    
39756     fontSelect : false,
39757     
39758     
39759     formatCombo : false,
39760     
39761     init : function(editor)
39762     {
39763         this.editor = editor;
39764         
39765         
39766         var fid = editor.frameId;
39767         var etb = this;
39768         function btn(id, toggle, handler){
39769             var xid = fid + '-'+ id ;
39770             return {
39771                 id : xid,
39772                 cmd : id,
39773                 cls : 'x-btn-icon x-edit-'+id,
39774                 enableToggle:toggle !== false,
39775                 scope: editor, // was editor...
39776                 handler:handler||editor.relayBtnCmd,
39777                 clickEvent:'mousedown',
39778                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39779                 tabIndex:-1
39780             };
39781         }
39782         
39783         
39784         
39785         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39786         this.tb = tb;
39787          // stop form submits
39788         tb.el.on('click', function(e){
39789             e.preventDefault(); // what does this do?
39790         });
39791
39792         if(!this.disable.font && !Roo.isSafari){
39793             /* why no safari for fonts
39794             editor.fontSelect = tb.el.createChild({
39795                 tag:'select',
39796                 tabIndex: -1,
39797                 cls:'x-font-select',
39798                 html: editor.createFontOptions()
39799             });
39800             editor.fontSelect.on('change', function(){
39801                 var font = editor.fontSelect.dom.value;
39802                 editor.relayCmd('fontname', font);
39803                 editor.deferFocus();
39804             }, editor);
39805             tb.add(
39806                 editor.fontSelect.dom,
39807                 '-'
39808             );
39809             */
39810         };
39811         if(!this.disable.formats){
39812             this.formatCombo = new Roo.form.ComboBox({
39813                 store: new Roo.data.SimpleStore({
39814                     id : 'tag',
39815                     fields: ['tag'],
39816                     data : this.formats // from states.js
39817                 }),
39818                 blockFocus : true,
39819                 //autoCreate : {tag: "div",  size: "20"},
39820                 displayField:'tag',
39821                 typeAhead: false,
39822                 mode: 'local',
39823                 editable : false,
39824                 triggerAction: 'all',
39825                 emptyText:'Add tag',
39826                 selectOnFocus:true,
39827                 width:135,
39828                 listeners : {
39829                     'select': function(c, r, i) {
39830                         editor.insertTag(r.get('tag'));
39831                         editor.focus();
39832                     }
39833                 }
39834
39835             });
39836             tb.addField(this.formatCombo);
39837             
39838         }
39839         
39840         if(!this.disable.format){
39841             tb.add(
39842                 btn('bold'),
39843                 btn('italic'),
39844                 btn('underline')
39845             );
39846         };
39847         if(!this.disable.fontSize){
39848             tb.add(
39849                 '-',
39850                 
39851                 
39852                 btn('increasefontsize', false, editor.adjustFont),
39853                 btn('decreasefontsize', false, editor.adjustFont)
39854             );
39855         };
39856         
39857         
39858         if(!this.disable.colors){
39859             tb.add(
39860                 '-', {
39861                     id:editor.frameId +'-forecolor',
39862                     cls:'x-btn-icon x-edit-forecolor',
39863                     clickEvent:'mousedown',
39864                     tooltip: this.buttonTips['forecolor'] || undefined,
39865                     tabIndex:-1,
39866                     menu : new Roo.menu.ColorMenu({
39867                         allowReselect: true,
39868                         focus: Roo.emptyFn,
39869                         value:'000000',
39870                         plain:true,
39871                         selectHandler: function(cp, color){
39872                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39873                             editor.deferFocus();
39874                         },
39875                         scope: editor,
39876                         clickEvent:'mousedown'
39877                     })
39878                 }, {
39879                     id:editor.frameId +'backcolor',
39880                     cls:'x-btn-icon x-edit-backcolor',
39881                     clickEvent:'mousedown',
39882                     tooltip: this.buttonTips['backcolor'] || undefined,
39883                     tabIndex:-1,
39884                     menu : new Roo.menu.ColorMenu({
39885                         focus: Roo.emptyFn,
39886                         value:'FFFFFF',
39887                         plain:true,
39888                         allowReselect: true,
39889                         selectHandler: function(cp, color){
39890                             if(Roo.isGecko){
39891                                 editor.execCmd('useCSS', false);
39892                                 editor.execCmd('hilitecolor', color);
39893                                 editor.execCmd('useCSS', true);
39894                                 editor.deferFocus();
39895                             }else{
39896                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39897                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39898                                 editor.deferFocus();
39899                             }
39900                         },
39901                         scope:editor,
39902                         clickEvent:'mousedown'
39903                     })
39904                 }
39905             );
39906         };
39907         // now add all the items...
39908         
39909
39910         if(!this.disable.alignments){
39911             tb.add(
39912                 '-',
39913                 btn('justifyleft'),
39914                 btn('justifycenter'),
39915                 btn('justifyright')
39916             );
39917         };
39918
39919         //if(!Roo.isSafari){
39920             if(!this.disable.links){
39921                 tb.add(
39922                     '-',
39923                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39924                 );
39925             };
39926
39927             if(!this.disable.lists){
39928                 tb.add(
39929                     '-',
39930                     btn('insertorderedlist'),
39931                     btn('insertunorderedlist')
39932                 );
39933             }
39934             if(!this.disable.sourceEdit){
39935                 tb.add(
39936                     '-',
39937                     btn('sourceedit', true, function(btn){
39938                         this.toggleSourceEdit(btn.pressed);
39939                     })
39940                 );
39941             }
39942         //}
39943         
39944         var smenu = { };
39945         // special menu.. - needs to be tidied up..
39946         if (!this.disable.special) {
39947             smenu = {
39948                 text: "&#169;",
39949                 cls: 'x-edit-none',
39950                 
39951                 menu : {
39952                     items : []
39953                 }
39954             };
39955             for (var i =0; i < this.specialChars.length; i++) {
39956                 smenu.menu.items.push({
39957                     
39958                     html: this.specialChars[i],
39959                     handler: function(a,b) {
39960                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39961                         
39962                     },
39963                     tabIndex:-1
39964                 });
39965             }
39966             
39967             
39968             tb.add(smenu);
39969             
39970             
39971         }
39972          
39973         if (!this.disable.specialElements) {
39974             var semenu = {
39975                 text: "Other;",
39976                 cls: 'x-edit-none',
39977                 menu : {
39978                     items : []
39979                 }
39980             };
39981             for (var i =0; i < this.specialElements.length; i++) {
39982                 semenu.menu.items.push(
39983                     Roo.apply({ 
39984                         handler: function(a,b) {
39985                             editor.insertAtCursor(this.ihtml);
39986                         }
39987                     }, this.specialElements[i])
39988                 );
39989                     
39990             }
39991             
39992             tb.add(semenu);
39993             
39994             
39995         }
39996          
39997         
39998         if (this.btns) {
39999             for(var i =0; i< this.btns.length;i++) {
40000                 var b = this.btns[i];
40001                 b.cls =  'x-edit-none';
40002                 b.scope = editor;
40003                 tb.add(b);
40004             }
40005         
40006         }
40007         
40008         
40009         
40010         // disable everything...
40011         
40012         this.tb.items.each(function(item){
40013            if(item.id != editor.frameId+ '-sourceedit'){
40014                 item.disable();
40015             }
40016         });
40017         this.rendered = true;
40018         
40019         // the all the btns;
40020         editor.on('editorevent', this.updateToolbar, this);
40021         // other toolbars need to implement this..
40022         //editor.on('editmodechange', this.updateToolbar, this);
40023     },
40024     
40025     
40026     
40027     /**
40028      * Protected method that will not generally be called directly. It triggers
40029      * a toolbar update by reading the markup state of the current selection in the editor.
40030      */
40031     updateToolbar: function(){
40032
40033         if(!this.editor.activated){
40034             this.editor.onFirstFocus();
40035             return;
40036         }
40037
40038         var btns = this.tb.items.map, 
40039             doc = this.editor.doc,
40040             frameId = this.editor.frameId;
40041
40042         if(!this.disable.font && !Roo.isSafari){
40043             /*
40044             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40045             if(name != this.fontSelect.dom.value){
40046                 this.fontSelect.dom.value = name;
40047             }
40048             */
40049         }
40050         if(!this.disable.format){
40051             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40052             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40053             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40054         }
40055         if(!this.disable.alignments){
40056             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40057             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40058             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40059         }
40060         if(!Roo.isSafari && !this.disable.lists){
40061             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40062             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40063         }
40064         
40065         var ans = this.editor.getAllAncestors();
40066         if (this.formatCombo) {
40067             
40068             
40069             var store = this.formatCombo.store;
40070             this.formatCombo.setValue("");
40071             for (var i =0; i < ans.length;i++) {
40072                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40073                     // select it..
40074                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40075                     break;
40076                 }
40077             }
40078         }
40079         
40080         
40081         
40082         // hides menus... - so this cant be on a menu...
40083         Roo.menu.MenuMgr.hideAll();
40084
40085         //this.editorsyncValue();
40086     },
40087    
40088     
40089     createFontOptions : function(){
40090         var buf = [], fs = this.fontFamilies, ff, lc;
40091         for(var i = 0, len = fs.length; i< len; i++){
40092             ff = fs[i];
40093             lc = ff.toLowerCase();
40094             buf.push(
40095                 '<option value="',lc,'" style="font-family:',ff,';"',
40096                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40097                     ff,
40098                 '</option>'
40099             );
40100         }
40101         return buf.join('');
40102     },
40103     
40104     toggleSourceEdit : function(sourceEditMode){
40105         if(sourceEditMode === undefined){
40106             sourceEditMode = !this.sourceEditMode;
40107         }
40108         this.sourceEditMode = sourceEditMode === true;
40109         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40110         // just toggle the button?
40111         if(btn.pressed !== this.editor.sourceEditMode){
40112             btn.toggle(this.editor.sourceEditMode);
40113             return;
40114         }
40115         
40116         if(this.sourceEditMode){
40117             this.tb.items.each(function(item){
40118                 if(item.cmd != 'sourceedit'){
40119                     item.disable();
40120                 }
40121             });
40122           
40123         }else{
40124             if(this.initialized){
40125                 this.tb.items.each(function(item){
40126                     item.enable();
40127                 });
40128             }
40129             
40130         }
40131         // tell the editor that it's been pressed..
40132         this.editor.toggleSourceEdit(sourceEditMode);
40133        
40134     },
40135      /**
40136      * Object collection of toolbar tooltips for the buttons in the editor. The key
40137      * is the command id associated with that button and the value is a valid QuickTips object.
40138      * For example:
40139 <pre><code>
40140 {
40141     bold : {
40142         title: 'Bold (Ctrl+B)',
40143         text: 'Make the selected text bold.',
40144         cls: 'x-html-editor-tip'
40145     },
40146     italic : {
40147         title: 'Italic (Ctrl+I)',
40148         text: 'Make the selected text italic.',
40149         cls: 'x-html-editor-tip'
40150     },
40151     ...
40152 </code></pre>
40153     * @type Object
40154      */
40155     buttonTips : {
40156         bold : {
40157             title: 'Bold (Ctrl+B)',
40158             text: 'Make the selected text bold.',
40159             cls: 'x-html-editor-tip'
40160         },
40161         italic : {
40162             title: 'Italic (Ctrl+I)',
40163             text: 'Make the selected text italic.',
40164             cls: 'x-html-editor-tip'
40165         },
40166         underline : {
40167             title: 'Underline (Ctrl+U)',
40168             text: 'Underline the selected text.',
40169             cls: 'x-html-editor-tip'
40170         },
40171         increasefontsize : {
40172             title: 'Grow Text',
40173             text: 'Increase the font size.',
40174             cls: 'x-html-editor-tip'
40175         },
40176         decreasefontsize : {
40177             title: 'Shrink Text',
40178             text: 'Decrease the font size.',
40179             cls: 'x-html-editor-tip'
40180         },
40181         backcolor : {
40182             title: 'Text Highlight Color',
40183             text: 'Change the background color of the selected text.',
40184             cls: 'x-html-editor-tip'
40185         },
40186         forecolor : {
40187             title: 'Font Color',
40188             text: 'Change the color of the selected text.',
40189             cls: 'x-html-editor-tip'
40190         },
40191         justifyleft : {
40192             title: 'Align Text Left',
40193             text: 'Align text to the left.',
40194             cls: 'x-html-editor-tip'
40195         },
40196         justifycenter : {
40197             title: 'Center Text',
40198             text: 'Center text in the editor.',
40199             cls: 'x-html-editor-tip'
40200         },
40201         justifyright : {
40202             title: 'Align Text Right',
40203             text: 'Align text to the right.',
40204             cls: 'x-html-editor-tip'
40205         },
40206         insertunorderedlist : {
40207             title: 'Bullet List',
40208             text: 'Start a bulleted list.',
40209             cls: 'x-html-editor-tip'
40210         },
40211         insertorderedlist : {
40212             title: 'Numbered List',
40213             text: 'Start a numbered list.',
40214             cls: 'x-html-editor-tip'
40215         },
40216         createlink : {
40217             title: 'Hyperlink',
40218             text: 'Make the selected text a hyperlink.',
40219             cls: 'x-html-editor-tip'
40220         },
40221         sourceedit : {
40222             title: 'Source Edit',
40223             text: 'Switch to source editing mode.',
40224             cls: 'x-html-editor-tip'
40225         }
40226     },
40227     // private
40228     onDestroy : function(){
40229         if(this.rendered){
40230             
40231             this.tb.items.each(function(item){
40232                 if(item.menu){
40233                     item.menu.removeAll();
40234                     if(item.menu.el){
40235                         item.menu.el.destroy();
40236                     }
40237                 }
40238                 item.destroy();
40239             });
40240              
40241         }
40242     },
40243     onFirstFocus: function() {
40244         this.tb.items.each(function(item){
40245            item.enable();
40246         });
40247     }
40248 });
40249
40250
40251
40252
40253 // <script type="text/javascript">
40254 /*
40255  * Based on
40256  * Ext JS Library 1.1.1
40257  * Copyright(c) 2006-2007, Ext JS, LLC.
40258  *  
40259  
40260  */
40261
40262  
40263 /**
40264  * @class Roo.form.HtmlEditor.ToolbarContext
40265  * Context Toolbar
40266  * 
40267  * Usage:
40268  *
40269  new Roo.form.HtmlEditor({
40270     ....
40271     toolbars : [
40272         { xtype: 'ToolbarStandard', styles : {} }
40273         { xtype: 'ToolbarContext', disable : {} }
40274     ]
40275 })
40276
40277      
40278  * 
40279  * @config : {Object} disable List of elements to disable.. (not done yet.)
40280  * @config : {Object} styles  Map of styles available.
40281  * 
40282  */
40283
40284 Roo.form.HtmlEditor.ToolbarContext = function(config)
40285 {
40286     
40287     Roo.apply(this, config);
40288     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40289     // dont call parent... till later.
40290     this.styles = this.styles || {};
40291 }
40292 Roo.form.HtmlEditor.ToolbarContext.types = {
40293     'IMG' : {
40294         width : {
40295             title: "Width",
40296             width: 40
40297         },
40298         height:  {
40299             title: "Height",
40300             width: 40
40301         },
40302         align: {
40303             title: "Align",
40304             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40305             width : 80
40306             
40307         },
40308         border: {
40309             title: "Border",
40310             width: 40
40311         },
40312         alt: {
40313             title: "Alt",
40314             width: 120
40315         },
40316         src : {
40317             title: "Src",
40318             width: 220
40319         }
40320         
40321     },
40322     'A' : {
40323         name : {
40324             title: "Name",
40325             width: 50
40326         },
40327         href:  {
40328             title: "Href",
40329             width: 220
40330         } // border?
40331         
40332     },
40333     'TABLE' : {
40334         rows : {
40335             title: "Rows",
40336             width: 20
40337         },
40338         cols : {
40339             title: "Cols",
40340             width: 20
40341         },
40342         width : {
40343             title: "Width",
40344             width: 40
40345         },
40346         height : {
40347             title: "Height",
40348             width: 40
40349         },
40350         border : {
40351             title: "Border",
40352             width: 20
40353         }
40354     },
40355     'TD' : {
40356         width : {
40357             title: "Width",
40358             width: 40
40359         },
40360         height : {
40361             title: "Height",
40362             width: 40
40363         },   
40364         align: {
40365             title: "Align",
40366             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40367             width: 80
40368         },
40369         valign: {
40370             title: "Valign",
40371             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40372             width: 80
40373         },
40374         colspan: {
40375             title: "Colspan",
40376             width: 20
40377             
40378         }
40379     },
40380     'INPUT' : {
40381         name : {
40382             title: "name",
40383             width: 120
40384         },
40385         value : {
40386             title: "Value",
40387             width: 120
40388         },
40389         width : {
40390             title: "Width",
40391             width: 40
40392         }
40393     },
40394     'LABEL' : {
40395         'for' : {
40396             title: "For",
40397             width: 120
40398         }
40399     },
40400     'TEXTAREA' : {
40401           name : {
40402             title: "name",
40403             width: 120
40404         },
40405         rows : {
40406             title: "Rows",
40407             width: 20
40408         },
40409         cols : {
40410             title: "Cols",
40411             width: 20
40412         }
40413     },
40414     'SELECT' : {
40415         name : {
40416             title: "name",
40417             width: 120
40418         },
40419         selectoptions : {
40420             title: "Options",
40421             width: 200
40422         }
40423     },
40424     
40425     // should we really allow this??
40426     // should this just be 
40427     'BODY' : {
40428         title : {
40429             title: "title",
40430             width: 200,
40431             disabled : true
40432         }
40433     },
40434     '*' : {
40435         // empty..
40436     }
40437 };
40438
40439
40440
40441 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40442     
40443     tb: false,
40444     
40445     rendered: false,
40446     
40447     editor : false,
40448     /**
40449      * @cfg {Object} disable  List of toolbar elements to disable
40450          
40451      */
40452     disable : false,
40453     /**
40454      * @cfg {Object} styles List of styles 
40455      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40456      *
40457      * These must be defined in the page, so they get rendered correctly..
40458      * .headline { }
40459      * TD.underline { }
40460      * 
40461      */
40462     styles : false,
40463     
40464     
40465     
40466     toolbars : false,
40467     
40468     init : function(editor)
40469     {
40470         this.editor = editor;
40471         
40472         
40473         var fid = editor.frameId;
40474         var etb = this;
40475         function btn(id, toggle, handler){
40476             var xid = fid + '-'+ id ;
40477             return {
40478                 id : xid,
40479                 cmd : id,
40480                 cls : 'x-btn-icon x-edit-'+id,
40481                 enableToggle:toggle !== false,
40482                 scope: editor, // was editor...
40483                 handler:handler||editor.relayBtnCmd,
40484                 clickEvent:'mousedown',
40485                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40486                 tabIndex:-1
40487             };
40488         }
40489         // create a new element.
40490         var wdiv = editor.wrap.createChild({
40491                 tag: 'div'
40492             }, editor.wrap.dom.firstChild.nextSibling, true);
40493         
40494         // can we do this more than once??
40495         
40496          // stop form submits
40497       
40498  
40499         // disable everything...
40500         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40501         this.toolbars = {};
40502            
40503         for (var i in  ty) {
40504           
40505             this.toolbars[i] = this.buildToolbar(ty[i],i);
40506         }
40507         this.tb = this.toolbars.BODY;
40508         this.tb.el.show();
40509         this.buildFooter();
40510         this.footer.show();
40511          
40512         this.rendered = true;
40513         
40514         // the all the btns;
40515         editor.on('editorevent', this.updateToolbar, this);
40516         // other toolbars need to implement this..
40517         //editor.on('editmodechange', this.updateToolbar, this);
40518     },
40519     
40520     
40521     
40522     /**
40523      * Protected method that will not generally be called directly. It triggers
40524      * a toolbar update by reading the markup state of the current selection in the editor.
40525      */
40526     updateToolbar: function(ignore_a,ignore_b,sel){
40527
40528         
40529         if(!this.editor.activated){
40530              this.editor.onFirstFocus();
40531             return;
40532         }
40533         var updateFooter = sel ? false : true;
40534         
40535         
40536         var ans = this.editor.getAllAncestors();
40537         
40538         // pick
40539         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40540         
40541         if (!sel) { 
40542             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40543             sel = sel ? sel : this.editor.doc.body;
40544             sel = sel.tagName.length ? sel : this.editor.doc.body;
40545             
40546         }
40547         // pick a menu that exists..
40548         var tn = sel.tagName.toUpperCase();
40549         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40550         
40551         tn = sel.tagName.toUpperCase();
40552         
40553         // if current menu does not match..
40554         if (this.tb.name != tn) {
40555                 
40556             this.tb.el.hide();
40557             ///console.log("show: " + tn);
40558             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40559             this.tb.el.show();
40560             // update name
40561             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40562             
40563             
40564             // update attributes
40565             if (this.tb.fields) {
40566                 this.tb.fields.each(function(e) {
40567                    e.setValue(sel.getAttribute(e.name));
40568                 });
40569             }
40570             
40571             // update styles
40572             var st = this.tb.fields.item(0);
40573             st.store.removeAll();
40574             var cn = sel.className.split(/\s+/);
40575             
40576             var avs = [];
40577             if (this.styles['*']) {
40578                 
40579                 Roo.each(this.styles['*'], function(v) {
40580                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40581                 });
40582             }
40583             if (this.styles[tn]) { 
40584                 Roo.each(this.styles[tn], function(v) {
40585                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40586                 });
40587             }
40588             
40589             st.store.loadData(avs);
40590             st.collapse();
40591             st.setValue(cn);
40592             
40593             // flag our selected Node.
40594             this.tb.selectedNode = sel;
40595            
40596            
40597             Roo.menu.MenuMgr.hideAll();
40598
40599         }
40600         
40601         if (!updateFooter) {
40602             return;
40603         }
40604         // update the footer
40605         //
40606         var html = '';
40607         
40608         this.footerEls = ans.reverse();
40609         Roo.each(this.footerEls, function(a,i) {
40610             if (!a) { return; }
40611             html += html.length ? ' &gt; '  :  '';
40612             
40613             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40614             
40615         });
40616        
40617         // 
40618         var sz = this.footDisp.up('td').getSize();
40619         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40620         this.footDisp.dom.style.marginLeft = '5px';
40621         
40622         this.footDisp.dom.style.overflow = 'hidden';
40623         
40624         this.footDisp.dom.innerHTML = html;
40625             
40626         //this.editorsyncValue();
40627     },
40628    
40629        
40630     // private
40631     onDestroy : function(){
40632         if(this.rendered){
40633             
40634             this.tb.items.each(function(item){
40635                 if(item.menu){
40636                     item.menu.removeAll();
40637                     if(item.menu.el){
40638                         item.menu.el.destroy();
40639                     }
40640                 }
40641                 item.destroy();
40642             });
40643              
40644         }
40645     },
40646     onFirstFocus: function() {
40647         // need to do this for all the toolbars..
40648         this.tb.items.each(function(item){
40649            item.enable();
40650         });
40651     },
40652     buildToolbar: function(tlist, nm)
40653     {
40654         var editor = this.editor;
40655          // create a new element.
40656         var wdiv = editor.wrap.createChild({
40657                 tag: 'div'
40658             }, editor.wrap.dom.firstChild.nextSibling, true);
40659         
40660        
40661         var tb = new Roo.Toolbar(wdiv);
40662         // add the name..
40663         
40664         tb.add(nm+ ":&nbsp;");
40665         
40666         // styles...
40667         if (this.styles) {
40668             
40669             // this needs a multi-select checkbox...
40670             tb.addField( new Roo.form.ComboBox({
40671                 store: new Roo.data.SimpleStore({
40672                     id : 'val',
40673                     fields: ['val', 'selected'],
40674                     data : [] 
40675                 }),
40676                 name : 'className',
40677                 displayField:'val',
40678                 typeAhead: false,
40679                 mode: 'local',
40680                 editable : false,
40681                 triggerAction: 'all',
40682                 emptyText:'Select Style',
40683                 selectOnFocus:true,
40684                 width: 130,
40685                 listeners : {
40686                     'select': function(c, r, i) {
40687                         // initial support only for on class per el..
40688                         tb.selectedNode.className =  r ? r.get('val') : '';
40689                     }
40690                 }
40691     
40692             }));
40693         }
40694             
40695         
40696         
40697         for (var i in tlist) {
40698             
40699             var item = tlist[i];
40700             tb.add(item.title + ":&nbsp;");
40701             
40702             
40703             
40704             
40705             if (item.opts) {
40706                 // opts == pulldown..
40707                 tb.addField( new Roo.form.ComboBox({
40708                     store: new Roo.data.SimpleStore({
40709                         id : 'val',
40710                         fields: ['val'],
40711                         data : item.opts  
40712                     }),
40713                     name : i,
40714                     displayField:'val',
40715                     typeAhead: false,
40716                     mode: 'local',
40717                     editable : false,
40718                     triggerAction: 'all',
40719                     emptyText:'Select',
40720                     selectOnFocus:true,
40721                     width: item.width ? item.width  : 130,
40722                     listeners : {
40723                         'select': function(c, r, i) {
40724                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40725                         }
40726                     }
40727
40728                 }));
40729                 continue;
40730                     
40731                  
40732                 
40733                 tb.addField( new Roo.form.TextField({
40734                     name: i,
40735                     width: 100,
40736                     //allowBlank:false,
40737                     value: ''
40738                 }));
40739                 continue;
40740             }
40741             tb.addField( new Roo.form.TextField({
40742                 name: i,
40743                 width: item.width,
40744                 //allowBlank:true,
40745                 value: '',
40746                 listeners: {
40747                     'change' : function(f, nv, ov) {
40748                         tb.selectedNode.setAttribute(f.name, nv);
40749                     }
40750                 }
40751             }));
40752              
40753         }
40754         tb.el.on('click', function(e){
40755             e.preventDefault(); // what does this do?
40756         });
40757         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40758         tb.el.hide();
40759         tb.name = nm;
40760         // dont need to disable them... as they will get hidden
40761         return tb;
40762          
40763         
40764     },
40765     buildFooter : function()
40766     {
40767         
40768         var fel = this.editor.wrap.createChild();
40769         this.footer = new Roo.Toolbar(fel);
40770         // toolbar has scrolly on left / right?
40771         var footDisp= new Roo.Toolbar.Fill();
40772         var _t = this;
40773         this.footer.add(
40774             {
40775                 text : '&lt;',
40776                 xtype: 'Button',
40777                 handler : function() {
40778                     _t.footDisp.scrollTo('left',0,true)
40779                 }
40780             }
40781         );
40782         this.footer.add( footDisp );
40783         this.footer.add( 
40784             {
40785                 text : '&gt;',
40786                 xtype: 'Button',
40787                 handler : function() {
40788                     // no animation..
40789                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40790                 }
40791             }
40792         );
40793         var fel = Roo.get(footDisp.el);
40794         fel.addClass('x-editor-context');
40795         this.footDispWrap = fel; 
40796         this.footDispWrap.overflow  = 'hidden';
40797         
40798         this.footDisp = fel.createChild();
40799         this.footDispWrap.on('click', this.onContextClick, this)
40800         
40801         
40802     },
40803     onContextClick : function (ev,dom)
40804     {
40805         ev.preventDefault();
40806         var  cn = dom.className;
40807         Roo.log(cn);
40808         if (!cn.match(/x-ed-loc-/)) {
40809             return;
40810         }
40811         var n = cn.split('-').pop();
40812         var ans = this.footerEls;
40813         var sel = ans[n];
40814         
40815          // pick
40816         var range = this.editor.createRange();
40817         
40818         range.selectNodeContents(sel);
40819         //range.selectNode(sel);
40820         
40821         
40822         var selection = this.editor.getSelection();
40823         selection.removeAllRanges();
40824         selection.addRange(range);
40825         
40826         
40827         
40828         this.updateToolbar(null, null, sel);
40829         
40830         
40831     }
40832     
40833     
40834     
40835     
40836     
40837 });
40838
40839
40840
40841
40842
40843 /*
40844  * Based on:
40845  * Ext JS Library 1.1.1
40846  * Copyright(c) 2006-2007, Ext JS, LLC.
40847  *
40848  * Originally Released Under LGPL - original licence link has changed is not relivant.
40849  *
40850  * Fork - LGPL
40851  * <script type="text/javascript">
40852  */
40853  
40854 /**
40855  * @class Roo.form.BasicForm
40856  * @extends Roo.util.Observable
40857  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40858  * @constructor
40859  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40860  * @param {Object} config Configuration options
40861  */
40862 Roo.form.BasicForm = function(el, config){
40863     this.allItems = [];
40864     this.childForms = [];
40865     Roo.apply(this, config);
40866     /*
40867      * The Roo.form.Field items in this form.
40868      * @type MixedCollection
40869      */
40870      
40871      
40872     this.items = new Roo.util.MixedCollection(false, function(o){
40873         return o.id || (o.id = Roo.id());
40874     });
40875     this.addEvents({
40876         /**
40877          * @event beforeaction
40878          * Fires before any action is performed. Return false to cancel the action.
40879          * @param {Form} this
40880          * @param {Action} action The action to be performed
40881          */
40882         beforeaction: true,
40883         /**
40884          * @event actionfailed
40885          * Fires when an action fails.
40886          * @param {Form} this
40887          * @param {Action} action The action that failed
40888          */
40889         actionfailed : true,
40890         /**
40891          * @event actioncomplete
40892          * Fires when an action is completed.
40893          * @param {Form} this
40894          * @param {Action} action The action that completed
40895          */
40896         actioncomplete : true
40897     });
40898     if(el){
40899         this.initEl(el);
40900     }
40901     Roo.form.BasicForm.superclass.constructor.call(this);
40902 };
40903
40904 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40905     /**
40906      * @cfg {String} method
40907      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40908      */
40909     /**
40910      * @cfg {DataReader} reader
40911      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40912      * This is optional as there is built-in support for processing JSON.
40913      */
40914     /**
40915      * @cfg {DataReader} errorReader
40916      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40917      * This is completely optional as there is built-in support for processing JSON.
40918      */
40919     /**
40920      * @cfg {String} url
40921      * The URL to use for form actions if one isn't supplied in the action options.
40922      */
40923     /**
40924      * @cfg {Boolean} fileUpload
40925      * Set to true if this form is a file upload.
40926      */
40927      
40928     /**
40929      * @cfg {Object} baseParams
40930      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40931      */
40932      /**
40933      
40934     /**
40935      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40936      */
40937     timeout: 30,
40938
40939     // private
40940     activeAction : null,
40941
40942     /**
40943      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40944      * or setValues() data instead of when the form was first created.
40945      */
40946     trackResetOnLoad : false,
40947     
40948     
40949     /**
40950      * childForms - used for multi-tab forms
40951      * @type {Array}
40952      */
40953     childForms : false,
40954     
40955     /**
40956      * allItems - full list of fields.
40957      * @type {Array}
40958      */
40959     allItems : false,
40960     
40961     /**
40962      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40963      * element by passing it or its id or mask the form itself by passing in true.
40964      * @type Mixed
40965      */
40966     waitMsgTarget : false,
40967
40968     // private
40969     initEl : function(el){
40970         this.el = Roo.get(el);
40971         this.id = this.el.id || Roo.id();
40972         this.el.on('submit', this.onSubmit, this);
40973         this.el.addClass('x-form');
40974     },
40975
40976     // private
40977     onSubmit : function(e){
40978         e.stopEvent();
40979     },
40980
40981     /**
40982      * Returns true if client-side validation on the form is successful.
40983      * @return Boolean
40984      */
40985     isValid : function(){
40986         var valid = true;
40987         this.items.each(function(f){
40988            if(!f.validate()){
40989                valid = false;
40990            }
40991         });
40992         return valid;
40993     },
40994
40995     /**
40996      * Returns true if any fields in this form have changed since their original load.
40997      * @return Boolean
40998      */
40999     isDirty : function(){
41000         var dirty = false;
41001         this.items.each(function(f){
41002            if(f.isDirty()){
41003                dirty = true;
41004                return false;
41005            }
41006         });
41007         return dirty;
41008     },
41009
41010     /**
41011      * Performs a predefined action (submit or load) or custom actions you define on this form.
41012      * @param {String} actionName The name of the action type
41013      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41014      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41015      * accept other config options):
41016      * <pre>
41017 Property          Type             Description
41018 ----------------  ---------------  ----------------------------------------------------------------------------------
41019 url               String           The url for the action (defaults to the form's url)
41020 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41021 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41022 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41023                                    validate the form on the client (defaults to false)
41024      * </pre>
41025      * @return {BasicForm} this
41026      */
41027     doAction : function(action, options){
41028         if(typeof action == 'string'){
41029             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41030         }
41031         if(this.fireEvent('beforeaction', this, action) !== false){
41032             this.beforeAction(action);
41033             action.run.defer(100, action);
41034         }
41035         return this;
41036     },
41037
41038     /**
41039      * Shortcut to do a submit action.
41040      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41041      * @return {BasicForm} this
41042      */
41043     submit : function(options){
41044         this.doAction('submit', options);
41045         return this;
41046     },
41047
41048     /**
41049      * Shortcut to do a load action.
41050      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41051      * @return {BasicForm} this
41052      */
41053     load : function(options){
41054         this.doAction('load', options);
41055         return this;
41056     },
41057
41058     /**
41059      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41060      * @param {Record} record The record to edit
41061      * @return {BasicForm} this
41062      */
41063     updateRecord : function(record){
41064         record.beginEdit();
41065         var fs = record.fields;
41066         fs.each(function(f){
41067             var field = this.findField(f.name);
41068             if(field){
41069                 record.set(f.name, field.getValue());
41070             }
41071         }, this);
41072         record.endEdit();
41073         return this;
41074     },
41075
41076     /**
41077      * Loads an Roo.data.Record into this form.
41078      * @param {Record} record The record to load
41079      * @return {BasicForm} this
41080      */
41081     loadRecord : function(record){
41082         this.setValues(record.data);
41083         return this;
41084     },
41085
41086     // private
41087     beforeAction : function(action){
41088         var o = action.options;
41089         
41090        
41091         if(this.waitMsgTarget === true){
41092             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41093         }else if(this.waitMsgTarget){
41094             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41095             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41096         }else {
41097             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41098         }
41099          
41100     },
41101
41102     // private
41103     afterAction : function(action, success){
41104         this.activeAction = null;
41105         var o = action.options;
41106         
41107         if(this.waitMsgTarget === true){
41108             this.el.unmask();
41109         }else if(this.waitMsgTarget){
41110             this.waitMsgTarget.unmask();
41111         }else{
41112             Roo.MessageBox.updateProgress(1);
41113             Roo.MessageBox.hide();
41114         }
41115          
41116         if(success){
41117             if(o.reset){
41118                 this.reset();
41119             }
41120             Roo.callback(o.success, o.scope, [this, action]);
41121             this.fireEvent('actioncomplete', this, action);
41122             
41123         }else{
41124             Roo.callback(o.failure, o.scope, [this, action]);
41125             // show an error message if no failed handler is set..
41126             if (!this.hasListener('actionfailed')) {
41127                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41128             }
41129             
41130             this.fireEvent('actionfailed', this, action);
41131         }
41132         
41133     },
41134
41135     /**
41136      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41137      * @param {String} id The value to search for
41138      * @return Field
41139      */
41140     findField : function(id){
41141         var field = this.items.get(id);
41142         if(!field){
41143             this.items.each(function(f){
41144                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41145                     field = f;
41146                     return false;
41147                 }
41148             });
41149         }
41150         return field || null;
41151     },
41152
41153     /**
41154      * Add a secondary form to this one, 
41155      * Used to provide tabbed forms. One form is primary, with hidden values 
41156      * which mirror the elements from the other forms.
41157      * 
41158      * @param {Roo.form.Form} form to add.
41159      * 
41160      */
41161     addForm : function(form)
41162     {
41163        
41164         if (this.childForms.indexOf(form) > -1) {
41165             // already added..
41166             return;
41167         }
41168         this.childForms.push(form);
41169         var n = '';
41170         Roo.each(form.allItems, function (fe) {
41171             
41172             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41173             if (this.findField(n)) { // already added..
41174                 return;
41175             }
41176             var add = new Roo.form.Hidden({
41177                 name : n
41178             });
41179             add.render(this.el);
41180             
41181             this.add( add );
41182         }, this);
41183         
41184     },
41185     /**
41186      * Mark fields in this form invalid in bulk.
41187      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41188      * @return {BasicForm} this
41189      */
41190     markInvalid : function(errors){
41191         if(errors instanceof Array){
41192             for(var i = 0, len = errors.length; i < len; i++){
41193                 var fieldError = errors[i];
41194                 var f = this.findField(fieldError.id);
41195                 if(f){
41196                     f.markInvalid(fieldError.msg);
41197                 }
41198             }
41199         }else{
41200             var field, id;
41201             for(id in errors){
41202                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41203                     field.markInvalid(errors[id]);
41204                 }
41205             }
41206         }
41207         Roo.each(this.childForms || [], function (f) {
41208             f.markInvalid(errors);
41209         });
41210         
41211         return this;
41212     },
41213
41214     /**
41215      * Set values for fields in this form in bulk.
41216      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41217      * @return {BasicForm} this
41218      */
41219     setValues : function(values){
41220         if(values instanceof Array){ // array of objects
41221             for(var i = 0, len = values.length; i < len; i++){
41222                 var v = values[i];
41223                 var f = this.findField(v.id);
41224                 if(f){
41225                     f.setValue(v.value);
41226                     if(this.trackResetOnLoad){
41227                         f.originalValue = f.getValue();
41228                     }
41229                 }
41230             }
41231         }else{ // object hash
41232             var field, id;
41233             for(id in values){
41234                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41235                     
41236                     if (field.setFromData && 
41237                         field.valueField && 
41238                         field.displayField &&
41239                         // combos' with local stores can 
41240                         // be queried via setValue()
41241                         // to set their value..
41242                         (field.store && !field.store.isLocal)
41243                         ) {
41244                         // it's a combo
41245                         var sd = { };
41246                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41247                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41248                         field.setFromData(sd);
41249                         
41250                     } else {
41251                         field.setValue(values[id]);
41252                     }
41253                     
41254                     
41255                     if(this.trackResetOnLoad){
41256                         field.originalValue = field.getValue();
41257                     }
41258                 }
41259             }
41260         }
41261          
41262         Roo.each(this.childForms || [], function (f) {
41263             f.setValues(values);
41264         });
41265                 
41266         return this;
41267     },
41268
41269     /**
41270      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41271      * they are returned as an array.
41272      * @param {Boolean} asString
41273      * @return {Object}
41274      */
41275     getValues : function(asString){
41276         if (this.childForms) {
41277             // copy values from the child forms
41278             Roo.each(this.childForms, function (f) {
41279                 this.setValues(f.getValues());
41280             }, this);
41281         }
41282         
41283         
41284         
41285         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41286         if(asString === true){
41287             return fs;
41288         }
41289         return Roo.urlDecode(fs);
41290     },
41291     
41292     /**
41293      * Returns the fields in this form as an object with key/value pairs. 
41294      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41295      * @return {Object}
41296      */
41297     getFieldValues : function()
41298     {
41299         if (this.childForms) {
41300             // copy values from the child forms
41301             Roo.each(this.childForms, function (f) {
41302                 this.setValues(f.getValues());
41303             }, this);
41304         }
41305         
41306         var ret = {};
41307         this.items.each(function(f){
41308             if (!f.getName()) {
41309                 return;
41310             }
41311             var v = f.getValue();
41312             if ((typeof(v) == 'object') && f.getRawValue) {
41313                 v = f.getRawValue() ; // dates..
41314             }
41315             ret[f.getName()] = v;
41316         });
41317         
41318         return ret;
41319     },
41320
41321     /**
41322      * Clears all invalid messages in this form.
41323      * @return {BasicForm} this
41324      */
41325     clearInvalid : function(){
41326         this.items.each(function(f){
41327            f.clearInvalid();
41328         });
41329         
41330         Roo.each(this.childForms || [], function (f) {
41331             f.clearInvalid();
41332         });
41333         
41334         
41335         return this;
41336     },
41337
41338     /**
41339      * Resets this form.
41340      * @return {BasicForm} this
41341      */
41342     reset : function(){
41343         this.items.each(function(f){
41344             f.reset();
41345         });
41346         
41347         Roo.each(this.childForms || [], function (f) {
41348             f.reset();
41349         });
41350        
41351         
41352         return this;
41353     },
41354
41355     /**
41356      * Add Roo.form components to this form.
41357      * @param {Field} field1
41358      * @param {Field} field2 (optional)
41359      * @param {Field} etc (optional)
41360      * @return {BasicForm} this
41361      */
41362     add : function(){
41363         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41364         return this;
41365     },
41366
41367
41368     /**
41369      * Removes a field from the items collection (does NOT remove its markup).
41370      * @param {Field} field
41371      * @return {BasicForm} this
41372      */
41373     remove : function(field){
41374         this.items.remove(field);
41375         return this;
41376     },
41377
41378     /**
41379      * Looks at the fields in this form, checks them for an id attribute,
41380      * and calls applyTo on the existing dom element with that id.
41381      * @return {BasicForm} this
41382      */
41383     render : function(){
41384         this.items.each(function(f){
41385             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41386                 f.applyTo(f.id);
41387             }
41388         });
41389         return this;
41390     },
41391
41392     /**
41393      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41394      * @param {Object} values
41395      * @return {BasicForm} this
41396      */
41397     applyToFields : function(o){
41398         this.items.each(function(f){
41399            Roo.apply(f, o);
41400         });
41401         return this;
41402     },
41403
41404     /**
41405      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41406      * @param {Object} values
41407      * @return {BasicForm} this
41408      */
41409     applyIfToFields : function(o){
41410         this.items.each(function(f){
41411            Roo.applyIf(f, o);
41412         });
41413         return this;
41414     }
41415 });
41416
41417 // back compat
41418 Roo.BasicForm = Roo.form.BasicForm;/*
41419  * Based on:
41420  * Ext JS Library 1.1.1
41421  * Copyright(c) 2006-2007, Ext JS, LLC.
41422  *
41423  * Originally Released Under LGPL - original licence link has changed is not relivant.
41424  *
41425  * Fork - LGPL
41426  * <script type="text/javascript">
41427  */
41428
41429 /**
41430  * @class Roo.form.Form
41431  * @extends Roo.form.BasicForm
41432  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41433  * @constructor
41434  * @param {Object} config Configuration options
41435  */
41436 Roo.form.Form = function(config){
41437     var xitems =  [];
41438     if (config.items) {
41439         xitems = config.items;
41440         delete config.items;
41441     }
41442    
41443     
41444     Roo.form.Form.superclass.constructor.call(this, null, config);
41445     this.url = this.url || this.action;
41446     if(!this.root){
41447         this.root = new Roo.form.Layout(Roo.applyIf({
41448             id: Roo.id()
41449         }, config));
41450     }
41451     this.active = this.root;
41452     /**
41453      * Array of all the buttons that have been added to this form via {@link addButton}
41454      * @type Array
41455      */
41456     this.buttons = [];
41457     this.allItems = [];
41458     this.addEvents({
41459         /**
41460          * @event clientvalidation
41461          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41462          * @param {Form} this
41463          * @param {Boolean} valid true if the form has passed client-side validation
41464          */
41465         clientvalidation: true,
41466         /**
41467          * @event rendered
41468          * Fires when the form is rendered
41469          * @param {Roo.form.Form} form
41470          */
41471         rendered : true
41472     });
41473     
41474     if (this.progressUrl) {
41475             // push a hidden field onto the list of fields..
41476             this.addxtype( {
41477                     xns: Roo.form, 
41478                     xtype : 'Hidden', 
41479                     name : 'UPLOAD_IDENTIFIER' 
41480             });
41481         }
41482         
41483     
41484     Roo.each(xitems, this.addxtype, this);
41485     
41486     
41487     
41488 };
41489
41490 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41491     /**
41492      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41493      */
41494     /**
41495      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41496      */
41497     /**
41498      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41499      */
41500     buttonAlign:'center',
41501
41502     /**
41503      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41504      */
41505     minButtonWidth:75,
41506
41507     /**
41508      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41509      * This property cascades to child containers if not set.
41510      */
41511     labelAlign:'left',
41512
41513     /**
41514      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41515      * fires a looping event with that state. This is required to bind buttons to the valid
41516      * state using the config value formBind:true on the button.
41517      */
41518     monitorValid : false,
41519
41520     /**
41521      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41522      */
41523     monitorPoll : 200,
41524     
41525     /**
41526      * @cfg {String} progressUrl - Url to return progress data 
41527      */
41528     
41529     progressUrl : false,
41530   
41531     /**
41532      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41533      * fields are added and the column is closed. If no fields are passed the column remains open
41534      * until end() is called.
41535      * @param {Object} config The config to pass to the column
41536      * @param {Field} field1 (optional)
41537      * @param {Field} field2 (optional)
41538      * @param {Field} etc (optional)
41539      * @return Column The column container object
41540      */
41541     column : function(c){
41542         var col = new Roo.form.Column(c);
41543         this.start(col);
41544         if(arguments.length > 1){ // duplicate code required because of Opera
41545             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41546             this.end();
41547         }
41548         return col;
41549     },
41550
41551     /**
41552      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41553      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41554      * until end() is called.
41555      * @param {Object} config The config to pass to the fieldset
41556      * @param {Field} field1 (optional)
41557      * @param {Field} field2 (optional)
41558      * @param {Field} etc (optional)
41559      * @return FieldSet The fieldset container object
41560      */
41561     fieldset : function(c){
41562         var fs = new Roo.form.FieldSet(c);
41563         this.start(fs);
41564         if(arguments.length > 1){ // duplicate code required because of Opera
41565             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41566             this.end();
41567         }
41568         return fs;
41569     },
41570
41571     /**
41572      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41573      * fields are added and the container is closed. If no fields are passed the container remains open
41574      * until end() is called.
41575      * @param {Object} config The config to pass to the Layout
41576      * @param {Field} field1 (optional)
41577      * @param {Field} field2 (optional)
41578      * @param {Field} etc (optional)
41579      * @return Layout The container object
41580      */
41581     container : function(c){
41582         var l = new Roo.form.Layout(c);
41583         this.start(l);
41584         if(arguments.length > 1){ // duplicate code required because of Opera
41585             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41586             this.end();
41587         }
41588         return l;
41589     },
41590
41591     /**
41592      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41593      * @param {Object} container A Roo.form.Layout or subclass of Layout
41594      * @return {Form} this
41595      */
41596     start : function(c){
41597         // cascade label info
41598         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41599         this.active.stack.push(c);
41600         c.ownerCt = this.active;
41601         this.active = c;
41602         return this;
41603     },
41604
41605     /**
41606      * Closes the current open container
41607      * @return {Form} this
41608      */
41609     end : function(){
41610         if(this.active == this.root){
41611             return this;
41612         }
41613         this.active = this.active.ownerCt;
41614         return this;
41615     },
41616
41617     /**
41618      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41619      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41620      * as the label of the field.
41621      * @param {Field} field1
41622      * @param {Field} field2 (optional)
41623      * @param {Field} etc. (optional)
41624      * @return {Form} this
41625      */
41626     add : function(){
41627         this.active.stack.push.apply(this.active.stack, arguments);
41628         this.allItems.push.apply(this.allItems,arguments);
41629         var r = [];
41630         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41631             if(a[i].isFormField){
41632                 r.push(a[i]);
41633             }
41634         }
41635         if(r.length > 0){
41636             Roo.form.Form.superclass.add.apply(this, r);
41637         }
41638         return this;
41639     },
41640     
41641
41642     
41643     
41644     
41645      /**
41646      * Find any element that has been added to a form, using it's ID or name
41647      * This can include framesets, columns etc. along with regular fields..
41648      * @param {String} id - id or name to find.
41649      
41650      * @return {Element} e - or false if nothing found.
41651      */
41652     findbyId : function(id)
41653     {
41654         var ret = false;
41655         if (!id) {
41656             return ret;
41657         }
41658         Roo.each(this.allItems, function(f){
41659             if (f.id == id || f.name == id ){
41660                 ret = f;
41661                 return false;
41662             }
41663         });
41664         return ret;
41665     },
41666
41667     
41668     
41669     /**
41670      * Render this form into the passed container. This should only be called once!
41671      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41672      * @return {Form} this
41673      */
41674     render : function(ct)
41675     {
41676         
41677         
41678         
41679         ct = Roo.get(ct);
41680         var o = this.autoCreate || {
41681             tag: 'form',
41682             method : this.method || 'POST',
41683             id : this.id || Roo.id()
41684         };
41685         this.initEl(ct.createChild(o));
41686
41687         this.root.render(this.el);
41688         
41689        
41690              
41691         this.items.each(function(f){
41692             f.render('x-form-el-'+f.id);
41693         });
41694
41695         if(this.buttons.length > 0){
41696             // tables are required to maintain order and for correct IE layout
41697             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41698                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41699                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41700             }}, null, true);
41701             var tr = tb.getElementsByTagName('tr')[0];
41702             for(var i = 0, len = this.buttons.length; i < len; i++) {
41703                 var b = this.buttons[i];
41704                 var td = document.createElement('td');
41705                 td.className = 'x-form-btn-td';
41706                 b.render(tr.appendChild(td));
41707             }
41708         }
41709         if(this.monitorValid){ // initialize after render
41710             this.startMonitoring();
41711         }
41712         this.fireEvent('rendered', this);
41713         return this;
41714     },
41715
41716     /**
41717      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41718      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41719      * object or a valid Roo.DomHelper element config
41720      * @param {Function} handler The function called when the button is clicked
41721      * @param {Object} scope (optional) The scope of the handler function
41722      * @return {Roo.Button}
41723      */
41724     addButton : function(config, handler, scope){
41725         var bc = {
41726             handler: handler,
41727             scope: scope,
41728             minWidth: this.minButtonWidth,
41729             hideParent:true
41730         };
41731         if(typeof config == "string"){
41732             bc.text = config;
41733         }else{
41734             Roo.apply(bc, config);
41735         }
41736         var btn = new Roo.Button(null, bc);
41737         this.buttons.push(btn);
41738         return btn;
41739     },
41740
41741      /**
41742      * Adds a series of form elements (using the xtype property as the factory method.
41743      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41744      * @param {Object} config 
41745      */
41746     
41747     addxtype : function()
41748     {
41749         var ar = Array.prototype.slice.call(arguments, 0);
41750         var ret = false;
41751         for(var i = 0; i < ar.length; i++) {
41752             if (!ar[i]) {
41753                 continue; // skip -- if this happends something invalid got sent, we 
41754                 // should ignore it, as basically that interface element will not show up
41755                 // and that should be pretty obvious!!
41756             }
41757             
41758             if (Roo.form[ar[i].xtype]) {
41759                 ar[i].form = this;
41760                 var fe = Roo.factory(ar[i], Roo.form);
41761                 if (!ret) {
41762                     ret = fe;
41763                 }
41764                 fe.form = this;
41765                 if (fe.store) {
41766                     fe.store.form = this;
41767                 }
41768                 if (fe.isLayout) {  
41769                          
41770                     this.start(fe);
41771                     this.allItems.push(fe);
41772                     if (fe.items && fe.addxtype) {
41773                         fe.addxtype.apply(fe, fe.items);
41774                         delete fe.items;
41775                     }
41776                      this.end();
41777                     continue;
41778                 }
41779                 
41780                 
41781                  
41782                 this.add(fe);
41783               //  console.log('adding ' + ar[i].xtype);
41784             }
41785             if (ar[i].xtype == 'Button') {  
41786                 //console.log('adding button');
41787                 //console.log(ar[i]);
41788                 this.addButton(ar[i]);
41789                 this.allItems.push(fe);
41790                 continue;
41791             }
41792             
41793             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41794                 alert('end is not supported on xtype any more, use items');
41795             //    this.end();
41796             //    //console.log('adding end');
41797             }
41798             
41799         }
41800         return ret;
41801     },
41802     
41803     /**
41804      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41805      * option "monitorValid"
41806      */
41807     startMonitoring : function(){
41808         if(!this.bound){
41809             this.bound = true;
41810             Roo.TaskMgr.start({
41811                 run : this.bindHandler,
41812                 interval : this.monitorPoll || 200,
41813                 scope: this
41814             });
41815         }
41816     },
41817
41818     /**
41819      * Stops monitoring of the valid state of this form
41820      */
41821     stopMonitoring : function(){
41822         this.bound = false;
41823     },
41824
41825     // private
41826     bindHandler : function(){
41827         if(!this.bound){
41828             return false; // stops binding
41829         }
41830         var valid = true;
41831         this.items.each(function(f){
41832             if(!f.isValid(true)){
41833                 valid = false;
41834                 return false;
41835             }
41836         });
41837         for(var i = 0, len = this.buttons.length; i < len; i++){
41838             var btn = this.buttons[i];
41839             if(btn.formBind === true && btn.disabled === valid){
41840                 btn.setDisabled(!valid);
41841             }
41842         }
41843         this.fireEvent('clientvalidation', this, valid);
41844     }
41845     
41846     
41847     
41848     
41849     
41850     
41851     
41852     
41853 });
41854
41855
41856 // back compat
41857 Roo.Form = Roo.form.Form;
41858 /*
41859  * Based on:
41860  * Ext JS Library 1.1.1
41861  * Copyright(c) 2006-2007, Ext JS, LLC.
41862  *
41863  * Originally Released Under LGPL - original licence link has changed is not relivant.
41864  *
41865  * Fork - LGPL
41866  * <script type="text/javascript">
41867  */
41868  
41869  /**
41870  * @class Roo.form.Action
41871  * Internal Class used to handle form actions
41872  * @constructor
41873  * @param {Roo.form.BasicForm} el The form element or its id
41874  * @param {Object} config Configuration options
41875  */
41876  
41877  
41878 // define the action interface
41879 Roo.form.Action = function(form, options){
41880     this.form = form;
41881     this.options = options || {};
41882 };
41883 /**
41884  * Client Validation Failed
41885  * @const 
41886  */
41887 Roo.form.Action.CLIENT_INVALID = 'client';
41888 /**
41889  * Server Validation Failed
41890  * @const 
41891  */
41892  Roo.form.Action.SERVER_INVALID = 'server';
41893  /**
41894  * Connect to Server Failed
41895  * @const 
41896  */
41897 Roo.form.Action.CONNECT_FAILURE = 'connect';
41898 /**
41899  * Reading Data from Server Failed
41900  * @const 
41901  */
41902 Roo.form.Action.LOAD_FAILURE = 'load';
41903
41904 Roo.form.Action.prototype = {
41905     type : 'default',
41906     failureType : undefined,
41907     response : undefined,
41908     result : undefined,
41909
41910     // interface method
41911     run : function(options){
41912
41913     },
41914
41915     // interface method
41916     success : function(response){
41917
41918     },
41919
41920     // interface method
41921     handleResponse : function(response){
41922
41923     },
41924
41925     // default connection failure
41926     failure : function(response){
41927         
41928         this.response = response;
41929         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41930         this.form.afterAction(this, false);
41931     },
41932
41933     processResponse : function(response){
41934         this.response = response;
41935         if(!response.responseText){
41936             return true;
41937         }
41938         this.result = this.handleResponse(response);
41939         return this.result;
41940     },
41941
41942     // utility functions used internally
41943     getUrl : function(appendParams){
41944         var url = this.options.url || this.form.url || this.form.el.dom.action;
41945         if(appendParams){
41946             var p = this.getParams();
41947             if(p){
41948                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41949             }
41950         }
41951         return url;
41952     },
41953
41954     getMethod : function(){
41955         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41956     },
41957
41958     getParams : function(){
41959         var bp = this.form.baseParams;
41960         var p = this.options.params;
41961         if(p){
41962             if(typeof p == "object"){
41963                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41964             }else if(typeof p == 'string' && bp){
41965                 p += '&' + Roo.urlEncode(bp);
41966             }
41967         }else if(bp){
41968             p = Roo.urlEncode(bp);
41969         }
41970         return p;
41971     },
41972
41973     createCallback : function(){
41974         return {
41975             success: this.success,
41976             failure: this.failure,
41977             scope: this,
41978             timeout: (this.form.timeout*1000),
41979             upload: this.form.fileUpload ? this.success : undefined
41980         };
41981     }
41982 };
41983
41984 Roo.form.Action.Submit = function(form, options){
41985     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41986 };
41987
41988 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41989     type : 'submit',
41990
41991     haveProgress : false,
41992     uploadComplete : false,
41993     
41994     // uploadProgress indicator.
41995     uploadProgress : function()
41996     {
41997         if (!this.form.progressUrl) {
41998             return;
41999         }
42000         
42001         if (!this.haveProgress) {
42002             Roo.MessageBox.progress("Uploading", "Uploading");
42003         }
42004         if (this.uploadComplete) {
42005            Roo.MessageBox.hide();
42006            return;
42007         }
42008         
42009         this.haveProgress = true;
42010    
42011         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42012         
42013         var c = new Roo.data.Connection();
42014         c.request({
42015             url : this.form.progressUrl,
42016             params: {
42017                 id : uid
42018             },
42019             method: 'GET',
42020             success : function(req){
42021                //console.log(data);
42022                 var rdata = false;
42023                 var edata;
42024                 try  {
42025                    rdata = Roo.decode(req.responseText)
42026                 } catch (e) {
42027                     Roo.log("Invalid data from server..");
42028                     Roo.log(edata);
42029                     return;
42030                 }
42031                 if (!rdata || !rdata.success) {
42032                     Roo.log(rdata);
42033                     return;
42034                 }
42035                 var data = rdata.data;
42036                 
42037                 if (this.uploadComplete) {
42038                    Roo.MessageBox.hide();
42039                    return;
42040                 }
42041                    
42042                 if (data){
42043                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42044                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42045                     );
42046                 }
42047                 this.uploadProgress.defer(2000,this);
42048             },
42049        
42050             failure: function(data) {
42051                 Roo.log('progress url failed ');
42052                 Roo.log(data);
42053             },
42054             scope : this
42055         });
42056            
42057     },
42058     
42059     
42060     run : function()
42061     {
42062         // run get Values on the form, so it syncs any secondary forms.
42063         this.form.getValues();
42064         
42065         var o = this.options;
42066         var method = this.getMethod();
42067         var isPost = method == 'POST';
42068         if(o.clientValidation === false || this.form.isValid()){
42069             
42070             if (this.form.progressUrl) {
42071                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42072                     (new Date() * 1) + '' + Math.random());
42073                     
42074             } 
42075             
42076             
42077             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42078                 form:this.form.el.dom,
42079                 url:this.getUrl(!isPost),
42080                 method: method,
42081                 params:isPost ? this.getParams() : null,
42082                 isUpload: this.form.fileUpload
42083             }));
42084             
42085             this.uploadProgress();
42086
42087         }else if (o.clientValidation !== false){ // client validation failed
42088             this.failureType = Roo.form.Action.CLIENT_INVALID;
42089             this.form.afterAction(this, false);
42090         }
42091     },
42092
42093     success : function(response)
42094     {
42095         this.uploadComplete= true;
42096         if (this.haveProgress) {
42097             Roo.MessageBox.hide();
42098         }
42099         
42100         
42101         var result = this.processResponse(response);
42102         if(result === true || result.success){
42103             this.form.afterAction(this, true);
42104             return;
42105         }
42106         if(result.errors){
42107             this.form.markInvalid(result.errors);
42108             this.failureType = Roo.form.Action.SERVER_INVALID;
42109         }
42110         this.form.afterAction(this, false);
42111     },
42112     failure : function(response)
42113     {
42114         this.uploadComplete= true;
42115         if (this.haveProgress) {
42116             Roo.MessageBox.hide();
42117         }
42118         
42119         
42120         this.response = response;
42121         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42122         this.form.afterAction(this, false);
42123     },
42124     
42125     handleResponse : function(response){
42126         if(this.form.errorReader){
42127             var rs = this.form.errorReader.read(response);
42128             var errors = [];
42129             if(rs.records){
42130                 for(var i = 0, len = rs.records.length; i < len; i++) {
42131                     var r = rs.records[i];
42132                     errors[i] = r.data;
42133                 }
42134             }
42135             if(errors.length < 1){
42136                 errors = null;
42137             }
42138             return {
42139                 success : rs.success,
42140                 errors : errors
42141             };
42142         }
42143         var ret = false;
42144         try {
42145             ret = Roo.decode(response.responseText);
42146         } catch (e) {
42147             ret = {
42148                 success: false,
42149                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42150                 errors : []
42151             };
42152         }
42153         return ret;
42154         
42155     }
42156 });
42157
42158
42159 Roo.form.Action.Load = function(form, options){
42160     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42161     this.reader = this.form.reader;
42162 };
42163
42164 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42165     type : 'load',
42166
42167     run : function(){
42168         
42169         Roo.Ajax.request(Roo.apply(
42170                 this.createCallback(), {
42171                     method:this.getMethod(),
42172                     url:this.getUrl(false),
42173                     params:this.getParams()
42174         }));
42175     },
42176
42177     success : function(response){
42178         
42179         var result = this.processResponse(response);
42180         if(result === true || !result.success || !result.data){
42181             this.failureType = Roo.form.Action.LOAD_FAILURE;
42182             this.form.afterAction(this, false);
42183             return;
42184         }
42185         this.form.clearInvalid();
42186         this.form.setValues(result.data);
42187         this.form.afterAction(this, true);
42188     },
42189
42190     handleResponse : function(response){
42191         if(this.form.reader){
42192             var rs = this.form.reader.read(response);
42193             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42194             return {
42195                 success : rs.success,
42196                 data : data
42197             };
42198         }
42199         return Roo.decode(response.responseText);
42200     }
42201 });
42202
42203 Roo.form.Action.ACTION_TYPES = {
42204     'load' : Roo.form.Action.Load,
42205     'submit' : Roo.form.Action.Submit
42206 };/*
42207  * Based on:
42208  * Ext JS Library 1.1.1
42209  * Copyright(c) 2006-2007, Ext JS, LLC.
42210  *
42211  * Originally Released Under LGPL - original licence link has changed is not relivant.
42212  *
42213  * Fork - LGPL
42214  * <script type="text/javascript">
42215  */
42216  
42217 /**
42218  * @class Roo.form.Layout
42219  * @extends Roo.Component
42220  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42221  * @constructor
42222  * @param {Object} config Configuration options
42223  */
42224 Roo.form.Layout = function(config){
42225     var xitems = [];
42226     if (config.items) {
42227         xitems = config.items;
42228         delete config.items;
42229     }
42230     Roo.form.Layout.superclass.constructor.call(this, config);
42231     this.stack = [];
42232     Roo.each(xitems, this.addxtype, this);
42233      
42234 };
42235
42236 Roo.extend(Roo.form.Layout, Roo.Component, {
42237     /**
42238      * @cfg {String/Object} autoCreate
42239      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42240      */
42241     /**
42242      * @cfg {String/Object/Function} style
42243      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42244      * a function which returns such a specification.
42245      */
42246     /**
42247      * @cfg {String} labelAlign
42248      * Valid values are "left," "top" and "right" (defaults to "left")
42249      */
42250     /**
42251      * @cfg {Number} labelWidth
42252      * Fixed width in pixels of all field labels (defaults to undefined)
42253      */
42254     /**
42255      * @cfg {Boolean} clear
42256      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42257      */
42258     clear : true,
42259     /**
42260      * @cfg {String} labelSeparator
42261      * The separator to use after field labels (defaults to ':')
42262      */
42263     labelSeparator : ':',
42264     /**
42265      * @cfg {Boolean} hideLabels
42266      * True to suppress the display of field labels in this layout (defaults to false)
42267      */
42268     hideLabels : false,
42269
42270     // private
42271     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42272     
42273     isLayout : true,
42274     
42275     // private
42276     onRender : function(ct, position){
42277         if(this.el){ // from markup
42278             this.el = Roo.get(this.el);
42279         }else {  // generate
42280             var cfg = this.getAutoCreate();
42281             this.el = ct.createChild(cfg, position);
42282         }
42283         if(this.style){
42284             this.el.applyStyles(this.style);
42285         }
42286         if(this.labelAlign){
42287             this.el.addClass('x-form-label-'+this.labelAlign);
42288         }
42289         if(this.hideLabels){
42290             this.labelStyle = "display:none";
42291             this.elementStyle = "padding-left:0;";
42292         }else{
42293             if(typeof this.labelWidth == 'number'){
42294                 this.labelStyle = "width:"+this.labelWidth+"px;";
42295                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42296             }
42297             if(this.labelAlign == 'top'){
42298                 this.labelStyle = "width:auto;";
42299                 this.elementStyle = "padding-left:0;";
42300             }
42301         }
42302         var stack = this.stack;
42303         var slen = stack.length;
42304         if(slen > 0){
42305             if(!this.fieldTpl){
42306                 var t = new Roo.Template(
42307                     '<div class="x-form-item {5}">',
42308                         '<label for="{0}" style="{2}">{1}{4}</label>',
42309                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42310                         '</div>',
42311                     '</div><div class="x-form-clear-left"></div>'
42312                 );
42313                 t.disableFormats = true;
42314                 t.compile();
42315                 Roo.form.Layout.prototype.fieldTpl = t;
42316             }
42317             for(var i = 0; i < slen; i++) {
42318                 if(stack[i].isFormField){
42319                     this.renderField(stack[i]);
42320                 }else{
42321                     this.renderComponent(stack[i]);
42322                 }
42323             }
42324         }
42325         if(this.clear){
42326             this.el.createChild({cls:'x-form-clear'});
42327         }
42328     },
42329
42330     // private
42331     renderField : function(f){
42332         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42333                f.id, //0
42334                f.fieldLabel, //1
42335                f.labelStyle||this.labelStyle||'', //2
42336                this.elementStyle||'', //3
42337                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42338                f.itemCls||this.itemCls||''  //5
42339        ], true).getPrevSibling());
42340     },
42341
42342     // private
42343     renderComponent : function(c){
42344         c.render(c.isLayout ? this.el : this.el.createChild());    
42345     },
42346     /**
42347      * Adds a object form elements (using the xtype property as the factory method.)
42348      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42349      * @param {Object} config 
42350      */
42351     addxtype : function(o)
42352     {
42353         // create the lement.
42354         o.form = this.form;
42355         var fe = Roo.factory(o, Roo.form);
42356         this.form.allItems.push(fe);
42357         this.stack.push(fe);
42358         
42359         if (fe.isFormField) {
42360             this.form.items.add(fe);
42361         }
42362          
42363         return fe;
42364     }
42365 });
42366
42367 /**
42368  * @class Roo.form.Column
42369  * @extends Roo.form.Layout
42370  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42371  * @constructor
42372  * @param {Object} config Configuration options
42373  */
42374 Roo.form.Column = function(config){
42375     Roo.form.Column.superclass.constructor.call(this, config);
42376 };
42377
42378 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42379     /**
42380      * @cfg {Number/String} width
42381      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42382      */
42383     /**
42384      * @cfg {String/Object} autoCreate
42385      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42386      */
42387
42388     // private
42389     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42390
42391     // private
42392     onRender : function(ct, position){
42393         Roo.form.Column.superclass.onRender.call(this, ct, position);
42394         if(this.width){
42395             this.el.setWidth(this.width);
42396         }
42397     }
42398 });
42399
42400
42401 /**
42402  * @class Roo.form.Row
42403  * @extends Roo.form.Layout
42404  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42405  * @constructor
42406  * @param {Object} config Configuration options
42407  */
42408
42409  
42410 Roo.form.Row = function(config){
42411     Roo.form.Row.superclass.constructor.call(this, config);
42412 };
42413  
42414 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42415       /**
42416      * @cfg {Number/String} width
42417      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42418      */
42419     /**
42420      * @cfg {Number/String} height
42421      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42422      */
42423     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42424     
42425     padWidth : 20,
42426     // private
42427     onRender : function(ct, position){
42428         //console.log('row render');
42429         if(!this.rowTpl){
42430             var t = new Roo.Template(
42431                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42432                     '<label for="{0}" style="{2}">{1}{4}</label>',
42433                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42434                     '</div>',
42435                 '</div>'
42436             );
42437             t.disableFormats = true;
42438             t.compile();
42439             Roo.form.Layout.prototype.rowTpl = t;
42440         }
42441         this.fieldTpl = this.rowTpl;
42442         
42443         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42444         var labelWidth = 100;
42445         
42446         if ((this.labelAlign != 'top')) {
42447             if (typeof this.labelWidth == 'number') {
42448                 labelWidth = this.labelWidth
42449             }
42450             this.padWidth =  20 + labelWidth;
42451             
42452         }
42453         
42454         Roo.form.Column.superclass.onRender.call(this, ct, position);
42455         if(this.width){
42456             this.el.setWidth(this.width);
42457         }
42458         if(this.height){
42459             this.el.setHeight(this.height);
42460         }
42461     },
42462     
42463     // private
42464     renderField : function(f){
42465         f.fieldEl = this.fieldTpl.append(this.el, [
42466                f.id, f.fieldLabel,
42467                f.labelStyle||this.labelStyle||'',
42468                this.elementStyle||'',
42469                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42470                f.itemCls||this.itemCls||'',
42471                f.width ? f.width + this.padWidth : 160 + this.padWidth
42472        ],true);
42473     }
42474 });
42475  
42476
42477 /**
42478  * @class Roo.form.FieldSet
42479  * @extends Roo.form.Layout
42480  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42481  * @constructor
42482  * @param {Object} config Configuration options
42483  */
42484 Roo.form.FieldSet = function(config){
42485     Roo.form.FieldSet.superclass.constructor.call(this, config);
42486 };
42487
42488 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42489     /**
42490      * @cfg {String} legend
42491      * The text to display as the legend for the FieldSet (defaults to '')
42492      */
42493     /**
42494      * @cfg {String/Object} autoCreate
42495      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42496      */
42497
42498     // private
42499     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42500
42501     // private
42502     onRender : function(ct, position){
42503         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42504         if(this.legend){
42505             this.setLegend(this.legend);
42506         }
42507     },
42508
42509     // private
42510     setLegend : function(text){
42511         if(this.rendered){
42512             this.el.child('legend').update(text);
42513         }
42514     }
42515 });/*
42516  * Based on:
42517  * Ext JS Library 1.1.1
42518  * Copyright(c) 2006-2007, Ext JS, LLC.
42519  *
42520  * Originally Released Under LGPL - original licence link has changed is not relivant.
42521  *
42522  * Fork - LGPL
42523  * <script type="text/javascript">
42524  */
42525 /**
42526  * @class Roo.form.VTypes
42527  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42528  * @singleton
42529  */
42530 Roo.form.VTypes = function(){
42531     // closure these in so they are only created once.
42532     var alpha = /^[a-zA-Z_]+$/;
42533     var alphanum = /^[a-zA-Z0-9_]+$/;
42534     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42535     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42536
42537     // All these messages and functions are configurable
42538     return {
42539         /**
42540          * The function used to validate email addresses
42541          * @param {String} value The email address
42542          */
42543         'email' : function(v){
42544             return email.test(v);
42545         },
42546         /**
42547          * The error text to display when the email validation function returns false
42548          * @type String
42549          */
42550         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42551         /**
42552          * The keystroke filter mask to be applied on email input
42553          * @type RegExp
42554          */
42555         'emailMask' : /[a-z0-9_\.\-@]/i,
42556
42557         /**
42558          * The function used to validate URLs
42559          * @param {String} value The URL
42560          */
42561         'url' : function(v){
42562             return url.test(v);
42563         },
42564         /**
42565          * The error text to display when the url validation function returns false
42566          * @type String
42567          */
42568         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42569         
42570         /**
42571          * The function used to validate alpha values
42572          * @param {String} value The value
42573          */
42574         'alpha' : function(v){
42575             return alpha.test(v);
42576         },
42577         /**
42578          * The error text to display when the alpha validation function returns false
42579          * @type String
42580          */
42581         'alphaText' : 'This field should only contain letters and _',
42582         /**
42583          * The keystroke filter mask to be applied on alpha input
42584          * @type RegExp
42585          */
42586         'alphaMask' : /[a-z_]/i,
42587
42588         /**
42589          * The function used to validate alphanumeric values
42590          * @param {String} value The value
42591          */
42592         'alphanum' : function(v){
42593             return alphanum.test(v);
42594         },
42595         /**
42596          * The error text to display when the alphanumeric validation function returns false
42597          * @type String
42598          */
42599         'alphanumText' : 'This field should only contain letters, numbers and _',
42600         /**
42601          * The keystroke filter mask to be applied on alphanumeric input
42602          * @type RegExp
42603          */
42604         'alphanumMask' : /[a-z0-9_]/i
42605     };
42606 }();//<script type="text/javascript">
42607
42608 /**
42609  * @class Roo.form.FCKeditor
42610  * @extends Roo.form.TextArea
42611  * Wrapper around the FCKEditor http://www.fckeditor.net
42612  * @constructor
42613  * Creates a new FCKeditor
42614  * @param {Object} config Configuration options
42615  */
42616 Roo.form.FCKeditor = function(config){
42617     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42618     this.addEvents({
42619          /**
42620          * @event editorinit
42621          * Fired when the editor is initialized - you can add extra handlers here..
42622          * @param {FCKeditor} this
42623          * @param {Object} the FCK object.
42624          */
42625         editorinit : true
42626     });
42627     
42628     
42629 };
42630 Roo.form.FCKeditor.editors = { };
42631 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42632 {
42633     //defaultAutoCreate : {
42634     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42635     //},
42636     // private
42637     /**
42638      * @cfg {Object} fck options - see fck manual for details.
42639      */
42640     fckconfig : false,
42641     
42642     /**
42643      * @cfg {Object} fck toolbar set (Basic or Default)
42644      */
42645     toolbarSet : 'Basic',
42646     /**
42647      * @cfg {Object} fck BasePath
42648      */ 
42649     basePath : '/fckeditor/',
42650     
42651     
42652     frame : false,
42653     
42654     value : '',
42655     
42656    
42657     onRender : function(ct, position)
42658     {
42659         if(!this.el){
42660             this.defaultAutoCreate = {
42661                 tag: "textarea",
42662                 style:"width:300px;height:60px;",
42663                 autocomplete: "off"
42664             };
42665         }
42666         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42667         /*
42668         if(this.grow){
42669             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42670             if(this.preventScrollbars){
42671                 this.el.setStyle("overflow", "hidden");
42672             }
42673             this.el.setHeight(this.growMin);
42674         }
42675         */
42676         //console.log('onrender' + this.getId() );
42677         Roo.form.FCKeditor.editors[this.getId()] = this;
42678          
42679
42680         this.replaceTextarea() ;
42681         
42682     },
42683     
42684     getEditor : function() {
42685         return this.fckEditor;
42686     },
42687     /**
42688      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42689      * @param {Mixed} value The value to set
42690      */
42691     
42692     
42693     setValue : function(value)
42694     {
42695         //console.log('setValue: ' + value);
42696         
42697         if(typeof(value) == 'undefined') { // not sure why this is happending...
42698             return;
42699         }
42700         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42701         
42702         //if(!this.el || !this.getEditor()) {
42703         //    this.value = value;
42704             //this.setValue.defer(100,this,[value]);    
42705         //    return;
42706         //} 
42707         
42708         if(!this.getEditor()) {
42709             return;
42710         }
42711         
42712         this.getEditor().SetData(value);
42713         
42714         //
42715
42716     },
42717
42718     /**
42719      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42720      * @return {Mixed} value The field value
42721      */
42722     getValue : function()
42723     {
42724         
42725         if (this.frame && this.frame.dom.style.display == 'none') {
42726             return Roo.form.FCKeditor.superclass.getValue.call(this);
42727         }
42728         
42729         if(!this.el || !this.getEditor()) {
42730            
42731            // this.getValue.defer(100,this); 
42732             return this.value;
42733         }
42734        
42735         
42736         var value=this.getEditor().GetData();
42737         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42738         return Roo.form.FCKeditor.superclass.getValue.call(this);
42739         
42740
42741     },
42742
42743     /**
42744      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42745      * @return {Mixed} value The field value
42746      */
42747     getRawValue : function()
42748     {
42749         if (this.frame && this.frame.dom.style.display == 'none') {
42750             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42751         }
42752         
42753         if(!this.el || !this.getEditor()) {
42754             //this.getRawValue.defer(100,this); 
42755             return this.value;
42756             return;
42757         }
42758         
42759         
42760         
42761         var value=this.getEditor().GetData();
42762         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42763         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42764          
42765     },
42766     
42767     setSize : function(w,h) {
42768         
42769         
42770         
42771         //if (this.frame && this.frame.dom.style.display == 'none') {
42772         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42773         //    return;
42774         //}
42775         //if(!this.el || !this.getEditor()) {
42776         //    this.setSize.defer(100,this, [w,h]); 
42777         //    return;
42778         //}
42779         
42780         
42781         
42782         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42783         
42784         this.frame.dom.setAttribute('width', w);
42785         this.frame.dom.setAttribute('height', h);
42786         this.frame.setSize(w,h);
42787         
42788     },
42789     
42790     toggleSourceEdit : function(value) {
42791         
42792       
42793          
42794         this.el.dom.style.display = value ? '' : 'none';
42795         this.frame.dom.style.display = value ?  'none' : '';
42796         
42797     },
42798     
42799     
42800     focus: function(tag)
42801     {
42802         if (this.frame.dom.style.display == 'none') {
42803             return Roo.form.FCKeditor.superclass.focus.call(this);
42804         }
42805         if(!this.el || !this.getEditor()) {
42806             this.focus.defer(100,this, [tag]); 
42807             return;
42808         }
42809         
42810         
42811         
42812         
42813         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42814         this.getEditor().Focus();
42815         if (tgs.length) {
42816             if (!this.getEditor().Selection.GetSelection()) {
42817                 this.focus.defer(100,this, [tag]); 
42818                 return;
42819             }
42820             
42821             
42822             var r = this.getEditor().EditorDocument.createRange();
42823             r.setStart(tgs[0],0);
42824             r.setEnd(tgs[0],0);
42825             this.getEditor().Selection.GetSelection().removeAllRanges();
42826             this.getEditor().Selection.GetSelection().addRange(r);
42827             this.getEditor().Focus();
42828         }
42829         
42830     },
42831     
42832     
42833     
42834     replaceTextarea : function()
42835     {
42836         if ( document.getElementById( this.getId() + '___Frame' ) )
42837             return ;
42838         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42839         //{
42840             // We must check the elements firstly using the Id and then the name.
42841         var oTextarea = document.getElementById( this.getId() );
42842         
42843         var colElementsByName = document.getElementsByName( this.getId() ) ;
42844          
42845         oTextarea.style.display = 'none' ;
42846
42847         if ( oTextarea.tabIndex ) {            
42848             this.TabIndex = oTextarea.tabIndex ;
42849         }
42850         
42851         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42852         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42853         this.frame = Roo.get(this.getId() + '___Frame')
42854     },
42855     
42856     _getConfigHtml : function()
42857     {
42858         var sConfig = '' ;
42859
42860         for ( var o in this.fckconfig ) {
42861             sConfig += sConfig.length > 0  ? '&amp;' : '';
42862             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42863         }
42864
42865         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42866     },
42867     
42868     
42869     _getIFrameHtml : function()
42870     {
42871         var sFile = 'fckeditor.html' ;
42872         /* no idea what this is about..
42873         try
42874         {
42875             if ( (/fcksource=true/i).test( window.top.location.search ) )
42876                 sFile = 'fckeditor.original.html' ;
42877         }
42878         catch (e) { 
42879         */
42880
42881         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42882         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42883         
42884         
42885         var html = '<iframe id="' + this.getId() +
42886             '___Frame" src="' + sLink +
42887             '" width="' + this.width +
42888             '" height="' + this.height + '"' +
42889             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42890             ' frameborder="0" scrolling="no"></iframe>' ;
42891
42892         return html ;
42893     },
42894     
42895     _insertHtmlBefore : function( html, element )
42896     {
42897         if ( element.insertAdjacentHTML )       {
42898             // IE
42899             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42900         } else { // Gecko
42901             var oRange = document.createRange() ;
42902             oRange.setStartBefore( element ) ;
42903             var oFragment = oRange.createContextualFragment( html );
42904             element.parentNode.insertBefore( oFragment, element ) ;
42905         }
42906     }
42907     
42908     
42909   
42910     
42911     
42912     
42913     
42914
42915 });
42916
42917 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42918
42919 function FCKeditor_OnComplete(editorInstance){
42920     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42921     f.fckEditor = editorInstance;
42922     //console.log("loaded");
42923     f.fireEvent('editorinit', f, editorInstance);
42924
42925   
42926
42927  
42928
42929
42930
42931
42932
42933
42934
42935
42936
42937
42938
42939
42940
42941
42942
42943 //<script type="text/javascript">
42944 /**
42945  * @class Roo.form.GridField
42946  * @extends Roo.form.Field
42947  * Embed a grid (or editable grid into a form)
42948  * STATUS ALPHA
42949  * 
42950  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42951  * it needs 
42952  * xgrid.store = Roo.data.Store
42953  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42954  * xgrid.store.reader = Roo.data.JsonReader 
42955  * 
42956  * 
42957  * @constructor
42958  * Creates a new GridField
42959  * @param {Object} config Configuration options
42960  */
42961 Roo.form.GridField = function(config){
42962     Roo.form.GridField.superclass.constructor.call(this, config);
42963      
42964 };
42965
42966 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42967     /**
42968      * @cfg {Number} width  - used to restrict width of grid..
42969      */
42970     width : 100,
42971     /**
42972      * @cfg {Number} height - used to restrict height of grid..
42973      */
42974     height : 50,
42975      /**
42976      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42977          * 
42978          *}
42979      */
42980     xgrid : false, 
42981     /**
42982      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42983      * {tag: "input", type: "checkbox", autocomplete: "off"})
42984      */
42985    // defaultAutoCreate : { tag: 'div' },
42986     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42987     /**
42988      * @cfg {String} addTitle Text to include for adding a title.
42989      */
42990     addTitle : false,
42991     //
42992     onResize : function(){
42993         Roo.form.Field.superclass.onResize.apply(this, arguments);
42994     },
42995
42996     initEvents : function(){
42997         // Roo.form.Checkbox.superclass.initEvents.call(this);
42998         // has no events...
42999        
43000     },
43001
43002
43003     getResizeEl : function(){
43004         return this.wrap;
43005     },
43006
43007     getPositionEl : function(){
43008         return this.wrap;
43009     },
43010
43011     // private
43012     onRender : function(ct, position){
43013         
43014         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43015         var style = this.style;
43016         delete this.style;
43017         
43018         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43019         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43020         this.viewEl = this.wrap.createChild({ tag: 'div' });
43021         if (style) {
43022             this.viewEl.applyStyles(style);
43023         }
43024         if (this.width) {
43025             this.viewEl.setWidth(this.width);
43026         }
43027         if (this.height) {
43028             this.viewEl.setHeight(this.height);
43029         }
43030         //if(this.inputValue !== undefined){
43031         //this.setValue(this.value);
43032         
43033         
43034         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43035         
43036         
43037         this.grid.render();
43038         this.grid.getDataSource().on('remove', this.refreshValue, this);
43039         this.grid.getDataSource().on('update', this.refreshValue, this);
43040         this.grid.on('afteredit', this.refreshValue, this);
43041  
43042     },
43043      
43044     
43045     /**
43046      * Sets the value of the item. 
43047      * @param {String} either an object  or a string..
43048      */
43049     setValue : function(v){
43050         //this.value = v;
43051         v = v || []; // empty set..
43052         // this does not seem smart - it really only affects memoryproxy grids..
43053         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43054             var ds = this.grid.getDataSource();
43055             // assumes a json reader..
43056             var data = {}
43057             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43058             ds.loadData( data);
43059         }
43060         Roo.form.GridField.superclass.setValue.call(this, v);
43061         this.refreshValue();
43062         // should load data in the grid really....
43063     },
43064     
43065     // private
43066     refreshValue: function() {
43067          var val = [];
43068         this.grid.getDataSource().each(function(r) {
43069             val.push(r.data);
43070         });
43071         this.el.dom.value = Roo.encode(val);
43072     }
43073     
43074      
43075     
43076     
43077 });/*
43078  * Based on:
43079  * Ext JS Library 1.1.1
43080  * Copyright(c) 2006-2007, Ext JS, LLC.
43081  *
43082  * Originally Released Under LGPL - original licence link has changed is not relivant.
43083  *
43084  * Fork - LGPL
43085  * <script type="text/javascript">
43086  */
43087 /**
43088  * @class Roo.form.DisplayField
43089  * @extends Roo.form.Field
43090  * A generic Field to display non-editable data.
43091  * @constructor
43092  * Creates a new Display Field item.
43093  * @param {Object} config Configuration options
43094  */
43095 Roo.form.DisplayField = function(config){
43096     Roo.form.DisplayField.superclass.constructor.call(this, config);
43097     
43098 };
43099
43100 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43101     inputType:      'hidden',
43102     allowBlank:     true,
43103     readOnly:         true,
43104     
43105  
43106     /**
43107      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43108      */
43109     focusClass : undefined,
43110     /**
43111      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43112      */
43113     fieldClass: 'x-form-field',
43114     
43115      /**
43116      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43117      */
43118     valueRenderer: undefined,
43119     
43120     width: 100,
43121     /**
43122      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43123      * {tag: "input", type: "checkbox", autocomplete: "off"})
43124      */
43125      
43126  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43127
43128     onResize : function(){
43129         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43130         
43131     },
43132
43133     initEvents : function(){
43134         // Roo.form.Checkbox.superclass.initEvents.call(this);
43135         // has no events...
43136        
43137     },
43138
43139
43140     getResizeEl : function(){
43141         return this.wrap;
43142     },
43143
43144     getPositionEl : function(){
43145         return this.wrap;
43146     },
43147
43148     // private
43149     onRender : function(ct, position){
43150         
43151         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43152         //if(this.inputValue !== undefined){
43153         this.wrap = this.el.wrap();
43154         
43155         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43156         
43157         if (this.bodyStyle) {
43158             this.viewEl.applyStyles(this.bodyStyle);
43159         }
43160         //this.viewEl.setStyle('padding', '2px');
43161         
43162         this.setValue(this.value);
43163         
43164     },
43165 /*
43166     // private
43167     initValue : Roo.emptyFn,
43168
43169   */
43170
43171         // private
43172     onClick : function(){
43173         
43174     },
43175
43176     /**
43177      * Sets the checked state of the checkbox.
43178      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43179      */
43180     setValue : function(v){
43181         this.value = v;
43182         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43183         // this might be called before we have a dom element..
43184         if (!this.viewEl) {
43185             return;
43186         }
43187         this.viewEl.dom.innerHTML = html;
43188         Roo.form.DisplayField.superclass.setValue.call(this, v);
43189
43190     }
43191 });/*
43192  * 
43193  * Licence- LGPL
43194  * 
43195  */
43196
43197 /**
43198  * @class Roo.form.DayPicker
43199  * @extends Roo.form.Field
43200  * A Day picker show [M] [T] [W] ....
43201  * @constructor
43202  * Creates a new Day Picker
43203  * @param {Object} config Configuration options
43204  */
43205 Roo.form.DayPicker= function(config){
43206     Roo.form.DayPicker.superclass.constructor.call(this, config);
43207      
43208 };
43209
43210 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43211     /**
43212      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43213      */
43214     focusClass : undefined,
43215     /**
43216      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43217      */
43218     fieldClass: "x-form-field",
43219    
43220     /**
43221      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43222      * {tag: "input", type: "checkbox", autocomplete: "off"})
43223      */
43224     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43225     
43226    
43227     actionMode : 'viewEl', 
43228     //
43229     // private
43230  
43231     inputType : 'hidden',
43232     
43233      
43234     inputElement: false, // real input element?
43235     basedOn: false, // ????
43236     
43237     isFormField: true, // not sure where this is needed!!!!
43238
43239     onResize : function(){
43240         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43241         if(!this.boxLabel){
43242             this.el.alignTo(this.wrap, 'c-c');
43243         }
43244     },
43245
43246     initEvents : function(){
43247         Roo.form.Checkbox.superclass.initEvents.call(this);
43248         this.el.on("click", this.onClick,  this);
43249         this.el.on("change", this.onClick,  this);
43250     },
43251
43252
43253     getResizeEl : function(){
43254         return this.wrap;
43255     },
43256
43257     getPositionEl : function(){
43258         return this.wrap;
43259     },
43260
43261     
43262     // private
43263     onRender : function(ct, position){
43264         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43265        
43266         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43267         
43268         var r1 = '<table><tr>';
43269         var r2 = '<tr class="x-form-daypick-icons">';
43270         for (var i=0; i < 7; i++) {
43271             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43272             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43273         }
43274         
43275         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43276         viewEl.select('img').on('click', this.onClick, this);
43277         this.viewEl = viewEl;   
43278         
43279         
43280         // this will not work on Chrome!!!
43281         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43282         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43283         
43284         
43285           
43286
43287     },
43288
43289     // private
43290     initValue : Roo.emptyFn,
43291
43292     /**
43293      * Returns the checked state of the checkbox.
43294      * @return {Boolean} True if checked, else false
43295      */
43296     getValue : function(){
43297         return this.el.dom.value;
43298         
43299     },
43300
43301         // private
43302     onClick : function(e){ 
43303         //this.setChecked(!this.checked);
43304         Roo.get(e.target).toggleClass('x-menu-item-checked');
43305         this.refreshValue();
43306         //if(this.el.dom.checked != this.checked){
43307         //    this.setValue(this.el.dom.checked);
43308        // }
43309     },
43310     
43311     // private
43312     refreshValue : function()
43313     {
43314         var val = '';
43315         this.viewEl.select('img',true).each(function(e,i,n)  {
43316             val += e.is(".x-menu-item-checked") ? String(n) : '';
43317         });
43318         this.setValue(val, true);
43319     },
43320
43321     /**
43322      * Sets the checked state of the checkbox.
43323      * On is always based on a string comparison between inputValue and the param.
43324      * @param {Boolean/String} value - the value to set 
43325      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43326      */
43327     setValue : function(v,suppressEvent){
43328         if (!this.el.dom) {
43329             return;
43330         }
43331         var old = this.el.dom.value ;
43332         this.el.dom.value = v;
43333         if (suppressEvent) {
43334             return ;
43335         }
43336          
43337         // update display..
43338         this.viewEl.select('img',true).each(function(e,i,n)  {
43339             
43340             var on = e.is(".x-menu-item-checked");
43341             var newv = v.indexOf(String(n)) > -1;
43342             if (on != newv) {
43343                 e.toggleClass('x-menu-item-checked');
43344             }
43345             
43346         });
43347         
43348         
43349         this.fireEvent('change', this, v, old);
43350         
43351         
43352     },
43353    
43354     // handle setting of hidden value by some other method!!?!?
43355     setFromHidden: function()
43356     {
43357         if(!this.el){
43358             return;
43359         }
43360         //console.log("SET FROM HIDDEN");
43361         //alert('setFrom hidden');
43362         this.setValue(this.el.dom.value);
43363     },
43364     
43365     onDestroy : function()
43366     {
43367         if(this.viewEl){
43368             Roo.get(this.viewEl).remove();
43369         }
43370          
43371         Roo.form.DayPicker.superclass.onDestroy.call(this);
43372     }
43373
43374 });//<script type="text/javasscript">
43375  
43376
43377 /**
43378  * @class Roo.DDView
43379  * A DnD enabled version of Roo.View.
43380  * @param {Element/String} container The Element in which to create the View.
43381  * @param {String} tpl The template string used to create the markup for each element of the View
43382  * @param {Object} config The configuration properties. These include all the config options of
43383  * {@link Roo.View} plus some specific to this class.<br>
43384  * <p>
43385  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43386  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43387  * <p>
43388  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43389 .x-view-drag-insert-above {
43390         border-top:1px dotted #3366cc;
43391 }
43392 .x-view-drag-insert-below {
43393         border-bottom:1px dotted #3366cc;
43394 }
43395 </code></pre>
43396  * 
43397  */
43398  
43399 Roo.DDView = function(container, tpl, config) {
43400     Roo.DDView.superclass.constructor.apply(this, arguments);
43401     this.getEl().setStyle("outline", "0px none");
43402     this.getEl().unselectable();
43403     if (this.dragGroup) {
43404                 this.setDraggable(this.dragGroup.split(","));
43405     }
43406     if (this.dropGroup) {
43407                 this.setDroppable(this.dropGroup.split(","));
43408     }
43409     if (this.deletable) {
43410         this.setDeletable();
43411     }
43412     this.isDirtyFlag = false;
43413         this.addEvents({
43414                 "drop" : true
43415         });
43416 };
43417
43418 Roo.extend(Roo.DDView, Roo.View, {
43419 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43420 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43421 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43422 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43423
43424         isFormField: true,
43425
43426         reset: Roo.emptyFn,
43427         
43428         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43429
43430         validate: function() {
43431                 return true;
43432         },
43433         
43434         destroy: function() {
43435                 this.purgeListeners();
43436                 this.getEl.removeAllListeners();
43437                 this.getEl().remove();
43438                 if (this.dragZone) {
43439                         if (this.dragZone.destroy) {
43440                                 this.dragZone.destroy();
43441                         }
43442                 }
43443                 if (this.dropZone) {
43444                         if (this.dropZone.destroy) {
43445                                 this.dropZone.destroy();
43446                         }
43447                 }
43448         },
43449
43450 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43451         getName: function() {
43452                 return this.name;
43453         },
43454
43455 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43456         setValue: function(v) {
43457                 if (!this.store) {
43458                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43459                 }
43460                 var data = {};
43461                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43462                 this.store.proxy = new Roo.data.MemoryProxy(data);
43463                 this.store.load();
43464         },
43465
43466 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43467         getValue: function() {
43468                 var result = '(';
43469                 this.store.each(function(rec) {
43470                         result += rec.id + ',';
43471                 });
43472                 return result.substr(0, result.length - 1) + ')';
43473         },
43474         
43475         getIds: function() {
43476                 var i = 0, result = new Array(this.store.getCount());
43477                 this.store.each(function(rec) {
43478                         result[i++] = rec.id;
43479                 });
43480                 return result;
43481         },
43482         
43483         isDirty: function() {
43484                 return this.isDirtyFlag;
43485         },
43486
43487 /**
43488  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43489  *      whole Element becomes the target, and this causes the drop gesture to append.
43490  */
43491     getTargetFromEvent : function(e) {
43492                 var target = e.getTarget();
43493                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43494                 target = target.parentNode;
43495                 }
43496                 if (!target) {
43497                         target = this.el.dom.lastChild || this.el.dom;
43498                 }
43499                 return target;
43500     },
43501
43502 /**
43503  *      Create the drag data which consists of an object which has the property "ddel" as
43504  *      the drag proxy element. 
43505  */
43506     getDragData : function(e) {
43507         var target = this.findItemFromChild(e.getTarget());
43508                 if(target) {
43509                         this.handleSelection(e);
43510                         var selNodes = this.getSelectedNodes();
43511             var dragData = {
43512                 source: this,
43513                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43514                 nodes: selNodes,
43515                 records: []
43516                         };
43517                         var selectedIndices = this.getSelectedIndexes();
43518                         for (var i = 0; i < selectedIndices.length; i++) {
43519                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43520                         }
43521                         if (selNodes.length == 1) {
43522                                 dragData.ddel = target.cloneNode(true); // the div element
43523                         } else {
43524                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43525                                 div.className = 'multi-proxy';
43526                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43527                                         div.appendChild(selNodes[i].cloneNode(true));
43528                                 }
43529                                 dragData.ddel = div;
43530                         }
43531             //console.log(dragData)
43532             //console.log(dragData.ddel.innerHTML)
43533                         return dragData;
43534                 }
43535         //console.log('nodragData')
43536                 return false;
43537     },
43538     
43539 /**     Specify to which ddGroup items in this DDView may be dragged. */
43540     setDraggable: function(ddGroup) {
43541         if (ddGroup instanceof Array) {
43542                 Roo.each(ddGroup, this.setDraggable, this);
43543                 return;
43544         }
43545         if (this.dragZone) {
43546                 this.dragZone.addToGroup(ddGroup);
43547         } else {
43548                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43549                                 containerScroll: true,
43550                                 ddGroup: ddGroup 
43551
43552                         });
43553 //                      Draggability implies selection. DragZone's mousedown selects the element.
43554                         if (!this.multiSelect) { this.singleSelect = true; }
43555
43556 //                      Wire the DragZone's handlers up to methods in *this*
43557                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43558                 }
43559     },
43560
43561 /**     Specify from which ddGroup this DDView accepts drops. */
43562     setDroppable: function(ddGroup) {
43563         if (ddGroup instanceof Array) {
43564                 Roo.each(ddGroup, this.setDroppable, this);
43565                 return;
43566         }
43567         if (this.dropZone) {
43568                 this.dropZone.addToGroup(ddGroup);
43569         } else {
43570                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43571                                 containerScroll: true,
43572                                 ddGroup: ddGroup
43573                         });
43574
43575 //                      Wire the DropZone's handlers up to methods in *this*
43576                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43577                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43578                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43579                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43580                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43581                 }
43582     },
43583
43584 /**     Decide whether to drop above or below a View node. */
43585     getDropPoint : function(e, n, dd){
43586         if (n == this.el.dom) { return "above"; }
43587                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43588                 var c = t + (b - t) / 2;
43589                 var y = Roo.lib.Event.getPageY(e);
43590                 if(y <= c) {
43591                         return "above";
43592                 }else{
43593                         return "below";
43594                 }
43595     },
43596
43597     onNodeEnter : function(n, dd, e, data){
43598                 return false;
43599     },
43600     
43601     onNodeOver : function(n, dd, e, data){
43602                 var pt = this.getDropPoint(e, n, dd);
43603                 // set the insert point style on the target node
43604                 var dragElClass = this.dropNotAllowed;
43605                 if (pt) {
43606                         var targetElClass;
43607                         if (pt == "above"){
43608                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43609                                 targetElClass = "x-view-drag-insert-above";
43610                         } else {
43611                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43612                                 targetElClass = "x-view-drag-insert-below";
43613                         }
43614                         if (this.lastInsertClass != targetElClass){
43615                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43616                                 this.lastInsertClass = targetElClass;
43617                         }
43618                 }
43619                 return dragElClass;
43620         },
43621
43622     onNodeOut : function(n, dd, e, data){
43623                 this.removeDropIndicators(n);
43624     },
43625
43626     onNodeDrop : function(n, dd, e, data){
43627         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43628                 return false;
43629         }
43630         var pt = this.getDropPoint(e, n, dd);
43631                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43632                 if (pt == "below") { insertAt++; }
43633                 for (var i = 0; i < data.records.length; i++) {
43634                         var r = data.records[i];
43635                         var dup = this.store.getById(r.id);
43636                         if (dup && (dd != this.dragZone)) {
43637                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43638                         } else {
43639                                 if (data.copy) {
43640                                         this.store.insert(insertAt++, r.copy());
43641                                 } else {
43642                                         data.source.isDirtyFlag = true;
43643                                         r.store.remove(r);
43644                                         this.store.insert(insertAt++, r);
43645                                 }
43646                                 this.isDirtyFlag = true;
43647                         }
43648                 }
43649                 this.dragZone.cachedTarget = null;
43650                 return true;
43651     },
43652
43653     removeDropIndicators : function(n){
43654                 if(n){
43655                         Roo.fly(n).removeClass([
43656                                 "x-view-drag-insert-above",
43657                                 "x-view-drag-insert-below"]);
43658                         this.lastInsertClass = "_noclass";
43659                 }
43660     },
43661
43662 /**
43663  *      Utility method. Add a delete option to the DDView's context menu.
43664  *      @param {String} imageUrl The URL of the "delete" icon image.
43665  */
43666         setDeletable: function(imageUrl) {
43667                 if (!this.singleSelect && !this.multiSelect) {
43668                         this.singleSelect = true;
43669                 }
43670                 var c = this.getContextMenu();
43671                 this.contextMenu.on("itemclick", function(item) {
43672                         switch (item.id) {
43673                                 case "delete":
43674                                         this.remove(this.getSelectedIndexes());
43675                                         break;
43676                         }
43677                 }, this);
43678                 this.contextMenu.add({
43679                         icon: imageUrl,
43680                         id: "delete",
43681                         text: 'Delete'
43682                 });
43683         },
43684         
43685 /**     Return the context menu for this DDView. */
43686         getContextMenu: function() {
43687                 if (!this.contextMenu) {
43688 //                      Create the View's context menu
43689                         this.contextMenu = new Roo.menu.Menu({
43690                                 id: this.id + "-contextmenu"
43691                         });
43692                         this.el.on("contextmenu", this.showContextMenu, this);
43693                 }
43694                 return this.contextMenu;
43695         },
43696         
43697         disableContextMenu: function() {
43698                 if (this.contextMenu) {
43699                         this.el.un("contextmenu", this.showContextMenu, this);
43700                 }
43701         },
43702
43703         showContextMenu: function(e, item) {
43704         item = this.findItemFromChild(e.getTarget());
43705                 if (item) {
43706                         e.stopEvent();
43707                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43708                         this.contextMenu.showAt(e.getXY());
43709             }
43710     },
43711
43712 /**
43713  *      Remove {@link Roo.data.Record}s at the specified indices.
43714  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43715  */
43716     remove: function(selectedIndices) {
43717                 selectedIndices = [].concat(selectedIndices);
43718                 for (var i = 0; i < selectedIndices.length; i++) {
43719                         var rec = this.store.getAt(selectedIndices[i]);
43720                         this.store.remove(rec);
43721                 }
43722     },
43723
43724 /**
43725  *      Double click fires the event, but also, if this is draggable, and there is only one other
43726  *      related DropZone, it transfers the selected node.
43727  */
43728     onDblClick : function(e){
43729         var item = this.findItemFromChild(e.getTarget());
43730         if(item){
43731             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43732                 return false;
43733             }
43734             if (this.dragGroup) {
43735                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43736                     while (targets.indexOf(this.dropZone) > -1) {
43737                             targets.remove(this.dropZone);
43738                                 }
43739                     if (targets.length == 1) {
43740                                         this.dragZone.cachedTarget = null;
43741                         var el = Roo.get(targets[0].getEl());
43742                         var box = el.getBox(true);
43743                         targets[0].onNodeDrop(el.dom, {
43744                                 target: el.dom,
43745                                 xy: [box.x, box.y + box.height - 1]
43746                         }, null, this.getDragData(e));
43747                     }
43748                 }
43749         }
43750     },
43751     
43752     handleSelection: function(e) {
43753                 this.dragZone.cachedTarget = null;
43754         var item = this.findItemFromChild(e.getTarget());
43755         if (!item) {
43756                 this.clearSelections(true);
43757                 return;
43758         }
43759                 if (item && (this.multiSelect || this.singleSelect)){
43760                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43761                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43762                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43763                                 this.unselect(item);
43764                         } else {
43765                                 this.select(item, this.multiSelect && e.ctrlKey);
43766                                 this.lastSelection = item;
43767                         }
43768                 }
43769     },
43770
43771     onItemClick : function(item, index, e){
43772                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43773                         return false;
43774                 }
43775                 return true;
43776     },
43777
43778     unselect : function(nodeInfo, suppressEvent){
43779                 var node = this.getNode(nodeInfo);
43780                 if(node && this.isSelected(node)){
43781                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43782                                 Roo.fly(node).removeClass(this.selectedClass);
43783                                 this.selections.remove(node);
43784                                 if(!suppressEvent){
43785                                         this.fireEvent("selectionchange", this, this.selections);
43786                                 }
43787                         }
43788                 }
43789     }
43790 });
43791 /*
43792  * Based on:
43793  * Ext JS Library 1.1.1
43794  * Copyright(c) 2006-2007, Ext JS, LLC.
43795  *
43796  * Originally Released Under LGPL - original licence link has changed is not relivant.
43797  *
43798  * Fork - LGPL
43799  * <script type="text/javascript">
43800  */
43801  
43802 /**
43803  * @class Roo.LayoutManager
43804  * @extends Roo.util.Observable
43805  * Base class for layout managers.
43806  */
43807 Roo.LayoutManager = function(container, config){
43808     Roo.LayoutManager.superclass.constructor.call(this);
43809     this.el = Roo.get(container);
43810     // ie scrollbar fix
43811     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43812         document.body.scroll = "no";
43813     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43814         this.el.position('relative');
43815     }
43816     this.id = this.el.id;
43817     this.el.addClass("x-layout-container");
43818     /** false to disable window resize monitoring @type Boolean */
43819     this.monitorWindowResize = true;
43820     this.regions = {};
43821     this.addEvents({
43822         /**
43823          * @event layout
43824          * Fires when a layout is performed. 
43825          * @param {Roo.LayoutManager} this
43826          */
43827         "layout" : true,
43828         /**
43829          * @event regionresized
43830          * Fires when the user resizes a region. 
43831          * @param {Roo.LayoutRegion} region The resized region
43832          * @param {Number} newSize The new size (width for east/west, height for north/south)
43833          */
43834         "regionresized" : true,
43835         /**
43836          * @event regioncollapsed
43837          * Fires when a region is collapsed. 
43838          * @param {Roo.LayoutRegion} region The collapsed region
43839          */
43840         "regioncollapsed" : true,
43841         /**
43842          * @event regionexpanded
43843          * Fires when a region is expanded.  
43844          * @param {Roo.LayoutRegion} region The expanded region
43845          */
43846         "regionexpanded" : true
43847     });
43848     this.updating = false;
43849     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43850 };
43851
43852 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43853     /**
43854      * Returns true if this layout is currently being updated
43855      * @return {Boolean}
43856      */
43857     isUpdating : function(){
43858         return this.updating; 
43859     },
43860     
43861     /**
43862      * Suspend the LayoutManager from doing auto-layouts while
43863      * making multiple add or remove calls
43864      */
43865     beginUpdate : function(){
43866         this.updating = true;    
43867     },
43868     
43869     /**
43870      * Restore auto-layouts and optionally disable the manager from performing a layout
43871      * @param {Boolean} noLayout true to disable a layout update 
43872      */
43873     endUpdate : function(noLayout){
43874         this.updating = false;
43875         if(!noLayout){
43876             this.layout();
43877         }    
43878     },
43879     
43880     layout: function(){
43881         
43882     },
43883     
43884     onRegionResized : function(region, newSize){
43885         this.fireEvent("regionresized", region, newSize);
43886         this.layout();
43887     },
43888     
43889     onRegionCollapsed : function(region){
43890         this.fireEvent("regioncollapsed", region);
43891     },
43892     
43893     onRegionExpanded : function(region){
43894         this.fireEvent("regionexpanded", region);
43895     },
43896         
43897     /**
43898      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43899      * performs box-model adjustments.
43900      * @return {Object} The size as an object {width: (the width), height: (the height)}
43901      */
43902     getViewSize : function(){
43903         var size;
43904         if(this.el.dom != document.body){
43905             size = this.el.getSize();
43906         }else{
43907             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43908         }
43909         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43910         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43911         return size;
43912     },
43913     
43914     /**
43915      * Returns the Element this layout is bound to.
43916      * @return {Roo.Element}
43917      */
43918     getEl : function(){
43919         return this.el;
43920     },
43921     
43922     /**
43923      * Returns the specified region.
43924      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43925      * @return {Roo.LayoutRegion}
43926      */
43927     getRegion : function(target){
43928         return this.regions[target.toLowerCase()];
43929     },
43930     
43931     onWindowResize : function(){
43932         if(this.monitorWindowResize){
43933             this.layout();
43934         }
43935     }
43936 });/*
43937  * Based on:
43938  * Ext JS Library 1.1.1
43939  * Copyright(c) 2006-2007, Ext JS, LLC.
43940  *
43941  * Originally Released Under LGPL - original licence link has changed is not relivant.
43942  *
43943  * Fork - LGPL
43944  * <script type="text/javascript">
43945  */
43946 /**
43947  * @class Roo.BorderLayout
43948  * @extends Roo.LayoutManager
43949  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43950  * please see: <br><br>
43951  * <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>
43952  * <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>
43953  * Example:
43954  <pre><code>
43955  var layout = new Roo.BorderLayout(document.body, {
43956     north: {
43957         initialSize: 25,
43958         titlebar: false
43959     },
43960     west: {
43961         split:true,
43962         initialSize: 200,
43963         minSize: 175,
43964         maxSize: 400,
43965         titlebar: true,
43966         collapsible: true
43967     },
43968     east: {
43969         split:true,
43970         initialSize: 202,
43971         minSize: 175,
43972         maxSize: 400,
43973         titlebar: true,
43974         collapsible: true
43975     },
43976     south: {
43977         split:true,
43978         initialSize: 100,
43979         minSize: 100,
43980         maxSize: 200,
43981         titlebar: true,
43982         collapsible: true
43983     },
43984     center: {
43985         titlebar: true,
43986         autoScroll:true,
43987         resizeTabs: true,
43988         minTabWidth: 50,
43989         preferredTabWidth: 150
43990     }
43991 });
43992
43993 // shorthand
43994 var CP = Roo.ContentPanel;
43995
43996 layout.beginUpdate();
43997 layout.add("north", new CP("north", "North"));
43998 layout.add("south", new CP("south", {title: "South", closable: true}));
43999 layout.add("west", new CP("west", {title: "West"}));
44000 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44001 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44002 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44003 layout.getRegion("center").showPanel("center1");
44004 layout.endUpdate();
44005 </code></pre>
44006
44007 <b>The container the layout is rendered into can be either the body element or any other element.
44008 If it is not the body element, the container needs to either be an absolute positioned element,
44009 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44010 the container size if it is not the body element.</b>
44011
44012 * @constructor
44013 * Create a new BorderLayout
44014 * @param {String/HTMLElement/Element} container The container this layout is bound to
44015 * @param {Object} config Configuration options
44016  */
44017 Roo.BorderLayout = function(container, config){
44018     config = config || {};
44019     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44020     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44021     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44022         var target = this.factory.validRegions[i];
44023         if(config[target]){
44024             this.addRegion(target, config[target]);
44025         }
44026     }
44027 };
44028
44029 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44030     /**
44031      * Creates and adds a new region if it doesn't already exist.
44032      * @param {String} target The target region key (north, south, east, west or center).
44033      * @param {Object} config The regions config object
44034      * @return {BorderLayoutRegion} The new region
44035      */
44036     addRegion : function(target, config){
44037         if(!this.regions[target]){
44038             var r = this.factory.create(target, this, config);
44039             this.bindRegion(target, r);
44040         }
44041         return this.regions[target];
44042     },
44043
44044     // private (kinda)
44045     bindRegion : function(name, r){
44046         this.regions[name] = r;
44047         r.on("visibilitychange", this.layout, this);
44048         r.on("paneladded", this.layout, this);
44049         r.on("panelremoved", this.layout, this);
44050         r.on("invalidated", this.layout, this);
44051         r.on("resized", this.onRegionResized, this);
44052         r.on("collapsed", this.onRegionCollapsed, this);
44053         r.on("expanded", this.onRegionExpanded, this);
44054     },
44055
44056     /**
44057      * Performs a layout update.
44058      */
44059     layout : function(){
44060         if(this.updating) return;
44061         var size = this.getViewSize();
44062         var w = size.width;
44063         var h = size.height;
44064         var centerW = w;
44065         var centerH = h;
44066         var centerY = 0;
44067         var centerX = 0;
44068         //var x = 0, y = 0;
44069
44070         var rs = this.regions;
44071         var north = rs["north"];
44072         var south = rs["south"]; 
44073         var west = rs["west"];
44074         var east = rs["east"];
44075         var center = rs["center"];
44076         //if(this.hideOnLayout){ // not supported anymore
44077             //c.el.setStyle("display", "none");
44078         //}
44079         if(north && north.isVisible()){
44080             var b = north.getBox();
44081             var m = north.getMargins();
44082             b.width = w - (m.left+m.right);
44083             b.x = m.left;
44084             b.y = m.top;
44085             centerY = b.height + b.y + m.bottom;
44086             centerH -= centerY;
44087             north.updateBox(this.safeBox(b));
44088         }
44089         if(south && south.isVisible()){
44090             var b = south.getBox();
44091             var m = south.getMargins();
44092             b.width = w - (m.left+m.right);
44093             b.x = m.left;
44094             var totalHeight = (b.height + m.top + m.bottom);
44095             b.y = h - totalHeight + m.top;
44096             centerH -= totalHeight;
44097             south.updateBox(this.safeBox(b));
44098         }
44099         if(west && west.isVisible()){
44100             var b = west.getBox();
44101             var m = west.getMargins();
44102             b.height = centerH - (m.top+m.bottom);
44103             b.x = m.left;
44104             b.y = centerY + m.top;
44105             var totalWidth = (b.width + m.left + m.right);
44106             centerX += totalWidth;
44107             centerW -= totalWidth;
44108             west.updateBox(this.safeBox(b));
44109         }
44110         if(east && east.isVisible()){
44111             var b = east.getBox();
44112             var m = east.getMargins();
44113             b.height = centerH - (m.top+m.bottom);
44114             var totalWidth = (b.width + m.left + m.right);
44115             b.x = w - totalWidth + m.left;
44116             b.y = centerY + m.top;
44117             centerW -= totalWidth;
44118             east.updateBox(this.safeBox(b));
44119         }
44120         if(center){
44121             var m = center.getMargins();
44122             var centerBox = {
44123                 x: centerX + m.left,
44124                 y: centerY + m.top,
44125                 width: centerW - (m.left+m.right),
44126                 height: centerH - (m.top+m.bottom)
44127             };
44128             //if(this.hideOnLayout){
44129                 //center.el.setStyle("display", "block");
44130             //}
44131             center.updateBox(this.safeBox(centerBox));
44132         }
44133         this.el.repaint();
44134         this.fireEvent("layout", this);
44135     },
44136
44137     // private
44138     safeBox : function(box){
44139         box.width = Math.max(0, box.width);
44140         box.height = Math.max(0, box.height);
44141         return box;
44142     },
44143
44144     /**
44145      * Adds a ContentPanel (or subclass) to this layout.
44146      * @param {String} target The target region key (north, south, east, west or center).
44147      * @param {Roo.ContentPanel} panel The panel to add
44148      * @return {Roo.ContentPanel} The added panel
44149      */
44150     add : function(target, panel){
44151          
44152         target = target.toLowerCase();
44153         return this.regions[target].add(panel);
44154     },
44155
44156     /**
44157      * Remove a ContentPanel (or subclass) to this layout.
44158      * @param {String} target The target region key (north, south, east, west or center).
44159      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44160      * @return {Roo.ContentPanel} The removed panel
44161      */
44162     remove : function(target, panel){
44163         target = target.toLowerCase();
44164         return this.regions[target].remove(panel);
44165     },
44166
44167     /**
44168      * Searches all regions for a panel with the specified id
44169      * @param {String} panelId
44170      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44171      */
44172     findPanel : function(panelId){
44173         var rs = this.regions;
44174         for(var target in rs){
44175             if(typeof rs[target] != "function"){
44176                 var p = rs[target].getPanel(panelId);
44177                 if(p){
44178                     return p;
44179                 }
44180             }
44181         }
44182         return null;
44183     },
44184
44185     /**
44186      * Searches all regions for a panel with the specified id and activates (shows) it.
44187      * @param {String/ContentPanel} panelId The panels id or the panel itself
44188      * @return {Roo.ContentPanel} The shown panel or null
44189      */
44190     showPanel : function(panelId) {
44191       var rs = this.regions;
44192       for(var target in rs){
44193          var r = rs[target];
44194          if(typeof r != "function"){
44195             if(r.hasPanel(panelId)){
44196                return r.showPanel(panelId);
44197             }
44198          }
44199       }
44200       return null;
44201    },
44202
44203    /**
44204      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44205      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44206      */
44207     restoreState : function(provider){
44208         if(!provider){
44209             provider = Roo.state.Manager;
44210         }
44211         var sm = new Roo.LayoutStateManager();
44212         sm.init(this, provider);
44213     },
44214
44215     /**
44216      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44217      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44218      * a valid ContentPanel config object.  Example:
44219      * <pre><code>
44220 // Create the main layout
44221 var layout = new Roo.BorderLayout('main-ct', {
44222     west: {
44223         split:true,
44224         minSize: 175,
44225         titlebar: true
44226     },
44227     center: {
44228         title:'Components'
44229     }
44230 }, 'main-ct');
44231
44232 // Create and add multiple ContentPanels at once via configs
44233 layout.batchAdd({
44234    west: {
44235        id: 'source-files',
44236        autoCreate:true,
44237        title:'Ext Source Files',
44238        autoScroll:true,
44239        fitToFrame:true
44240    },
44241    center : {
44242        el: cview,
44243        autoScroll:true,
44244        fitToFrame:true,
44245        toolbar: tb,
44246        resizeEl:'cbody'
44247    }
44248 });
44249 </code></pre>
44250      * @param {Object} regions An object containing ContentPanel configs by region name
44251      */
44252     batchAdd : function(regions){
44253         this.beginUpdate();
44254         for(var rname in regions){
44255             var lr = this.regions[rname];
44256             if(lr){
44257                 this.addTypedPanels(lr, regions[rname]);
44258             }
44259         }
44260         this.endUpdate();
44261     },
44262
44263     // private
44264     addTypedPanels : function(lr, ps){
44265         if(typeof ps == 'string'){
44266             lr.add(new Roo.ContentPanel(ps));
44267         }
44268         else if(ps instanceof Array){
44269             for(var i =0, len = ps.length; i < len; i++){
44270                 this.addTypedPanels(lr, ps[i]);
44271             }
44272         }
44273         else if(!ps.events){ // raw config?
44274             var el = ps.el;
44275             delete ps.el; // prevent conflict
44276             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44277         }
44278         else {  // panel object assumed!
44279             lr.add(ps);
44280         }
44281     },
44282     /**
44283      * Adds a xtype elements to the layout.
44284      * <pre><code>
44285
44286 layout.addxtype({
44287        xtype : 'ContentPanel',
44288        region: 'west',
44289        items: [ .... ]
44290    }
44291 );
44292
44293 layout.addxtype({
44294         xtype : 'NestedLayoutPanel',
44295         region: 'west',
44296         layout: {
44297            center: { },
44298            west: { }   
44299         },
44300         items : [ ... list of content panels or nested layout panels.. ]
44301    }
44302 );
44303 </code></pre>
44304      * @param {Object} cfg Xtype definition of item to add.
44305      */
44306     addxtype : function(cfg)
44307     {
44308         // basically accepts a pannel...
44309         // can accept a layout region..!?!?
44310        // console.log('BorderLayout add ' + cfg.xtype)
44311         
44312         if (!cfg.xtype.match(/Panel$/)) {
44313             return false;
44314         }
44315         var ret = false;
44316         var region = cfg.region;
44317         delete cfg.region;
44318         
44319           
44320         var xitems = [];
44321         if (cfg.items) {
44322             xitems = cfg.items;
44323             delete cfg.items;
44324         }
44325         
44326         
44327         switch(cfg.xtype) 
44328         {
44329             case 'ContentPanel':  // ContentPanel (el, cfg)
44330             case 'ScrollPanel':  // ContentPanel (el, cfg)
44331                 if(cfg.autoCreate) {
44332                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44333                 } else {
44334                     var el = this.el.createChild();
44335                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44336                 }
44337                 
44338                 this.add(region, ret);
44339                 break;
44340             
44341             
44342             case 'TreePanel': // our new panel!
44343                 cfg.el = this.el.createChild();
44344                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44345                 this.add(region, ret);
44346                 break;
44347             
44348             case 'NestedLayoutPanel': 
44349                 // create a new Layout (which is  a Border Layout...
44350                 var el = this.el.createChild();
44351                 var clayout = cfg.layout;
44352                 delete cfg.layout;
44353                 clayout.items   = clayout.items  || [];
44354                 // replace this exitems with the clayout ones..
44355                 xitems = clayout.items;
44356                  
44357                 
44358                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44359                     cfg.background = false;
44360                 }
44361                 var layout = new Roo.BorderLayout(el, clayout);
44362                 
44363                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44364                 //console.log('adding nested layout panel '  + cfg.toSource());
44365                 this.add(region, ret);
44366                 
44367                 break;
44368                 
44369             case 'GridPanel': 
44370             
44371                 // needs grid and region
44372                 
44373                 //var el = this.getRegion(region).el.createChild();
44374                 var el = this.el.createChild();
44375                 // create the grid first...
44376                 
44377                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44378                 delete cfg.grid;
44379                 if (region == 'center' && this.active ) {
44380                     cfg.background = false;
44381                 }
44382                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44383                 
44384                 this.add(region, ret);
44385                 if (cfg.background) {
44386                     ret.on('activate', function(gp) {
44387                         if (!gp.grid.rendered) {
44388                             gp.grid.render();
44389                         }
44390                     });
44391                 } else {
44392                     grid.render();
44393                 }
44394                 break;
44395            
44396                
44397                 
44398                 
44399             default: 
44400                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44401                 return null;
44402              // GridPanel (grid, cfg)
44403             
44404         }
44405         this.beginUpdate();
44406         // add children..
44407         Roo.each(xitems, function(i)  {
44408             ret.addxtype(i);
44409         });
44410         this.endUpdate();
44411         return ret;
44412         
44413     }
44414 });
44415
44416 /**
44417  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44418  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44419  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44420  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44421  * <pre><code>
44422 // shorthand
44423 var CP = Roo.ContentPanel;
44424
44425 var layout = Roo.BorderLayout.create({
44426     north: {
44427         initialSize: 25,
44428         titlebar: false,
44429         panels: [new CP("north", "North")]
44430     },
44431     west: {
44432         split:true,
44433         initialSize: 200,
44434         minSize: 175,
44435         maxSize: 400,
44436         titlebar: true,
44437         collapsible: true,
44438         panels: [new CP("west", {title: "West"})]
44439     },
44440     east: {
44441         split:true,
44442         initialSize: 202,
44443         minSize: 175,
44444         maxSize: 400,
44445         titlebar: true,
44446         collapsible: true,
44447         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44448     },
44449     south: {
44450         split:true,
44451         initialSize: 100,
44452         minSize: 100,
44453         maxSize: 200,
44454         titlebar: true,
44455         collapsible: true,
44456         panels: [new CP("south", {title: "South", closable: true})]
44457     },
44458     center: {
44459         titlebar: true,
44460         autoScroll:true,
44461         resizeTabs: true,
44462         minTabWidth: 50,
44463         preferredTabWidth: 150,
44464         panels: [
44465             new CP("center1", {title: "Close Me", closable: true}),
44466             new CP("center2", {title: "Center Panel", closable: false})
44467         ]
44468     }
44469 }, document.body);
44470
44471 layout.getRegion("center").showPanel("center1");
44472 </code></pre>
44473  * @param config
44474  * @param targetEl
44475  */
44476 Roo.BorderLayout.create = function(config, targetEl){
44477     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44478     layout.beginUpdate();
44479     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44480     for(var j = 0, jlen = regions.length; j < jlen; j++){
44481         var lr = regions[j];
44482         if(layout.regions[lr] && config[lr].panels){
44483             var r = layout.regions[lr];
44484             var ps = config[lr].panels;
44485             layout.addTypedPanels(r, ps);
44486         }
44487     }
44488     layout.endUpdate();
44489     return layout;
44490 };
44491
44492 // private
44493 Roo.BorderLayout.RegionFactory = {
44494     // private
44495     validRegions : ["north","south","east","west","center"],
44496
44497     // private
44498     create : function(target, mgr, config){
44499         target = target.toLowerCase();
44500         if(config.lightweight || config.basic){
44501             return new Roo.BasicLayoutRegion(mgr, config, target);
44502         }
44503         switch(target){
44504             case "north":
44505                 return new Roo.NorthLayoutRegion(mgr, config);
44506             case "south":
44507                 return new Roo.SouthLayoutRegion(mgr, config);
44508             case "east":
44509                 return new Roo.EastLayoutRegion(mgr, config);
44510             case "west":
44511                 return new Roo.WestLayoutRegion(mgr, config);
44512             case "center":
44513                 return new Roo.CenterLayoutRegion(mgr, config);
44514         }
44515         throw 'Layout region "'+target+'" not supported.';
44516     }
44517 };/*
44518  * Based on:
44519  * Ext JS Library 1.1.1
44520  * Copyright(c) 2006-2007, Ext JS, LLC.
44521  *
44522  * Originally Released Under LGPL - original licence link has changed is not relivant.
44523  *
44524  * Fork - LGPL
44525  * <script type="text/javascript">
44526  */
44527  
44528 /**
44529  * @class Roo.BasicLayoutRegion
44530  * @extends Roo.util.Observable
44531  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44532  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44533  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44534  */
44535 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44536     this.mgr = mgr;
44537     this.position  = pos;
44538     this.events = {
44539         /**
44540          * @scope Roo.BasicLayoutRegion
44541          */
44542         
44543         /**
44544          * @event beforeremove
44545          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44546          * @param {Roo.LayoutRegion} this
44547          * @param {Roo.ContentPanel} panel The panel
44548          * @param {Object} e The cancel event object
44549          */
44550         "beforeremove" : true,
44551         /**
44552          * @event invalidated
44553          * Fires when the layout for this region is changed.
44554          * @param {Roo.LayoutRegion} this
44555          */
44556         "invalidated" : true,
44557         /**
44558          * @event visibilitychange
44559          * Fires when this region is shown or hidden 
44560          * @param {Roo.LayoutRegion} this
44561          * @param {Boolean} visibility true or false
44562          */
44563         "visibilitychange" : true,
44564         /**
44565          * @event paneladded
44566          * Fires when a panel is added. 
44567          * @param {Roo.LayoutRegion} this
44568          * @param {Roo.ContentPanel} panel The panel
44569          */
44570         "paneladded" : true,
44571         /**
44572          * @event panelremoved
44573          * Fires when a panel is removed. 
44574          * @param {Roo.LayoutRegion} this
44575          * @param {Roo.ContentPanel} panel The panel
44576          */
44577         "panelremoved" : true,
44578         /**
44579          * @event collapsed
44580          * Fires when this region is collapsed.
44581          * @param {Roo.LayoutRegion} this
44582          */
44583         "collapsed" : true,
44584         /**
44585          * @event expanded
44586          * Fires when this region is expanded.
44587          * @param {Roo.LayoutRegion} this
44588          */
44589         "expanded" : true,
44590         /**
44591          * @event slideshow
44592          * Fires when this region is slid into view.
44593          * @param {Roo.LayoutRegion} this
44594          */
44595         "slideshow" : true,
44596         /**
44597          * @event slidehide
44598          * Fires when this region slides out of view. 
44599          * @param {Roo.LayoutRegion} this
44600          */
44601         "slidehide" : true,
44602         /**
44603          * @event panelactivated
44604          * Fires when a panel is activated. 
44605          * @param {Roo.LayoutRegion} this
44606          * @param {Roo.ContentPanel} panel The activated panel
44607          */
44608         "panelactivated" : true,
44609         /**
44610          * @event resized
44611          * Fires when the user resizes this region. 
44612          * @param {Roo.LayoutRegion} this
44613          * @param {Number} newSize The new size (width for east/west, height for north/south)
44614          */
44615         "resized" : true
44616     };
44617     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44618     this.panels = new Roo.util.MixedCollection();
44619     this.panels.getKey = this.getPanelId.createDelegate(this);
44620     this.box = null;
44621     this.activePanel = null;
44622     // ensure listeners are added...
44623     
44624     if (config.listeners || config.events) {
44625         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44626             listeners : config.listeners || {},
44627             events : config.events || {}
44628         });
44629     }
44630     
44631     if(skipConfig !== true){
44632         this.applyConfig(config);
44633     }
44634 };
44635
44636 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44637     getPanelId : function(p){
44638         return p.getId();
44639     },
44640     
44641     applyConfig : function(config){
44642         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44643         this.config = config;
44644         
44645     },
44646     
44647     /**
44648      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44649      * the width, for horizontal (north, south) the height.
44650      * @param {Number} newSize The new width or height
44651      */
44652     resizeTo : function(newSize){
44653         var el = this.el ? this.el :
44654                  (this.activePanel ? this.activePanel.getEl() : null);
44655         if(el){
44656             switch(this.position){
44657                 case "east":
44658                 case "west":
44659                     el.setWidth(newSize);
44660                     this.fireEvent("resized", this, newSize);
44661                 break;
44662                 case "north":
44663                 case "south":
44664                     el.setHeight(newSize);
44665                     this.fireEvent("resized", this, newSize);
44666                 break;                
44667             }
44668         }
44669     },
44670     
44671     getBox : function(){
44672         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44673     },
44674     
44675     getMargins : function(){
44676         return this.margins;
44677     },
44678     
44679     updateBox : function(box){
44680         this.box = box;
44681         var el = this.activePanel.getEl();
44682         el.dom.style.left = box.x + "px";
44683         el.dom.style.top = box.y + "px";
44684         this.activePanel.setSize(box.width, box.height);
44685     },
44686     
44687     /**
44688      * Returns the container element for this region.
44689      * @return {Roo.Element}
44690      */
44691     getEl : function(){
44692         return this.activePanel;
44693     },
44694     
44695     /**
44696      * Returns true if this region is currently visible.
44697      * @return {Boolean}
44698      */
44699     isVisible : function(){
44700         return this.activePanel ? true : false;
44701     },
44702     
44703     setActivePanel : function(panel){
44704         panel = this.getPanel(panel);
44705         if(this.activePanel && this.activePanel != panel){
44706             this.activePanel.setActiveState(false);
44707             this.activePanel.getEl().setLeftTop(-10000,-10000);
44708         }
44709         this.activePanel = panel;
44710         panel.setActiveState(true);
44711         if(this.box){
44712             panel.setSize(this.box.width, this.box.height);
44713         }
44714         this.fireEvent("panelactivated", this, panel);
44715         this.fireEvent("invalidated");
44716     },
44717     
44718     /**
44719      * Show the specified panel.
44720      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44721      * @return {Roo.ContentPanel} The shown panel or null
44722      */
44723     showPanel : function(panel){
44724         if(panel = this.getPanel(panel)){
44725             this.setActivePanel(panel);
44726         }
44727         return panel;
44728     },
44729     
44730     /**
44731      * Get the active panel for this region.
44732      * @return {Roo.ContentPanel} The active panel or null
44733      */
44734     getActivePanel : function(){
44735         return this.activePanel;
44736     },
44737     
44738     /**
44739      * Add the passed ContentPanel(s)
44740      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44741      * @return {Roo.ContentPanel} The panel added (if only one was added)
44742      */
44743     add : function(panel){
44744         if(arguments.length > 1){
44745             for(var i = 0, len = arguments.length; i < len; i++) {
44746                 this.add(arguments[i]);
44747             }
44748             return null;
44749         }
44750         if(this.hasPanel(panel)){
44751             this.showPanel(panel);
44752             return panel;
44753         }
44754         var el = panel.getEl();
44755         if(el.dom.parentNode != this.mgr.el.dom){
44756             this.mgr.el.dom.appendChild(el.dom);
44757         }
44758         if(panel.setRegion){
44759             panel.setRegion(this);
44760         }
44761         this.panels.add(panel);
44762         el.setStyle("position", "absolute");
44763         if(!panel.background){
44764             this.setActivePanel(panel);
44765             if(this.config.initialSize && this.panels.getCount()==1){
44766                 this.resizeTo(this.config.initialSize);
44767             }
44768         }
44769         this.fireEvent("paneladded", this, panel);
44770         return panel;
44771     },
44772     
44773     /**
44774      * Returns true if the panel is in this region.
44775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44776      * @return {Boolean}
44777      */
44778     hasPanel : function(panel){
44779         if(typeof panel == "object"){ // must be panel obj
44780             panel = panel.getId();
44781         }
44782         return this.getPanel(panel) ? true : false;
44783     },
44784     
44785     /**
44786      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44787      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44788      * @param {Boolean} preservePanel Overrides the config preservePanel option
44789      * @return {Roo.ContentPanel} The panel that was removed
44790      */
44791     remove : function(panel, preservePanel){
44792         panel = this.getPanel(panel);
44793         if(!panel){
44794             return null;
44795         }
44796         var e = {};
44797         this.fireEvent("beforeremove", this, panel, e);
44798         if(e.cancel === true){
44799             return null;
44800         }
44801         var panelId = panel.getId();
44802         this.panels.removeKey(panelId);
44803         return panel;
44804     },
44805     
44806     /**
44807      * Returns the panel specified or null if it's not in this region.
44808      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44809      * @return {Roo.ContentPanel}
44810      */
44811     getPanel : function(id){
44812         if(typeof id == "object"){ // must be panel obj
44813             return id;
44814         }
44815         return this.panels.get(id);
44816     },
44817     
44818     /**
44819      * Returns this regions position (north/south/east/west/center).
44820      * @return {String} 
44821      */
44822     getPosition: function(){
44823         return this.position;    
44824     }
44825 });/*
44826  * Based on:
44827  * Ext JS Library 1.1.1
44828  * Copyright(c) 2006-2007, Ext JS, LLC.
44829  *
44830  * Originally Released Under LGPL - original licence link has changed is not relivant.
44831  *
44832  * Fork - LGPL
44833  * <script type="text/javascript">
44834  */
44835  
44836 /**
44837  * @class Roo.LayoutRegion
44838  * @extends Roo.BasicLayoutRegion
44839  * This class represents a region in a layout manager.
44840  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
44841  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
44842  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
44843  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44844  * @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})
44845  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
44846  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
44847  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44848  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44849  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44850  * @cfg {String}    title           The title for the region (overrides panel titles)
44851  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44852  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44853  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44854  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44855  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44856  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44857  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44858  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44859  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44860  * @cfg {Boolean}   showPin         True to show a pin button
44861  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44862  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44863  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44864  * @cfg {Number}    width           For East/West panels
44865  * @cfg {Number}    height          For North/South panels
44866  * @cfg {Boolean}   split           To show the splitter
44867  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44868  */
44869 Roo.LayoutRegion = function(mgr, config, pos){
44870     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44871     var dh = Roo.DomHelper;
44872     /** This region's container element 
44873     * @type Roo.Element */
44874     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44875     /** This region's title element 
44876     * @type Roo.Element */
44877
44878     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44879         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44880         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44881     ]}, true);
44882     this.titleEl.enableDisplayMode();
44883     /** This region's title text element 
44884     * @type HTMLElement */
44885     this.titleTextEl = this.titleEl.dom.firstChild;
44886     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44887     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44888     this.closeBtn.enableDisplayMode();
44889     this.closeBtn.on("click", this.closeClicked, this);
44890     this.closeBtn.hide();
44891
44892     this.createBody(config);
44893     this.visible = true;
44894     this.collapsed = false;
44895
44896     if(config.hideWhenEmpty){
44897         this.hide();
44898         this.on("paneladded", this.validateVisibility, this);
44899         this.on("panelremoved", this.validateVisibility, this);
44900     }
44901     this.applyConfig(config);
44902 };
44903
44904 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44905
44906     createBody : function(){
44907         /** This region's body element 
44908         * @type Roo.Element */
44909         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44910     },
44911
44912     applyConfig : function(c){
44913         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44914             var dh = Roo.DomHelper;
44915             if(c.titlebar !== false){
44916                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44917                 this.collapseBtn.on("click", this.collapse, this);
44918                 this.collapseBtn.enableDisplayMode();
44919
44920                 if(c.showPin === true || this.showPin){
44921                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44922                     this.stickBtn.enableDisplayMode();
44923                     this.stickBtn.on("click", this.expand, this);
44924                     this.stickBtn.hide();
44925                 }
44926             }
44927             /** This region's collapsed element
44928             * @type Roo.Element */
44929             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44930                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44931             ]}, true);
44932             if(c.floatable !== false){
44933                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44934                this.collapsedEl.on("click", this.collapseClick, this);
44935             }
44936
44937             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44938                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44939                    id: "message", unselectable: "on", style:{"float":"left"}});
44940                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44941              }
44942             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44943             this.expandBtn.on("click", this.expand, this);
44944         }
44945         if(this.collapseBtn){
44946             this.collapseBtn.setVisible(c.collapsible == true);
44947         }
44948         this.cmargins = c.cmargins || this.cmargins ||
44949                          (this.position == "west" || this.position == "east" ?
44950                              {top: 0, left: 2, right:2, bottom: 0} :
44951                              {top: 2, left: 0, right:0, bottom: 2});
44952         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44953         this.bottomTabs = c.tabPosition != "top";
44954         this.autoScroll = c.autoScroll || false;
44955         if(this.autoScroll){
44956             this.bodyEl.setStyle("overflow", "auto");
44957         }else{
44958             this.bodyEl.setStyle("overflow", "hidden");
44959         }
44960         //if(c.titlebar !== false){
44961             if((!c.titlebar && !c.title) || c.titlebar === false){
44962                 this.titleEl.hide();
44963             }else{
44964                 this.titleEl.show();
44965                 if(c.title){
44966                     this.titleTextEl.innerHTML = c.title;
44967                 }
44968             }
44969         //}
44970         this.duration = c.duration || .30;
44971         this.slideDuration = c.slideDuration || .45;
44972         this.config = c;
44973         if(c.collapsed){
44974             this.collapse(true);
44975         }
44976         if(c.hidden){
44977             this.hide();
44978         }
44979     },
44980     /**
44981      * Returns true if this region is currently visible.
44982      * @return {Boolean}
44983      */
44984     isVisible : function(){
44985         return this.visible;
44986     },
44987
44988     /**
44989      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44990      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44991      */
44992     setCollapsedTitle : function(title){
44993         title = title || "&#160;";
44994         if(this.collapsedTitleTextEl){
44995             this.collapsedTitleTextEl.innerHTML = title;
44996         }
44997     },
44998
44999     getBox : function(){
45000         var b;
45001         if(!this.collapsed){
45002             b = this.el.getBox(false, true);
45003         }else{
45004             b = this.collapsedEl.getBox(false, true);
45005         }
45006         return b;
45007     },
45008
45009     getMargins : function(){
45010         return this.collapsed ? this.cmargins : this.margins;
45011     },
45012
45013     highlight : function(){
45014         this.el.addClass("x-layout-panel-dragover");
45015     },
45016
45017     unhighlight : function(){
45018         this.el.removeClass("x-layout-panel-dragover");
45019     },
45020
45021     updateBox : function(box){
45022         this.box = box;
45023         if(!this.collapsed){
45024             this.el.dom.style.left = box.x + "px";
45025             this.el.dom.style.top = box.y + "px";
45026             this.updateBody(box.width, box.height);
45027         }else{
45028             this.collapsedEl.dom.style.left = box.x + "px";
45029             this.collapsedEl.dom.style.top = box.y + "px";
45030             this.collapsedEl.setSize(box.width, box.height);
45031         }
45032         if(this.tabs){
45033             this.tabs.autoSizeTabs();
45034         }
45035     },
45036
45037     updateBody : function(w, h){
45038         if(w !== null){
45039             this.el.setWidth(w);
45040             w -= this.el.getBorderWidth("rl");
45041             if(this.config.adjustments){
45042                 w += this.config.adjustments[0];
45043             }
45044         }
45045         if(h !== null){
45046             this.el.setHeight(h);
45047             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45048             h -= this.el.getBorderWidth("tb");
45049             if(this.config.adjustments){
45050                 h += this.config.adjustments[1];
45051             }
45052             this.bodyEl.setHeight(h);
45053             if(this.tabs){
45054                 h = this.tabs.syncHeight(h);
45055             }
45056         }
45057         if(this.panelSize){
45058             w = w !== null ? w : this.panelSize.width;
45059             h = h !== null ? h : this.panelSize.height;
45060         }
45061         if(this.activePanel){
45062             var el = this.activePanel.getEl();
45063             w = w !== null ? w : el.getWidth();
45064             h = h !== null ? h : el.getHeight();
45065             this.panelSize = {width: w, height: h};
45066             this.activePanel.setSize(w, h);
45067         }
45068         if(Roo.isIE && this.tabs){
45069             this.tabs.el.repaint();
45070         }
45071     },
45072
45073     /**
45074      * Returns the container element for this region.
45075      * @return {Roo.Element}
45076      */
45077     getEl : function(){
45078         return this.el;
45079     },
45080
45081     /**
45082      * Hides this region.
45083      */
45084     hide : function(){
45085         if(!this.collapsed){
45086             this.el.dom.style.left = "-2000px";
45087             this.el.hide();
45088         }else{
45089             this.collapsedEl.dom.style.left = "-2000px";
45090             this.collapsedEl.hide();
45091         }
45092         this.visible = false;
45093         this.fireEvent("visibilitychange", this, false);
45094     },
45095
45096     /**
45097      * Shows this region if it was previously hidden.
45098      */
45099     show : function(){
45100         if(!this.collapsed){
45101             this.el.show();
45102         }else{
45103             this.collapsedEl.show();
45104         }
45105         this.visible = true;
45106         this.fireEvent("visibilitychange", this, true);
45107     },
45108
45109     closeClicked : function(){
45110         if(this.activePanel){
45111             this.remove(this.activePanel);
45112         }
45113     },
45114
45115     collapseClick : function(e){
45116         if(this.isSlid){
45117            e.stopPropagation();
45118            this.slideIn();
45119         }else{
45120            e.stopPropagation();
45121            this.slideOut();
45122         }
45123     },
45124
45125     /**
45126      * Collapses this region.
45127      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45128      */
45129     collapse : function(skipAnim){
45130         if(this.collapsed) return;
45131         this.collapsed = true;
45132         if(this.split){
45133             this.split.el.hide();
45134         }
45135         if(this.config.animate && skipAnim !== true){
45136             this.fireEvent("invalidated", this);
45137             this.animateCollapse();
45138         }else{
45139             this.el.setLocation(-20000,-20000);
45140             this.el.hide();
45141             this.collapsedEl.show();
45142             this.fireEvent("collapsed", this);
45143             this.fireEvent("invalidated", this);
45144         }
45145     },
45146
45147     animateCollapse : function(){
45148         // overridden
45149     },
45150
45151     /**
45152      * Expands this region if it was previously collapsed.
45153      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45154      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45155      */
45156     expand : function(e, skipAnim){
45157         if(e) e.stopPropagation();
45158         if(!this.collapsed || this.el.hasActiveFx()) return;
45159         if(this.isSlid){
45160             this.afterSlideIn();
45161             skipAnim = true;
45162         }
45163         this.collapsed = false;
45164         if(this.config.animate && skipAnim !== true){
45165             this.animateExpand();
45166         }else{
45167             this.el.show();
45168             if(this.split){
45169                 this.split.el.show();
45170             }
45171             this.collapsedEl.setLocation(-2000,-2000);
45172             this.collapsedEl.hide();
45173             this.fireEvent("invalidated", this);
45174             this.fireEvent("expanded", this);
45175         }
45176     },
45177
45178     animateExpand : function(){
45179         // overridden
45180     },
45181
45182     initTabs : function()
45183     {
45184         this.bodyEl.setStyle("overflow", "hidden");
45185         var ts = new Roo.TabPanel(
45186                 this.bodyEl.dom,
45187                 {
45188                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45189                     disableTooltips: this.config.disableTabTips,
45190                     toolbar : this.config.toolbar
45191                 }
45192         );
45193         if(this.config.hideTabs){
45194             ts.stripWrap.setDisplayed(false);
45195         }
45196         this.tabs = ts;
45197         ts.resizeTabs = this.config.resizeTabs === true;
45198         ts.minTabWidth = this.config.minTabWidth || 40;
45199         ts.maxTabWidth = this.config.maxTabWidth || 250;
45200         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45201         ts.monitorResize = false;
45202         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45203         ts.bodyEl.addClass('x-layout-tabs-body');
45204         this.panels.each(this.initPanelAsTab, this);
45205     },
45206
45207     initPanelAsTab : function(panel){
45208         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45209                     this.config.closeOnTab && panel.isClosable());
45210         if(panel.tabTip !== undefined){
45211             ti.setTooltip(panel.tabTip);
45212         }
45213         ti.on("activate", function(){
45214               this.setActivePanel(panel);
45215         }, this);
45216         if(this.config.closeOnTab){
45217             ti.on("beforeclose", function(t, e){
45218                 e.cancel = true;
45219                 this.remove(panel);
45220             }, this);
45221         }
45222         return ti;
45223     },
45224
45225     updatePanelTitle : function(panel, title){
45226         if(this.activePanel == panel){
45227             this.updateTitle(title);
45228         }
45229         if(this.tabs){
45230             var ti = this.tabs.getTab(panel.getEl().id);
45231             ti.setText(title);
45232             if(panel.tabTip !== undefined){
45233                 ti.setTooltip(panel.tabTip);
45234             }
45235         }
45236     },
45237
45238     updateTitle : function(title){
45239         if(this.titleTextEl && !this.config.title){
45240             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45241         }
45242     },
45243
45244     setActivePanel : function(panel){
45245         panel = this.getPanel(panel);
45246         if(this.activePanel && this.activePanel != panel){
45247             this.activePanel.setActiveState(false);
45248         }
45249         this.activePanel = panel;
45250         panel.setActiveState(true);
45251         if(this.panelSize){
45252             panel.setSize(this.panelSize.width, this.panelSize.height);
45253         }
45254         if(this.closeBtn){
45255             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45256         }
45257         this.updateTitle(panel.getTitle());
45258         if(this.tabs){
45259             this.fireEvent("invalidated", this);
45260         }
45261         this.fireEvent("panelactivated", this, panel);
45262     },
45263
45264     /**
45265      * Shows the specified panel.
45266      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45267      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45268      */
45269     showPanel : function(panel){
45270         if(panel = this.getPanel(panel)){
45271             if(this.tabs){
45272                 var tab = this.tabs.getTab(panel.getEl().id);
45273                 if(tab.isHidden()){
45274                     this.tabs.unhideTab(tab.id);
45275                 }
45276                 tab.activate();
45277             }else{
45278                 this.setActivePanel(panel);
45279             }
45280         }
45281         return panel;
45282     },
45283
45284     /**
45285      * Get the active panel for this region.
45286      * @return {Roo.ContentPanel} The active panel or null
45287      */
45288     getActivePanel : function(){
45289         return this.activePanel;
45290     },
45291
45292     validateVisibility : function(){
45293         if(this.panels.getCount() < 1){
45294             this.updateTitle("&#160;");
45295             this.closeBtn.hide();
45296             this.hide();
45297         }else{
45298             if(!this.isVisible()){
45299                 this.show();
45300             }
45301         }
45302     },
45303
45304     /**
45305      * Adds the passed ContentPanel(s) to this region.
45306      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45307      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45308      */
45309     add : function(panel){
45310         if(arguments.length > 1){
45311             for(var i = 0, len = arguments.length; i < len; i++) {
45312                 this.add(arguments[i]);
45313             }
45314             return null;
45315         }
45316         if(this.hasPanel(panel)){
45317             this.showPanel(panel);
45318             return panel;
45319         }
45320         panel.setRegion(this);
45321         this.panels.add(panel);
45322         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45323             this.bodyEl.dom.appendChild(panel.getEl().dom);
45324             if(panel.background !== true){
45325                 this.setActivePanel(panel);
45326             }
45327             this.fireEvent("paneladded", this, panel);
45328             return panel;
45329         }
45330         if(!this.tabs){
45331             this.initTabs();
45332         }else{
45333             this.initPanelAsTab(panel);
45334         }
45335         if(panel.background !== true){
45336             this.tabs.activate(panel.getEl().id);
45337         }
45338         this.fireEvent("paneladded", this, panel);
45339         return panel;
45340     },
45341
45342     /**
45343      * Hides the tab for the specified panel.
45344      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45345      */
45346     hidePanel : function(panel){
45347         if(this.tabs && (panel = this.getPanel(panel))){
45348             this.tabs.hideTab(panel.getEl().id);
45349         }
45350     },
45351
45352     /**
45353      * Unhides the tab for a previously hidden panel.
45354      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45355      */
45356     unhidePanel : function(panel){
45357         if(this.tabs && (panel = this.getPanel(panel))){
45358             this.tabs.unhideTab(panel.getEl().id);
45359         }
45360     },
45361
45362     clearPanels : function(){
45363         while(this.panels.getCount() > 0){
45364              this.remove(this.panels.first());
45365         }
45366     },
45367
45368     /**
45369      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45370      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45371      * @param {Boolean} preservePanel Overrides the config preservePanel option
45372      * @return {Roo.ContentPanel} The panel that was removed
45373      */
45374     remove : function(panel, preservePanel){
45375         panel = this.getPanel(panel);
45376         if(!panel){
45377             return null;
45378         }
45379         var e = {};
45380         this.fireEvent("beforeremove", this, panel, e);
45381         if(e.cancel === true){
45382             return null;
45383         }
45384         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45385         var panelId = panel.getId();
45386         this.panels.removeKey(panelId);
45387         if(preservePanel){
45388             document.body.appendChild(panel.getEl().dom);
45389         }
45390         if(this.tabs){
45391             this.tabs.removeTab(panel.getEl().id);
45392         }else if (!preservePanel){
45393             this.bodyEl.dom.removeChild(panel.getEl().dom);
45394         }
45395         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45396             var p = this.panels.first();
45397             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45398             tempEl.appendChild(p.getEl().dom);
45399             this.bodyEl.update("");
45400             this.bodyEl.dom.appendChild(p.getEl().dom);
45401             tempEl = null;
45402             this.updateTitle(p.getTitle());
45403             this.tabs = null;
45404             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45405             this.setActivePanel(p);
45406         }
45407         panel.setRegion(null);
45408         if(this.activePanel == panel){
45409             this.activePanel = null;
45410         }
45411         if(this.config.autoDestroy !== false && preservePanel !== true){
45412             try{panel.destroy();}catch(e){}
45413         }
45414         this.fireEvent("panelremoved", this, panel);
45415         return panel;
45416     },
45417
45418     /**
45419      * Returns the TabPanel component used by this region
45420      * @return {Roo.TabPanel}
45421      */
45422     getTabs : function(){
45423         return this.tabs;
45424     },
45425
45426     createTool : function(parentEl, className){
45427         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45428             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45429         btn.addClassOnOver("x-layout-tools-button-over");
45430         return btn;
45431     }
45432 });/*
45433  * Based on:
45434  * Ext JS Library 1.1.1
45435  * Copyright(c) 2006-2007, Ext JS, LLC.
45436  *
45437  * Originally Released Under LGPL - original licence link has changed is not relivant.
45438  *
45439  * Fork - LGPL
45440  * <script type="text/javascript">
45441  */
45442  
45443
45444
45445 /**
45446  * @class Roo.SplitLayoutRegion
45447  * @extends Roo.LayoutRegion
45448  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45449  */
45450 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45451     this.cursor = cursor;
45452     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45453 };
45454
45455 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45456     splitTip : "Drag to resize.",
45457     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45458     useSplitTips : false,
45459
45460     applyConfig : function(config){
45461         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45462         if(config.split){
45463             if(!this.split){
45464                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45465                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45466                 /** The SplitBar for this region 
45467                 * @type Roo.SplitBar */
45468                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45469                 this.split.on("moved", this.onSplitMove, this);
45470                 this.split.useShim = config.useShim === true;
45471                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45472                 if(this.useSplitTips){
45473                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45474                 }
45475                 if(config.collapsible){
45476                     this.split.el.on("dblclick", this.collapse,  this);
45477                 }
45478             }
45479             if(typeof config.minSize != "undefined"){
45480                 this.split.minSize = config.minSize;
45481             }
45482             if(typeof config.maxSize != "undefined"){
45483                 this.split.maxSize = config.maxSize;
45484             }
45485             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45486                 this.hideSplitter();
45487             }
45488         }
45489     },
45490
45491     getHMaxSize : function(){
45492          var cmax = this.config.maxSize || 10000;
45493          var center = this.mgr.getRegion("center");
45494          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45495     },
45496
45497     getVMaxSize : function(){
45498          var cmax = this.config.maxSize || 10000;
45499          var center = this.mgr.getRegion("center");
45500          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45501     },
45502
45503     onSplitMove : function(split, newSize){
45504         this.fireEvent("resized", this, newSize);
45505     },
45506     
45507     /** 
45508      * Returns the {@link Roo.SplitBar} for this region.
45509      * @return {Roo.SplitBar}
45510      */
45511     getSplitBar : function(){
45512         return this.split;
45513     },
45514     
45515     hide : function(){
45516         this.hideSplitter();
45517         Roo.SplitLayoutRegion.superclass.hide.call(this);
45518     },
45519
45520     hideSplitter : function(){
45521         if(this.split){
45522             this.split.el.setLocation(-2000,-2000);
45523             this.split.el.hide();
45524         }
45525     },
45526
45527     show : function(){
45528         if(this.split){
45529             this.split.el.show();
45530         }
45531         Roo.SplitLayoutRegion.superclass.show.call(this);
45532     },
45533     
45534     beforeSlide: function(){
45535         if(Roo.isGecko){// firefox overflow auto bug workaround
45536             this.bodyEl.clip();
45537             if(this.tabs) this.tabs.bodyEl.clip();
45538             if(this.activePanel){
45539                 this.activePanel.getEl().clip();
45540                 
45541                 if(this.activePanel.beforeSlide){
45542                     this.activePanel.beforeSlide();
45543                 }
45544             }
45545         }
45546     },
45547     
45548     afterSlide : function(){
45549         if(Roo.isGecko){// firefox overflow auto bug workaround
45550             this.bodyEl.unclip();
45551             if(this.tabs) this.tabs.bodyEl.unclip();
45552             if(this.activePanel){
45553                 this.activePanel.getEl().unclip();
45554                 if(this.activePanel.afterSlide){
45555                     this.activePanel.afterSlide();
45556                 }
45557             }
45558         }
45559     },
45560
45561     initAutoHide : function(){
45562         if(this.autoHide !== false){
45563             if(!this.autoHideHd){
45564                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45565                 this.autoHideHd = {
45566                     "mouseout": function(e){
45567                         if(!e.within(this.el, true)){
45568                             st.delay(500);
45569                         }
45570                     },
45571                     "mouseover" : function(e){
45572                         st.cancel();
45573                     },
45574                     scope : this
45575                 };
45576             }
45577             this.el.on(this.autoHideHd);
45578         }
45579     },
45580
45581     clearAutoHide : function(){
45582         if(this.autoHide !== false){
45583             this.el.un("mouseout", this.autoHideHd.mouseout);
45584             this.el.un("mouseover", this.autoHideHd.mouseover);
45585         }
45586     },
45587
45588     clearMonitor : function(){
45589         Roo.get(document).un("click", this.slideInIf, this);
45590     },
45591
45592     // these names are backwards but not changed for compat
45593     slideOut : function(){
45594         if(this.isSlid || this.el.hasActiveFx()){
45595             return;
45596         }
45597         this.isSlid = true;
45598         if(this.collapseBtn){
45599             this.collapseBtn.hide();
45600         }
45601         this.closeBtnState = this.closeBtn.getStyle('display');
45602         this.closeBtn.hide();
45603         if(this.stickBtn){
45604             this.stickBtn.show();
45605         }
45606         this.el.show();
45607         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45608         this.beforeSlide();
45609         this.el.setStyle("z-index", 10001);
45610         this.el.slideIn(this.getSlideAnchor(), {
45611             callback: function(){
45612                 this.afterSlide();
45613                 this.initAutoHide();
45614                 Roo.get(document).on("click", this.slideInIf, this);
45615                 this.fireEvent("slideshow", this);
45616             },
45617             scope: this,
45618             block: true
45619         });
45620     },
45621
45622     afterSlideIn : function(){
45623         this.clearAutoHide();
45624         this.isSlid = false;
45625         this.clearMonitor();
45626         this.el.setStyle("z-index", "");
45627         if(this.collapseBtn){
45628             this.collapseBtn.show();
45629         }
45630         this.closeBtn.setStyle('display', this.closeBtnState);
45631         if(this.stickBtn){
45632             this.stickBtn.hide();
45633         }
45634         this.fireEvent("slidehide", this);
45635     },
45636
45637     slideIn : function(cb){
45638         if(!this.isSlid || this.el.hasActiveFx()){
45639             Roo.callback(cb);
45640             return;
45641         }
45642         this.isSlid = false;
45643         this.beforeSlide();
45644         this.el.slideOut(this.getSlideAnchor(), {
45645             callback: function(){
45646                 this.el.setLeftTop(-10000, -10000);
45647                 this.afterSlide();
45648                 this.afterSlideIn();
45649                 Roo.callback(cb);
45650             },
45651             scope: this,
45652             block: true
45653         });
45654     },
45655     
45656     slideInIf : function(e){
45657         if(!e.within(this.el)){
45658             this.slideIn();
45659         }
45660     },
45661
45662     animateCollapse : function(){
45663         this.beforeSlide();
45664         this.el.setStyle("z-index", 20000);
45665         var anchor = this.getSlideAnchor();
45666         this.el.slideOut(anchor, {
45667             callback : function(){
45668                 this.el.setStyle("z-index", "");
45669                 this.collapsedEl.slideIn(anchor, {duration:.3});
45670                 this.afterSlide();
45671                 this.el.setLocation(-10000,-10000);
45672                 this.el.hide();
45673                 this.fireEvent("collapsed", this);
45674             },
45675             scope: this,
45676             block: true
45677         });
45678     },
45679
45680     animateExpand : function(){
45681         this.beforeSlide();
45682         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45683         this.el.setStyle("z-index", 20000);
45684         this.collapsedEl.hide({
45685             duration:.1
45686         });
45687         this.el.slideIn(this.getSlideAnchor(), {
45688             callback : function(){
45689                 this.el.setStyle("z-index", "");
45690                 this.afterSlide();
45691                 if(this.split){
45692                     this.split.el.show();
45693                 }
45694                 this.fireEvent("invalidated", this);
45695                 this.fireEvent("expanded", this);
45696             },
45697             scope: this,
45698             block: true
45699         });
45700     },
45701
45702     anchors : {
45703         "west" : "left",
45704         "east" : "right",
45705         "north" : "top",
45706         "south" : "bottom"
45707     },
45708
45709     sanchors : {
45710         "west" : "l",
45711         "east" : "r",
45712         "north" : "t",
45713         "south" : "b"
45714     },
45715
45716     canchors : {
45717         "west" : "tl-tr",
45718         "east" : "tr-tl",
45719         "north" : "tl-bl",
45720         "south" : "bl-tl"
45721     },
45722
45723     getAnchor : function(){
45724         return this.anchors[this.position];
45725     },
45726
45727     getCollapseAnchor : function(){
45728         return this.canchors[this.position];
45729     },
45730
45731     getSlideAnchor : function(){
45732         return this.sanchors[this.position];
45733     },
45734
45735     getAlignAdj : function(){
45736         var cm = this.cmargins;
45737         switch(this.position){
45738             case "west":
45739                 return [0, 0];
45740             break;
45741             case "east":
45742                 return [0, 0];
45743             break;
45744             case "north":
45745                 return [0, 0];
45746             break;
45747             case "south":
45748                 return [0, 0];
45749             break;
45750         }
45751     },
45752
45753     getExpandAdj : function(){
45754         var c = this.collapsedEl, cm = this.cmargins;
45755         switch(this.position){
45756             case "west":
45757                 return [-(cm.right+c.getWidth()+cm.left), 0];
45758             break;
45759             case "east":
45760                 return [cm.right+c.getWidth()+cm.left, 0];
45761             break;
45762             case "north":
45763                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45764             break;
45765             case "south":
45766                 return [0, cm.top+cm.bottom+c.getHeight()];
45767             break;
45768         }
45769     }
45770 });/*
45771  * Based on:
45772  * Ext JS Library 1.1.1
45773  * Copyright(c) 2006-2007, Ext JS, LLC.
45774  *
45775  * Originally Released Under LGPL - original licence link has changed is not relivant.
45776  *
45777  * Fork - LGPL
45778  * <script type="text/javascript">
45779  */
45780 /*
45781  * These classes are private internal classes
45782  */
45783 Roo.CenterLayoutRegion = function(mgr, config){
45784     Roo.LayoutRegion.call(this, mgr, config, "center");
45785     this.visible = true;
45786     this.minWidth = config.minWidth || 20;
45787     this.minHeight = config.minHeight || 20;
45788 };
45789
45790 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45791     hide : function(){
45792         // center panel can't be hidden
45793     },
45794     
45795     show : function(){
45796         // center panel can't be hidden
45797     },
45798     
45799     getMinWidth: function(){
45800         return this.minWidth;
45801     },
45802     
45803     getMinHeight: function(){
45804         return this.minHeight;
45805     }
45806 });
45807
45808
45809 Roo.NorthLayoutRegion = function(mgr, config){
45810     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45811     if(this.split){
45812         this.split.placement = Roo.SplitBar.TOP;
45813         this.split.orientation = Roo.SplitBar.VERTICAL;
45814         this.split.el.addClass("x-layout-split-v");
45815     }
45816     var size = config.initialSize || config.height;
45817     if(typeof size != "undefined"){
45818         this.el.setHeight(size);
45819     }
45820 };
45821 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45822     orientation: Roo.SplitBar.VERTICAL,
45823     getBox : function(){
45824         if(this.collapsed){
45825             return this.collapsedEl.getBox();
45826         }
45827         var box = this.el.getBox();
45828         if(this.split){
45829             box.height += this.split.el.getHeight();
45830         }
45831         return box;
45832     },
45833     
45834     updateBox : function(box){
45835         if(this.split && !this.collapsed){
45836             box.height -= this.split.el.getHeight();
45837             this.split.el.setLeft(box.x);
45838             this.split.el.setTop(box.y+box.height);
45839             this.split.el.setWidth(box.width);
45840         }
45841         if(this.collapsed){
45842             this.updateBody(box.width, null);
45843         }
45844         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45845     }
45846 });
45847
45848 Roo.SouthLayoutRegion = function(mgr, config){
45849     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45850     if(this.split){
45851         this.split.placement = Roo.SplitBar.BOTTOM;
45852         this.split.orientation = Roo.SplitBar.VERTICAL;
45853         this.split.el.addClass("x-layout-split-v");
45854     }
45855     var size = config.initialSize || config.height;
45856     if(typeof size != "undefined"){
45857         this.el.setHeight(size);
45858     }
45859 };
45860 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45861     orientation: Roo.SplitBar.VERTICAL,
45862     getBox : function(){
45863         if(this.collapsed){
45864             return this.collapsedEl.getBox();
45865         }
45866         var box = this.el.getBox();
45867         if(this.split){
45868             var sh = this.split.el.getHeight();
45869             box.height += sh;
45870             box.y -= sh;
45871         }
45872         return box;
45873     },
45874     
45875     updateBox : function(box){
45876         if(this.split && !this.collapsed){
45877             var sh = this.split.el.getHeight();
45878             box.height -= sh;
45879             box.y += sh;
45880             this.split.el.setLeft(box.x);
45881             this.split.el.setTop(box.y-sh);
45882             this.split.el.setWidth(box.width);
45883         }
45884         if(this.collapsed){
45885             this.updateBody(box.width, null);
45886         }
45887         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45888     }
45889 });
45890
45891 Roo.EastLayoutRegion = function(mgr, config){
45892     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45893     if(this.split){
45894         this.split.placement = Roo.SplitBar.RIGHT;
45895         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45896         this.split.el.addClass("x-layout-split-h");
45897     }
45898     var size = config.initialSize || config.width;
45899     if(typeof size != "undefined"){
45900         this.el.setWidth(size);
45901     }
45902 };
45903 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45904     orientation: Roo.SplitBar.HORIZONTAL,
45905     getBox : function(){
45906         if(this.collapsed){
45907             return this.collapsedEl.getBox();
45908         }
45909         var box = this.el.getBox();
45910         if(this.split){
45911             var sw = this.split.el.getWidth();
45912             box.width += sw;
45913             box.x -= sw;
45914         }
45915         return box;
45916     },
45917
45918     updateBox : function(box){
45919         if(this.split && !this.collapsed){
45920             var sw = this.split.el.getWidth();
45921             box.width -= sw;
45922             this.split.el.setLeft(box.x);
45923             this.split.el.setTop(box.y);
45924             this.split.el.setHeight(box.height);
45925             box.x += sw;
45926         }
45927         if(this.collapsed){
45928             this.updateBody(null, box.height);
45929         }
45930         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45931     }
45932 });
45933
45934 Roo.WestLayoutRegion = function(mgr, config){
45935     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45936     if(this.split){
45937         this.split.placement = Roo.SplitBar.LEFT;
45938         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45939         this.split.el.addClass("x-layout-split-h");
45940     }
45941     var size = config.initialSize || config.width;
45942     if(typeof size != "undefined"){
45943         this.el.setWidth(size);
45944     }
45945 };
45946 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45947     orientation: Roo.SplitBar.HORIZONTAL,
45948     getBox : function(){
45949         if(this.collapsed){
45950             return this.collapsedEl.getBox();
45951         }
45952         var box = this.el.getBox();
45953         if(this.split){
45954             box.width += this.split.el.getWidth();
45955         }
45956         return box;
45957     },
45958     
45959     updateBox : function(box){
45960         if(this.split && !this.collapsed){
45961             var sw = this.split.el.getWidth();
45962             box.width -= sw;
45963             this.split.el.setLeft(box.x+box.width);
45964             this.split.el.setTop(box.y);
45965             this.split.el.setHeight(box.height);
45966         }
45967         if(this.collapsed){
45968             this.updateBody(null, box.height);
45969         }
45970         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45971     }
45972 });
45973 /*
45974  * Based on:
45975  * Ext JS Library 1.1.1
45976  * Copyright(c) 2006-2007, Ext JS, LLC.
45977  *
45978  * Originally Released Under LGPL - original licence link has changed is not relivant.
45979  *
45980  * Fork - LGPL
45981  * <script type="text/javascript">
45982  */
45983  
45984  
45985 /*
45986  * Private internal class for reading and applying state
45987  */
45988 Roo.LayoutStateManager = function(layout){
45989      // default empty state
45990      this.state = {
45991         north: {},
45992         south: {},
45993         east: {},
45994         west: {}       
45995     };
45996 };
45997
45998 Roo.LayoutStateManager.prototype = {
45999     init : function(layout, provider){
46000         this.provider = provider;
46001         var state = provider.get(layout.id+"-layout-state");
46002         if(state){
46003             var wasUpdating = layout.isUpdating();
46004             if(!wasUpdating){
46005                 layout.beginUpdate();
46006             }
46007             for(var key in state){
46008                 if(typeof state[key] != "function"){
46009                     var rstate = state[key];
46010                     var r = layout.getRegion(key);
46011                     if(r && rstate){
46012                         if(rstate.size){
46013                             r.resizeTo(rstate.size);
46014                         }
46015                         if(rstate.collapsed == true){
46016                             r.collapse(true);
46017                         }else{
46018                             r.expand(null, true);
46019                         }
46020                     }
46021                 }
46022             }
46023             if(!wasUpdating){
46024                 layout.endUpdate();
46025             }
46026             this.state = state; 
46027         }
46028         this.layout = layout;
46029         layout.on("regionresized", this.onRegionResized, this);
46030         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46031         layout.on("regionexpanded", this.onRegionExpanded, this);
46032     },
46033     
46034     storeState : function(){
46035         this.provider.set(this.layout.id+"-layout-state", this.state);
46036     },
46037     
46038     onRegionResized : function(region, newSize){
46039         this.state[region.getPosition()].size = newSize;
46040         this.storeState();
46041     },
46042     
46043     onRegionCollapsed : function(region){
46044         this.state[region.getPosition()].collapsed = true;
46045         this.storeState();
46046     },
46047     
46048     onRegionExpanded : function(region){
46049         this.state[region.getPosition()].collapsed = false;
46050         this.storeState();
46051     }
46052 };/*
46053  * Based on:
46054  * Ext JS Library 1.1.1
46055  * Copyright(c) 2006-2007, Ext JS, LLC.
46056  *
46057  * Originally Released Under LGPL - original licence link has changed is not relivant.
46058  *
46059  * Fork - LGPL
46060  * <script type="text/javascript">
46061  */
46062 /**
46063  * @class Roo.ContentPanel
46064  * @extends Roo.util.Observable
46065  * A basic ContentPanel element.
46066  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46067  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46068  * @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
46069  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46070  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46071  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46072  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46073  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46074  * @cfg {String} title          The title for this panel
46075  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46076  * @cfg {String} url            Calls {@link #setUrl} with this value
46077  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46078  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46079  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46080  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46081
46082  * @constructor
46083  * Create a new ContentPanel.
46084  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46085  * @param {String/Object} config A string to set only the title or a config object
46086  * @param {String} content (optional) Set the HTML content for this panel
46087  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46088  */
46089 Roo.ContentPanel = function(el, config, content){
46090     
46091      
46092     /*
46093     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46094         config = el;
46095         el = Roo.id();
46096     }
46097     if (config && config.parentLayout) { 
46098         el = config.parentLayout.el.createChild(); 
46099     }
46100     */
46101     if(el.autoCreate){ // xtype is available if this is called from factory
46102         config = el;
46103         el = Roo.id();
46104     }
46105     this.el = Roo.get(el);
46106     if(!this.el && config && config.autoCreate){
46107         if(typeof config.autoCreate == "object"){
46108             if(!config.autoCreate.id){
46109                 config.autoCreate.id = config.id||el;
46110             }
46111             this.el = Roo.DomHelper.append(document.body,
46112                         config.autoCreate, true);
46113         }else{
46114             this.el = Roo.DomHelper.append(document.body,
46115                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46116         }
46117     }
46118     this.closable = false;
46119     this.loaded = false;
46120     this.active = false;
46121     if(typeof config == "string"){
46122         this.title = config;
46123     }else{
46124         Roo.apply(this, config);
46125     }
46126     
46127     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46128         this.wrapEl = this.el.wrap();
46129         this.toolbar.container = this.el.insertSibling(false, 'before');
46130         this.toolbar = new Roo.Toolbar(this.toolbar);
46131     }
46132     
46133     
46134     
46135     if(this.resizeEl){
46136         this.resizeEl = Roo.get(this.resizeEl, true);
46137     }else{
46138         this.resizeEl = this.el;
46139     }
46140     this.addEvents({
46141         /**
46142          * @event activate
46143          * Fires when this panel is activated. 
46144          * @param {Roo.ContentPanel} this
46145          */
46146         "activate" : true,
46147         /**
46148          * @event deactivate
46149          * Fires when this panel is activated. 
46150          * @param {Roo.ContentPanel} this
46151          */
46152         "deactivate" : true,
46153
46154         /**
46155          * @event resize
46156          * Fires when this panel is resized if fitToFrame is true.
46157          * @param {Roo.ContentPanel} this
46158          * @param {Number} width The width after any component adjustments
46159          * @param {Number} height The height after any component adjustments
46160          */
46161         "resize" : true,
46162         
46163          /**
46164          * @event render
46165          * Fires when this tab is created
46166          * @param {Roo.ContentPanel} this
46167          */
46168         "render" : true
46169         
46170         
46171         
46172     });
46173     if(this.autoScroll){
46174         this.resizeEl.setStyle("overflow", "auto");
46175     } else {
46176         // fix randome scrolling
46177         this.el.on('scroll', function() {
46178             Roo.log('fix random scolling');
46179             this.scrollTo('top',0); 
46180         });
46181     }
46182     content = content || this.content;
46183     if(content){
46184         this.setContent(content);
46185     }
46186     if(config && config.url){
46187         this.setUrl(this.url, this.params, this.loadOnce);
46188     }
46189     
46190     
46191     
46192     Roo.ContentPanel.superclass.constructor.call(this);
46193     
46194     this.fireEvent('render', this);
46195 };
46196
46197 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46198     tabTip:'',
46199     setRegion : function(region){
46200         this.region = region;
46201         if(region){
46202            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46203         }else{
46204            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46205         } 
46206     },
46207     
46208     /**
46209      * Returns the toolbar for this Panel if one was configured. 
46210      * @return {Roo.Toolbar} 
46211      */
46212     getToolbar : function(){
46213         return this.toolbar;
46214     },
46215     
46216     setActiveState : function(active){
46217         this.active = active;
46218         if(!active){
46219             this.fireEvent("deactivate", this);
46220         }else{
46221             this.fireEvent("activate", this);
46222         }
46223     },
46224     /**
46225      * Updates this panel's element
46226      * @param {String} content The new content
46227      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46228     */
46229     setContent : function(content, loadScripts){
46230         this.el.update(content, loadScripts);
46231     },
46232
46233     ignoreResize : function(w, h){
46234         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46235             return true;
46236         }else{
46237             this.lastSize = {width: w, height: h};
46238             return false;
46239         }
46240     },
46241     /**
46242      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46243      * @return {Roo.UpdateManager} The UpdateManager
46244      */
46245     getUpdateManager : function(){
46246         return this.el.getUpdateManager();
46247     },
46248      /**
46249      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46250      * @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:
46251 <pre><code>
46252 panel.load({
46253     url: "your-url.php",
46254     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46255     callback: yourFunction,
46256     scope: yourObject, //(optional scope)
46257     discardUrl: false,
46258     nocache: false,
46259     text: "Loading...",
46260     timeout: 30,
46261     scripts: false
46262 });
46263 </code></pre>
46264      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46265      * 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.
46266      * @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}
46267      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46268      * @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.
46269      * @return {Roo.ContentPanel} this
46270      */
46271     load : function(){
46272         var um = this.el.getUpdateManager();
46273         um.update.apply(um, arguments);
46274         return this;
46275     },
46276
46277
46278     /**
46279      * 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.
46280      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46281      * @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)
46282      * @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)
46283      * @return {Roo.UpdateManager} The UpdateManager
46284      */
46285     setUrl : function(url, params, loadOnce){
46286         if(this.refreshDelegate){
46287             this.removeListener("activate", this.refreshDelegate);
46288         }
46289         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46290         this.on("activate", this.refreshDelegate);
46291         return this.el.getUpdateManager();
46292     },
46293     
46294     _handleRefresh : function(url, params, loadOnce){
46295         if(!loadOnce || !this.loaded){
46296             var updater = this.el.getUpdateManager();
46297             updater.update(url, params, this._setLoaded.createDelegate(this));
46298         }
46299     },
46300     
46301     _setLoaded : function(){
46302         this.loaded = true;
46303     }, 
46304     
46305     /**
46306      * Returns this panel's id
46307      * @return {String} 
46308      */
46309     getId : function(){
46310         return this.el.id;
46311     },
46312     
46313     /** 
46314      * Returns this panel's element - used by regiosn to add.
46315      * @return {Roo.Element} 
46316      */
46317     getEl : function(){
46318         return this.wrapEl || this.el;
46319     },
46320     
46321     adjustForComponents : function(width, height){
46322         if(this.resizeEl != this.el){
46323             width -= this.el.getFrameWidth('lr');
46324             height -= this.el.getFrameWidth('tb');
46325         }
46326         if(this.toolbar){
46327             var te = this.toolbar.getEl();
46328             height -= te.getHeight();
46329             te.setWidth(width);
46330         }
46331         if(this.adjustments){
46332             width += this.adjustments[0];
46333             height += this.adjustments[1];
46334         }
46335         return {"width": width, "height": height};
46336     },
46337     
46338     setSize : function(width, height){
46339         if(this.fitToFrame && !this.ignoreResize(width, height)){
46340             if(this.fitContainer && this.resizeEl != this.el){
46341                 this.el.setSize(width, height);
46342             }
46343             var size = this.adjustForComponents(width, height);
46344             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46345             this.fireEvent('resize', this, size.width, size.height);
46346         }
46347     },
46348     
46349     /**
46350      * Returns this panel's title
46351      * @return {String} 
46352      */
46353     getTitle : function(){
46354         return this.title;
46355     },
46356     
46357     /**
46358      * Set this panel's title
46359      * @param {String} title
46360      */
46361     setTitle : function(title){
46362         this.title = title;
46363         if(this.region){
46364             this.region.updatePanelTitle(this, title);
46365         }
46366     },
46367     
46368     /**
46369      * Returns true is this panel was configured to be closable
46370      * @return {Boolean} 
46371      */
46372     isClosable : function(){
46373         return this.closable;
46374     },
46375     
46376     beforeSlide : function(){
46377         this.el.clip();
46378         this.resizeEl.clip();
46379     },
46380     
46381     afterSlide : function(){
46382         this.el.unclip();
46383         this.resizeEl.unclip();
46384     },
46385     
46386     /**
46387      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46388      *   Will fail silently if the {@link #setUrl} method has not been called.
46389      *   This does not activate the panel, just updates its content.
46390      */
46391     refresh : function(){
46392         if(this.refreshDelegate){
46393            this.loaded = false;
46394            this.refreshDelegate();
46395         }
46396     },
46397     
46398     /**
46399      * Destroys this panel
46400      */
46401     destroy : function(){
46402         this.el.removeAllListeners();
46403         var tempEl = document.createElement("span");
46404         tempEl.appendChild(this.el.dom);
46405         tempEl.innerHTML = "";
46406         this.el.remove();
46407         this.el = null;
46408     },
46409     
46410     /**
46411      * form - if the content panel contains a form - this is a reference to it.
46412      * @type {Roo.form.Form}
46413      */
46414     form : false,
46415     /**
46416      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46417      *    This contains a reference to it.
46418      * @type {Roo.View}
46419      */
46420     view : false,
46421     
46422       /**
46423      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46424      * <pre><code>
46425
46426 layout.addxtype({
46427        xtype : 'Form',
46428        items: [ .... ]
46429    }
46430 );
46431
46432 </code></pre>
46433      * @param {Object} cfg Xtype definition of item to add.
46434      */
46435     
46436     addxtype : function(cfg) {
46437         // add form..
46438         if (cfg.xtype.match(/^Form$/)) {
46439             var el = this.el.createChild();
46440
46441             this.form = new  Roo.form.Form(cfg);
46442             
46443             
46444             if ( this.form.allItems.length) this.form.render(el.dom);
46445             return this.form;
46446         }
46447         // should only have one of theses..
46448         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46449             // views..
46450             cfg.el = this.el.appendChild(document.createElement("div"));
46451             // factory?
46452             
46453             var ret = new Roo.factory(cfg);
46454             ret.render && ret.render(false, ''); // render blank..
46455             this.view = ret;
46456             return ret;
46457         }
46458         return false;
46459     }
46460 });
46461
46462 /**
46463  * @class Roo.GridPanel
46464  * @extends Roo.ContentPanel
46465  * @constructor
46466  * Create a new GridPanel.
46467  * @param {Roo.grid.Grid} grid The grid for this panel
46468  * @param {String/Object} config A string to set only the panel's title, or a config object
46469  */
46470 Roo.GridPanel = function(grid, config){
46471     
46472   
46473     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46474         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46475         
46476     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46477     
46478     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46479     
46480     if(this.toolbar){
46481         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46482     }
46483     // xtype created footer. - not sure if will work as we normally have to render first..
46484     if (this.footer && !this.footer.el && this.footer.xtype) {
46485         
46486         this.footer.container = this.grid.getView().getFooterPanel(true);
46487         this.footer.dataSource = this.grid.dataSource;
46488         this.footer = Roo.factory(this.footer, Roo);
46489         
46490     }
46491     
46492     grid.monitorWindowResize = false; // turn off autosizing
46493     grid.autoHeight = false;
46494     grid.autoWidth = false;
46495     this.grid = grid;
46496     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46497 };
46498
46499 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46500     getId : function(){
46501         return this.grid.id;
46502     },
46503     
46504     /**
46505      * Returns the grid for this panel
46506      * @return {Roo.grid.Grid} 
46507      */
46508     getGrid : function(){
46509         return this.grid;    
46510     },
46511     
46512     setSize : function(width, height){
46513         if(!this.ignoreResize(width, height)){
46514             var grid = this.grid;
46515             var size = this.adjustForComponents(width, height);
46516             grid.getGridEl().setSize(size.width, size.height);
46517             grid.autoSize();
46518         }
46519     },
46520     
46521     beforeSlide : function(){
46522         this.grid.getView().scroller.clip();
46523     },
46524     
46525     afterSlide : function(){
46526         this.grid.getView().scroller.unclip();
46527     },
46528     
46529     destroy : function(){
46530         this.grid.destroy();
46531         delete this.grid;
46532         Roo.GridPanel.superclass.destroy.call(this); 
46533     }
46534 });
46535
46536
46537 /**
46538  * @class Roo.NestedLayoutPanel
46539  * @extends Roo.ContentPanel
46540  * @constructor
46541  * Create a new NestedLayoutPanel.
46542  * 
46543  * 
46544  * @param {Roo.BorderLayout} layout The layout for this panel
46545  * @param {String/Object} config A string to set only the title or a config object
46546  */
46547 Roo.NestedLayoutPanel = function(layout, config)
46548 {
46549     // construct with only one argument..
46550     /* FIXME - implement nicer consturctors
46551     if (layout.layout) {
46552         config = layout;
46553         layout = config.layout;
46554         delete config.layout;
46555     }
46556     if (layout.xtype && !layout.getEl) {
46557         // then layout needs constructing..
46558         layout = Roo.factory(layout, Roo);
46559     }
46560     */
46561     
46562     
46563     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46564     
46565     layout.monitorWindowResize = false; // turn off autosizing
46566     this.layout = layout;
46567     this.layout.getEl().addClass("x-layout-nested-layout");
46568     
46569     
46570     
46571     
46572 };
46573
46574 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46575
46576     setSize : function(width, height){
46577         if(!this.ignoreResize(width, height)){
46578             var size = this.adjustForComponents(width, height);
46579             var el = this.layout.getEl();
46580             el.setSize(size.width, size.height);
46581             var touch = el.dom.offsetWidth;
46582             this.layout.layout();
46583             // ie requires a double layout on the first pass
46584             if(Roo.isIE && !this.initialized){
46585                 this.initialized = true;
46586                 this.layout.layout();
46587             }
46588         }
46589     },
46590     
46591     // activate all subpanels if not currently active..
46592     
46593     setActiveState : function(active){
46594         this.active = active;
46595         if(!active){
46596             this.fireEvent("deactivate", this);
46597             return;
46598         }
46599         
46600         this.fireEvent("activate", this);
46601         // not sure if this should happen before or after..
46602         if (!this.layout) {
46603             return; // should not happen..
46604         }
46605         var reg = false;
46606         for (var r in this.layout.regions) {
46607             reg = this.layout.getRegion(r);
46608             if (reg.getActivePanel()) {
46609                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46610                 reg.setActivePanel(reg.getActivePanel());
46611                 continue;
46612             }
46613             if (!reg.panels.length) {
46614                 continue;
46615             }
46616             reg.showPanel(reg.getPanel(0));
46617         }
46618         
46619         
46620         
46621         
46622     },
46623     
46624     /**
46625      * Returns the nested BorderLayout for this panel
46626      * @return {Roo.BorderLayout} 
46627      */
46628     getLayout : function(){
46629         return this.layout;
46630     },
46631     
46632      /**
46633      * Adds a xtype elements to the layout of the nested panel
46634      * <pre><code>
46635
46636 panel.addxtype({
46637        xtype : 'ContentPanel',
46638        region: 'west',
46639        items: [ .... ]
46640    }
46641 );
46642
46643 panel.addxtype({
46644         xtype : 'NestedLayoutPanel',
46645         region: 'west',
46646         layout: {
46647            center: { },
46648            west: { }   
46649         },
46650         items : [ ... list of content panels or nested layout panels.. ]
46651    }
46652 );
46653 </code></pre>
46654      * @param {Object} cfg Xtype definition of item to add.
46655      */
46656     addxtype : function(cfg) {
46657         return this.layout.addxtype(cfg);
46658     
46659     }
46660 });
46661
46662 Roo.ScrollPanel = function(el, config, content){
46663     config = config || {};
46664     config.fitToFrame = true;
46665     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46666     
46667     this.el.dom.style.overflow = "hidden";
46668     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46669     this.el.removeClass("x-layout-inactive-content");
46670     this.el.on("mousewheel", this.onWheel, this);
46671
46672     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46673     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46674     up.unselectable(); down.unselectable();
46675     up.on("click", this.scrollUp, this);
46676     down.on("click", this.scrollDown, this);
46677     up.addClassOnOver("x-scroller-btn-over");
46678     down.addClassOnOver("x-scroller-btn-over");
46679     up.addClassOnClick("x-scroller-btn-click");
46680     down.addClassOnClick("x-scroller-btn-click");
46681     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46682
46683     this.resizeEl = this.el;
46684     this.el = wrap; this.up = up; this.down = down;
46685 };
46686
46687 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46688     increment : 100,
46689     wheelIncrement : 5,
46690     scrollUp : function(){
46691         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46692     },
46693
46694     scrollDown : function(){
46695         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46696     },
46697
46698     afterScroll : function(){
46699         var el = this.resizeEl;
46700         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46701         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46702         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46703     },
46704
46705     setSize : function(){
46706         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46707         this.afterScroll();
46708     },
46709
46710     onWheel : function(e){
46711         var d = e.getWheelDelta();
46712         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46713         this.afterScroll();
46714         e.stopEvent();
46715     },
46716
46717     setContent : function(content, loadScripts){
46718         this.resizeEl.update(content, loadScripts);
46719     }
46720
46721 });
46722
46723
46724
46725
46726
46727
46728
46729
46730
46731 /**
46732  * @class Roo.TreePanel
46733  * @extends Roo.ContentPanel
46734  * @constructor
46735  * Create a new TreePanel. - defaults to fit/scoll contents.
46736  * @param {String/Object} config A string to set only the panel's title, or a config object
46737  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46738  */
46739 Roo.TreePanel = function(config){
46740     var el = config.el;
46741     var tree = config.tree;
46742     delete config.tree; 
46743     delete config.el; // hopefull!
46744     
46745     // wrapper for IE7 strict & safari scroll issue
46746     
46747     var treeEl = el.createChild();
46748     config.resizeEl = treeEl;
46749     
46750     
46751     
46752     Roo.TreePanel.superclass.constructor.call(this, el, config);
46753  
46754  
46755     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46756     //console.log(tree);
46757     this.on('activate', function()
46758     {
46759         if (this.tree.rendered) {
46760             return;
46761         }
46762         //console.log('render tree');
46763         this.tree.render();
46764     });
46765     
46766     this.on('resize',  function (cp, w, h) {
46767             this.tree.innerCt.setWidth(w);
46768             this.tree.innerCt.setHeight(h);
46769             this.tree.innerCt.setStyle('overflow-y', 'auto');
46770     });
46771
46772         
46773     
46774 };
46775
46776 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46777     fitToFrame : true,
46778     autoScroll : true
46779 });
46780
46781
46782
46783
46784
46785
46786
46787
46788
46789
46790
46791 /*
46792  * Based on:
46793  * Ext JS Library 1.1.1
46794  * Copyright(c) 2006-2007, Ext JS, LLC.
46795  *
46796  * Originally Released Under LGPL - original licence link has changed is not relivant.
46797  *
46798  * Fork - LGPL
46799  * <script type="text/javascript">
46800  */
46801  
46802
46803 /**
46804  * @class Roo.ReaderLayout
46805  * @extends Roo.BorderLayout
46806  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46807  * center region containing two nested regions (a top one for a list view and one for item preview below),
46808  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46809  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46810  * expedites the setup of the overall layout and regions for this common application style.
46811  * Example:
46812  <pre><code>
46813 var reader = new Roo.ReaderLayout();
46814 var CP = Roo.ContentPanel;  // shortcut for adding
46815
46816 reader.beginUpdate();
46817 reader.add("north", new CP("north", "North"));
46818 reader.add("west", new CP("west", {title: "West"}));
46819 reader.add("east", new CP("east", {title: "East"}));
46820
46821 reader.regions.listView.add(new CP("listView", "List"));
46822 reader.regions.preview.add(new CP("preview", "Preview"));
46823 reader.endUpdate();
46824 </code></pre>
46825 * @constructor
46826 * Create a new ReaderLayout
46827 * @param {Object} config Configuration options
46828 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46829 * document.body if omitted)
46830 */
46831 Roo.ReaderLayout = function(config, renderTo){
46832     var c = config || {size:{}};
46833     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46834         north: c.north !== false ? Roo.apply({
46835             split:false,
46836             initialSize: 32,
46837             titlebar: false
46838         }, c.north) : false,
46839         west: c.west !== false ? Roo.apply({
46840             split:true,
46841             initialSize: 200,
46842             minSize: 175,
46843             maxSize: 400,
46844             titlebar: true,
46845             collapsible: true,
46846             animate: true,
46847             margins:{left:5,right:0,bottom:5,top:5},
46848             cmargins:{left:5,right:5,bottom:5,top:5}
46849         }, c.west) : false,
46850         east: c.east !== false ? Roo.apply({
46851             split:true,
46852             initialSize: 200,
46853             minSize: 175,
46854             maxSize: 400,
46855             titlebar: true,
46856             collapsible: true,
46857             animate: true,
46858             margins:{left:0,right:5,bottom:5,top:5},
46859             cmargins:{left:5,right:5,bottom:5,top:5}
46860         }, c.east) : false,
46861         center: Roo.apply({
46862             tabPosition: 'top',
46863             autoScroll:false,
46864             closeOnTab: true,
46865             titlebar:false,
46866             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46867         }, c.center)
46868     });
46869
46870     this.el.addClass('x-reader');
46871
46872     this.beginUpdate();
46873
46874     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46875         south: c.preview !== false ? Roo.apply({
46876             split:true,
46877             initialSize: 200,
46878             minSize: 100,
46879             autoScroll:true,
46880             collapsible:true,
46881             titlebar: true,
46882             cmargins:{top:5,left:0, right:0, bottom:0}
46883         }, c.preview) : false,
46884         center: Roo.apply({
46885             autoScroll:false,
46886             titlebar:false,
46887             minHeight:200
46888         }, c.listView)
46889     });
46890     this.add('center', new Roo.NestedLayoutPanel(inner,
46891             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46892
46893     this.endUpdate();
46894
46895     this.regions.preview = inner.getRegion('south');
46896     this.regions.listView = inner.getRegion('center');
46897 };
46898
46899 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46900  * Based on:
46901  * Ext JS Library 1.1.1
46902  * Copyright(c) 2006-2007, Ext JS, LLC.
46903  *
46904  * Originally Released Under LGPL - original licence link has changed is not relivant.
46905  *
46906  * Fork - LGPL
46907  * <script type="text/javascript">
46908  */
46909  
46910 /**
46911  * @class Roo.grid.Grid
46912  * @extends Roo.util.Observable
46913  * This class represents the primary interface of a component based grid control.
46914  * <br><br>Usage:<pre><code>
46915  var grid = new Roo.grid.Grid("my-container-id", {
46916      ds: myDataStore,
46917      cm: myColModel,
46918      selModel: mySelectionModel,
46919      autoSizeColumns: true,
46920      monitorWindowResize: false,
46921      trackMouseOver: true
46922  });
46923  // set any options
46924  grid.render();
46925  * </code></pre>
46926  * <b>Common Problems:</b><br/>
46927  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46928  * element will correct this<br/>
46929  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46930  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46931  * are unpredictable.<br/>
46932  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46933  * grid to calculate dimensions/offsets.<br/>
46934   * @constructor
46935  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46936  * The container MUST have some type of size defined for the grid to fill. The container will be
46937  * automatically set to position relative if it isn't already.
46938  * @param {Object} config A config object that sets properties on this grid.
46939  */
46940 Roo.grid.Grid = function(container, config){
46941         // initialize the container
46942         this.container = Roo.get(container);
46943         this.container.update("");
46944         this.container.setStyle("overflow", "hidden");
46945     this.container.addClass('x-grid-container');
46946
46947     this.id = this.container.id;
46948
46949     Roo.apply(this, config);
46950     // check and correct shorthanded configs
46951     if(this.ds){
46952         this.dataSource = this.ds;
46953         delete this.ds;
46954     }
46955     if(this.cm){
46956         this.colModel = this.cm;
46957         delete this.cm;
46958     }
46959     if(this.sm){
46960         this.selModel = this.sm;
46961         delete this.sm;
46962     }
46963
46964     if (this.selModel) {
46965         this.selModel = Roo.factory(this.selModel, Roo.grid);
46966         this.sm = this.selModel;
46967         this.sm.xmodule = this.xmodule || false;
46968     }
46969     if (typeof(this.colModel.config) == 'undefined') {
46970         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46971         this.cm = this.colModel;
46972         this.cm.xmodule = this.xmodule || false;
46973     }
46974     if (this.dataSource) {
46975         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46976         this.ds = this.dataSource;
46977         this.ds.xmodule = this.xmodule || false;
46978          
46979     }
46980     
46981     
46982     
46983     if(this.width){
46984         this.container.setWidth(this.width);
46985     }
46986
46987     if(this.height){
46988         this.container.setHeight(this.height);
46989     }
46990     /** @private */
46991         this.addEvents({
46992         // raw events
46993         /**
46994          * @event click
46995          * The raw click event for the entire grid.
46996          * @param {Roo.EventObject} e
46997          */
46998         "click" : true,
46999         /**
47000          * @event dblclick
47001          * The raw dblclick event for the entire grid.
47002          * @param {Roo.EventObject} e
47003          */
47004         "dblclick" : true,
47005         /**
47006          * @event contextmenu
47007          * The raw contextmenu event for the entire grid.
47008          * @param {Roo.EventObject} e
47009          */
47010         "contextmenu" : true,
47011         /**
47012          * @event mousedown
47013          * The raw mousedown event for the entire grid.
47014          * @param {Roo.EventObject} e
47015          */
47016         "mousedown" : true,
47017         /**
47018          * @event mouseup
47019          * The raw mouseup event for the entire grid.
47020          * @param {Roo.EventObject} e
47021          */
47022         "mouseup" : true,
47023         /**
47024          * @event mouseover
47025          * The raw mouseover event for the entire grid.
47026          * @param {Roo.EventObject} e
47027          */
47028         "mouseover" : true,
47029         /**
47030          * @event mouseout
47031          * The raw mouseout event for the entire grid.
47032          * @param {Roo.EventObject} e
47033          */
47034         "mouseout" : true,
47035         /**
47036          * @event keypress
47037          * The raw keypress event for the entire grid.
47038          * @param {Roo.EventObject} e
47039          */
47040         "keypress" : true,
47041         /**
47042          * @event keydown
47043          * The raw keydown event for the entire grid.
47044          * @param {Roo.EventObject} e
47045          */
47046         "keydown" : true,
47047
47048         // custom events
47049
47050         /**
47051          * @event cellclick
47052          * Fires when a cell is clicked
47053          * @param {Grid} this
47054          * @param {Number} rowIndex
47055          * @param {Number} columnIndex
47056          * @param {Roo.EventObject} e
47057          */
47058         "cellclick" : true,
47059         /**
47060          * @event celldblclick
47061          * Fires when a cell is double clicked
47062          * @param {Grid} this
47063          * @param {Number} rowIndex
47064          * @param {Number} columnIndex
47065          * @param {Roo.EventObject} e
47066          */
47067         "celldblclick" : true,
47068         /**
47069          * @event rowclick
47070          * Fires when a row is clicked
47071          * @param {Grid} this
47072          * @param {Number} rowIndex
47073          * @param {Roo.EventObject} e
47074          */
47075         "rowclick" : true,
47076         /**
47077          * @event rowdblclick
47078          * Fires when a row is double clicked
47079          * @param {Grid} this
47080          * @param {Number} rowIndex
47081          * @param {Roo.EventObject} e
47082          */
47083         "rowdblclick" : true,
47084         /**
47085          * @event headerclick
47086          * Fires when a header is clicked
47087          * @param {Grid} this
47088          * @param {Number} columnIndex
47089          * @param {Roo.EventObject} e
47090          */
47091         "headerclick" : true,
47092         /**
47093          * @event headerdblclick
47094          * Fires when a header cell is double clicked
47095          * @param {Grid} this
47096          * @param {Number} columnIndex
47097          * @param {Roo.EventObject} e
47098          */
47099         "headerdblclick" : true,
47100         /**
47101          * @event rowcontextmenu
47102          * Fires when a row is right clicked
47103          * @param {Grid} this
47104          * @param {Number} rowIndex
47105          * @param {Roo.EventObject} e
47106          */
47107         "rowcontextmenu" : true,
47108         /**
47109          * @event cellcontextmenu
47110          * Fires when a cell is right clicked
47111          * @param {Grid} this
47112          * @param {Number} rowIndex
47113          * @param {Number} cellIndex
47114          * @param {Roo.EventObject} e
47115          */
47116          "cellcontextmenu" : true,
47117         /**
47118          * @event headercontextmenu
47119          * Fires when a header is right clicked
47120          * @param {Grid} this
47121          * @param {Number} columnIndex
47122          * @param {Roo.EventObject} e
47123          */
47124         "headercontextmenu" : true,
47125         /**
47126          * @event bodyscroll
47127          * Fires when the body element is scrolled
47128          * @param {Number} scrollLeft
47129          * @param {Number} scrollTop
47130          */
47131         "bodyscroll" : true,
47132         /**
47133          * @event columnresize
47134          * Fires when the user resizes a column
47135          * @param {Number} columnIndex
47136          * @param {Number} newSize
47137          */
47138         "columnresize" : true,
47139         /**
47140          * @event columnmove
47141          * Fires when the user moves a column
47142          * @param {Number} oldIndex
47143          * @param {Number} newIndex
47144          */
47145         "columnmove" : true,
47146         /**
47147          * @event startdrag
47148          * Fires when row(s) start being dragged
47149          * @param {Grid} this
47150          * @param {Roo.GridDD} dd The drag drop object
47151          * @param {event} e The raw browser event
47152          */
47153         "startdrag" : true,
47154         /**
47155          * @event enddrag
47156          * Fires when a drag operation is complete
47157          * @param {Grid} this
47158          * @param {Roo.GridDD} dd The drag drop object
47159          * @param {event} e The raw browser event
47160          */
47161         "enddrag" : true,
47162         /**
47163          * @event dragdrop
47164          * Fires when dragged row(s) are dropped on a valid DD target
47165          * @param {Grid} this
47166          * @param {Roo.GridDD} dd The drag drop object
47167          * @param {String} targetId The target drag drop object
47168          * @param {event} e The raw browser event
47169          */
47170         "dragdrop" : true,
47171         /**
47172          * @event dragover
47173          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47174          * @param {Grid} this
47175          * @param {Roo.GridDD} dd The drag drop object
47176          * @param {String} targetId The target drag drop object
47177          * @param {event} e The raw browser event
47178          */
47179         "dragover" : true,
47180         /**
47181          * @event dragenter
47182          *  Fires when the dragged row(s) first cross another DD target while being dragged
47183          * @param {Grid} this
47184          * @param {Roo.GridDD} dd The drag drop object
47185          * @param {String} targetId The target drag drop object
47186          * @param {event} e The raw browser event
47187          */
47188         "dragenter" : true,
47189         /**
47190          * @event dragout
47191          * Fires when the dragged row(s) leave another DD target while being dragged
47192          * @param {Grid} this
47193          * @param {Roo.GridDD} dd The drag drop object
47194          * @param {String} targetId The target drag drop object
47195          * @param {event} e The raw browser event
47196          */
47197         "dragout" : true,
47198         /**
47199          * @event rowclass
47200          * Fires when a row is rendered, so you can change add a style to it.
47201          * @param {GridView} gridview   The grid view
47202          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47203          */
47204         'rowclass' : true,
47205
47206         /**
47207          * @event render
47208          * Fires when the grid is rendered
47209          * @param {Grid} grid
47210          */
47211         'render' : true
47212     });
47213
47214     Roo.grid.Grid.superclass.constructor.call(this);
47215 };
47216 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47217     
47218     /**
47219      * @cfg {String} ddGroup - drag drop group.
47220      */
47221
47222     /**
47223      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47224      */
47225     minColumnWidth : 25,
47226
47227     /**
47228      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47229      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47230      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47231      */
47232     autoSizeColumns : false,
47233
47234     /**
47235      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47236      */
47237     autoSizeHeaders : true,
47238
47239     /**
47240      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47241      */
47242     monitorWindowResize : true,
47243
47244     /**
47245      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47246      * rows measured to get a columns size. Default is 0 (all rows).
47247      */
47248     maxRowsToMeasure : 0,
47249
47250     /**
47251      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47252      */
47253     trackMouseOver : true,
47254
47255     /**
47256     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47257     */
47258     
47259     /**
47260     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47261     */
47262     enableDragDrop : false,
47263     
47264     /**
47265     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47266     */
47267     enableColumnMove : true,
47268     
47269     /**
47270     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47271     */
47272     enableColumnHide : true,
47273     
47274     /**
47275     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47276     */
47277     enableRowHeightSync : false,
47278     
47279     /**
47280     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47281     */
47282     stripeRows : true,
47283     
47284     /**
47285     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47286     */
47287     autoHeight : false,
47288
47289     /**
47290      * @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.
47291      */
47292     autoExpandColumn : false,
47293
47294     /**
47295     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47296     * Default is 50.
47297     */
47298     autoExpandMin : 50,
47299
47300     /**
47301     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47302     */
47303     autoExpandMax : 1000,
47304
47305     /**
47306     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47307     */
47308     view : null,
47309
47310     /**
47311     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47312     */
47313     loadMask : false,
47314     /**
47315     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47316     */
47317     dropTarget: false,
47318     
47319    
47320     
47321     // private
47322     rendered : false,
47323
47324     /**
47325     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47326     * of a fixed width. Default is false.
47327     */
47328     /**
47329     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47330     */
47331     /**
47332      * Called once after all setup has been completed and the grid is ready to be rendered.
47333      * @return {Roo.grid.Grid} this
47334      */
47335     render : function()
47336     {
47337         var c = this.container;
47338         // try to detect autoHeight/width mode
47339         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47340             this.autoHeight = true;
47341         }
47342         var view = this.getView();
47343         view.init(this);
47344
47345         c.on("click", this.onClick, this);
47346         c.on("dblclick", this.onDblClick, this);
47347         c.on("contextmenu", this.onContextMenu, this);
47348         c.on("keydown", this.onKeyDown, this);
47349
47350         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47351
47352         this.getSelectionModel().init(this);
47353
47354         view.render();
47355
47356         if(this.loadMask){
47357             this.loadMask = new Roo.LoadMask(this.container,
47358                     Roo.apply({store:this.dataSource}, this.loadMask));
47359         }
47360         
47361         
47362         if (this.toolbar && this.toolbar.xtype) {
47363             this.toolbar.container = this.getView().getHeaderPanel(true);
47364             this.toolbar = new Roo.Toolbar(this.toolbar);
47365         }
47366         if (this.footer && this.footer.xtype) {
47367             this.footer.dataSource = this.getDataSource();
47368             this.footer.container = this.getView().getFooterPanel(true);
47369             this.footer = Roo.factory(this.footer, Roo);
47370         }
47371         if (this.dropTarget && this.dropTarget.xtype) {
47372             delete this.dropTarget.xtype;
47373             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47374         }
47375         
47376         
47377         this.rendered = true;
47378         this.fireEvent('render', this);
47379         return this;
47380     },
47381
47382         /**
47383          * Reconfigures the grid to use a different Store and Column Model.
47384          * The View will be bound to the new objects and refreshed.
47385          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47386          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47387          */
47388     reconfigure : function(dataSource, colModel){
47389         if(this.loadMask){
47390             this.loadMask.destroy();
47391             this.loadMask = new Roo.LoadMask(this.container,
47392                     Roo.apply({store:dataSource}, this.loadMask));
47393         }
47394         this.view.bind(dataSource, colModel);
47395         this.dataSource = dataSource;
47396         this.colModel = colModel;
47397         this.view.refresh(true);
47398     },
47399
47400     // private
47401     onKeyDown : function(e){
47402         this.fireEvent("keydown", e);
47403     },
47404
47405     /**
47406      * Destroy this grid.
47407      * @param {Boolean} removeEl True to remove the element
47408      */
47409     destroy : function(removeEl, keepListeners){
47410         if(this.loadMask){
47411             this.loadMask.destroy();
47412         }
47413         var c = this.container;
47414         c.removeAllListeners();
47415         this.view.destroy();
47416         this.colModel.purgeListeners();
47417         if(!keepListeners){
47418             this.purgeListeners();
47419         }
47420         c.update("");
47421         if(removeEl === true){
47422             c.remove();
47423         }
47424     },
47425
47426     // private
47427     processEvent : function(name, e){
47428         this.fireEvent(name, e);
47429         var t = e.getTarget();
47430         var v = this.view;
47431         var header = v.findHeaderIndex(t);
47432         if(header !== false){
47433             this.fireEvent("header" + name, this, header, e);
47434         }else{
47435             var row = v.findRowIndex(t);
47436             var cell = v.findCellIndex(t);
47437             if(row !== false){
47438                 this.fireEvent("row" + name, this, row, e);
47439                 if(cell !== false){
47440                     this.fireEvent("cell" + name, this, row, cell, e);
47441                 }
47442             }
47443         }
47444     },
47445
47446     // private
47447     onClick : function(e){
47448         this.processEvent("click", e);
47449     },
47450
47451     // private
47452     onContextMenu : function(e, t){
47453         this.processEvent("contextmenu", e);
47454     },
47455
47456     // private
47457     onDblClick : function(e){
47458         this.processEvent("dblclick", e);
47459     },
47460
47461     // private
47462     walkCells : function(row, col, step, fn, scope){
47463         var cm = this.colModel, clen = cm.getColumnCount();
47464         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47465         if(step < 0){
47466             if(col < 0){
47467                 row--;
47468                 first = false;
47469             }
47470             while(row >= 0){
47471                 if(!first){
47472                     col = clen-1;
47473                 }
47474                 first = false;
47475                 while(col >= 0){
47476                     if(fn.call(scope || this, row, col, cm) === true){
47477                         return [row, col];
47478                     }
47479                     col--;
47480                 }
47481                 row--;
47482             }
47483         } else {
47484             if(col >= clen){
47485                 row++;
47486                 first = false;
47487             }
47488             while(row < rlen){
47489                 if(!first){
47490                     col = 0;
47491                 }
47492                 first = false;
47493                 while(col < clen){
47494                     if(fn.call(scope || this, row, col, cm) === true){
47495                         return [row, col];
47496                     }
47497                     col++;
47498                 }
47499                 row++;
47500             }
47501         }
47502         return null;
47503     },
47504
47505     // private
47506     getSelections : function(){
47507         return this.selModel.getSelections();
47508     },
47509
47510     /**
47511      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47512      * but if manual update is required this method will initiate it.
47513      */
47514     autoSize : function(){
47515         if(this.rendered){
47516             this.view.layout();
47517             if(this.view.adjustForScroll){
47518                 this.view.adjustForScroll();
47519             }
47520         }
47521     },
47522
47523     /**
47524      * Returns the grid's underlying element.
47525      * @return {Element} The element
47526      */
47527     getGridEl : function(){
47528         return this.container;
47529     },
47530
47531     // private for compatibility, overridden by editor grid
47532     stopEditing : function(){},
47533
47534     /**
47535      * Returns the grid's SelectionModel.
47536      * @return {SelectionModel}
47537      */
47538     getSelectionModel : function(){
47539         if(!this.selModel){
47540             this.selModel = new Roo.grid.RowSelectionModel();
47541         }
47542         return this.selModel;
47543     },
47544
47545     /**
47546      * Returns the grid's DataSource.
47547      * @return {DataSource}
47548      */
47549     getDataSource : function(){
47550         return this.dataSource;
47551     },
47552
47553     /**
47554      * Returns the grid's ColumnModel.
47555      * @return {ColumnModel}
47556      */
47557     getColumnModel : function(){
47558         return this.colModel;
47559     },
47560
47561     /**
47562      * Returns the grid's GridView object.
47563      * @return {GridView}
47564      */
47565     getView : function(){
47566         if(!this.view){
47567             this.view = new Roo.grid.GridView(this.viewConfig);
47568         }
47569         return this.view;
47570     },
47571     /**
47572      * Called to get grid's drag proxy text, by default returns this.ddText.
47573      * @return {String}
47574      */
47575     getDragDropText : function(){
47576         var count = this.selModel.getCount();
47577         return String.format(this.ddText, count, count == 1 ? '' : 's');
47578     }
47579 });
47580 /**
47581  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47582  * %0 is replaced with the number of selected rows.
47583  * @type String
47584  */
47585 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47586  * Based on:
47587  * Ext JS Library 1.1.1
47588  * Copyright(c) 2006-2007, Ext JS, LLC.
47589  *
47590  * Originally Released Under LGPL - original licence link has changed is not relivant.
47591  *
47592  * Fork - LGPL
47593  * <script type="text/javascript">
47594  */
47595  
47596 Roo.grid.AbstractGridView = function(){
47597         this.grid = null;
47598         
47599         this.events = {
47600             "beforerowremoved" : true,
47601             "beforerowsinserted" : true,
47602             "beforerefresh" : true,
47603             "rowremoved" : true,
47604             "rowsinserted" : true,
47605             "rowupdated" : true,
47606             "refresh" : true
47607         };
47608     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47609 };
47610
47611 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47612     rowClass : "x-grid-row",
47613     cellClass : "x-grid-cell",
47614     tdClass : "x-grid-td",
47615     hdClass : "x-grid-hd",
47616     splitClass : "x-grid-hd-split",
47617     
47618         init: function(grid){
47619         this.grid = grid;
47620                 var cid = this.grid.getGridEl().id;
47621         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47622         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47623         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47624         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47625         },
47626         
47627         getColumnRenderers : function(){
47628         var renderers = [];
47629         var cm = this.grid.colModel;
47630         var colCount = cm.getColumnCount();
47631         for(var i = 0; i < colCount; i++){
47632             renderers[i] = cm.getRenderer(i);
47633         }
47634         return renderers;
47635     },
47636     
47637     getColumnIds : function(){
47638         var ids = [];
47639         var cm = this.grid.colModel;
47640         var colCount = cm.getColumnCount();
47641         for(var i = 0; i < colCount; i++){
47642             ids[i] = cm.getColumnId(i);
47643         }
47644         return ids;
47645     },
47646     
47647     getDataIndexes : function(){
47648         if(!this.indexMap){
47649             this.indexMap = this.buildIndexMap();
47650         }
47651         return this.indexMap.colToData;
47652     },
47653     
47654     getColumnIndexByDataIndex : function(dataIndex){
47655         if(!this.indexMap){
47656             this.indexMap = this.buildIndexMap();
47657         }
47658         return this.indexMap.dataToCol[dataIndex];
47659     },
47660     
47661     /**
47662      * Set a css style for a column dynamically. 
47663      * @param {Number} colIndex The index of the column
47664      * @param {String} name The css property name
47665      * @param {String} value The css value
47666      */
47667     setCSSStyle : function(colIndex, name, value){
47668         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47669         Roo.util.CSS.updateRule(selector, name, value);
47670     },
47671     
47672     generateRules : function(cm){
47673         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47674         Roo.util.CSS.removeStyleSheet(rulesId);
47675         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47676             var cid = cm.getColumnId(i);
47677             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47678                          this.tdSelector, cid, " {\n}\n",
47679                          this.hdSelector, cid, " {\n}\n",
47680                          this.splitSelector, cid, " {\n}\n");
47681         }
47682         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47683     }
47684 });/*
47685  * Based on:
47686  * Ext JS Library 1.1.1
47687  * Copyright(c) 2006-2007, Ext JS, LLC.
47688  *
47689  * Originally Released Under LGPL - original licence link has changed is not relivant.
47690  *
47691  * Fork - LGPL
47692  * <script type="text/javascript">
47693  */
47694
47695 // private
47696 // This is a support class used internally by the Grid components
47697 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47698     this.grid = grid;
47699     this.view = grid.getView();
47700     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47701     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47702     if(hd2){
47703         this.setHandleElId(Roo.id(hd));
47704         this.setOuterHandleElId(Roo.id(hd2));
47705     }
47706     this.scroll = false;
47707 };
47708 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47709     maxDragWidth: 120,
47710     getDragData : function(e){
47711         var t = Roo.lib.Event.getTarget(e);
47712         var h = this.view.findHeaderCell(t);
47713         if(h){
47714             return {ddel: h.firstChild, header:h};
47715         }
47716         return false;
47717     },
47718
47719     onInitDrag : function(e){
47720         this.view.headersDisabled = true;
47721         var clone = this.dragData.ddel.cloneNode(true);
47722         clone.id = Roo.id();
47723         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47724         this.proxy.update(clone);
47725         return true;
47726     },
47727
47728     afterValidDrop : function(){
47729         var v = this.view;
47730         setTimeout(function(){
47731             v.headersDisabled = false;
47732         }, 50);
47733     },
47734
47735     afterInvalidDrop : function(){
47736         var v = this.view;
47737         setTimeout(function(){
47738             v.headersDisabled = false;
47739         }, 50);
47740     }
47741 });
47742 /*
47743  * Based on:
47744  * Ext JS Library 1.1.1
47745  * Copyright(c) 2006-2007, Ext JS, LLC.
47746  *
47747  * Originally Released Under LGPL - original licence link has changed is not relivant.
47748  *
47749  * Fork - LGPL
47750  * <script type="text/javascript">
47751  */
47752 // private
47753 // This is a support class used internally by the Grid components
47754 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47755     this.grid = grid;
47756     this.view = grid.getView();
47757     // split the proxies so they don't interfere with mouse events
47758     this.proxyTop = Roo.DomHelper.append(document.body, {
47759         cls:"col-move-top", html:"&#160;"
47760     }, true);
47761     this.proxyBottom = Roo.DomHelper.append(document.body, {
47762         cls:"col-move-bottom", html:"&#160;"
47763     }, true);
47764     this.proxyTop.hide = this.proxyBottom.hide = function(){
47765         this.setLeftTop(-100,-100);
47766         this.setStyle("visibility", "hidden");
47767     };
47768     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47769     // temporarily disabled
47770     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47771     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47772 };
47773 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47774     proxyOffsets : [-4, -9],
47775     fly: Roo.Element.fly,
47776
47777     getTargetFromEvent : function(e){
47778         var t = Roo.lib.Event.getTarget(e);
47779         var cindex = this.view.findCellIndex(t);
47780         if(cindex !== false){
47781             return this.view.getHeaderCell(cindex);
47782         }
47783         return null;
47784     },
47785
47786     nextVisible : function(h){
47787         var v = this.view, cm = this.grid.colModel;
47788         h = h.nextSibling;
47789         while(h){
47790             if(!cm.isHidden(v.getCellIndex(h))){
47791                 return h;
47792             }
47793             h = h.nextSibling;
47794         }
47795         return null;
47796     },
47797
47798     prevVisible : function(h){
47799         var v = this.view, cm = this.grid.colModel;
47800         h = h.prevSibling;
47801         while(h){
47802             if(!cm.isHidden(v.getCellIndex(h))){
47803                 return h;
47804             }
47805             h = h.prevSibling;
47806         }
47807         return null;
47808     },
47809
47810     positionIndicator : function(h, n, e){
47811         var x = Roo.lib.Event.getPageX(e);
47812         var r = Roo.lib.Dom.getRegion(n.firstChild);
47813         var px, pt, py = r.top + this.proxyOffsets[1];
47814         if((r.right - x) <= (r.right-r.left)/2){
47815             px = r.right+this.view.borderWidth;
47816             pt = "after";
47817         }else{
47818             px = r.left;
47819             pt = "before";
47820         }
47821         var oldIndex = this.view.getCellIndex(h);
47822         var newIndex = this.view.getCellIndex(n);
47823
47824         if(this.grid.colModel.isFixed(newIndex)){
47825             return false;
47826         }
47827
47828         var locked = this.grid.colModel.isLocked(newIndex);
47829
47830         if(pt == "after"){
47831             newIndex++;
47832         }
47833         if(oldIndex < newIndex){
47834             newIndex--;
47835         }
47836         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47837             return false;
47838         }
47839         px +=  this.proxyOffsets[0];
47840         this.proxyTop.setLeftTop(px, py);
47841         this.proxyTop.show();
47842         if(!this.bottomOffset){
47843             this.bottomOffset = this.view.mainHd.getHeight();
47844         }
47845         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47846         this.proxyBottom.show();
47847         return pt;
47848     },
47849
47850     onNodeEnter : function(n, dd, e, data){
47851         if(data.header != n){
47852             this.positionIndicator(data.header, n, e);
47853         }
47854     },
47855
47856     onNodeOver : function(n, dd, e, data){
47857         var result = false;
47858         if(data.header != n){
47859             result = this.positionIndicator(data.header, n, e);
47860         }
47861         if(!result){
47862             this.proxyTop.hide();
47863             this.proxyBottom.hide();
47864         }
47865         return result ? this.dropAllowed : this.dropNotAllowed;
47866     },
47867
47868     onNodeOut : function(n, dd, e, data){
47869         this.proxyTop.hide();
47870         this.proxyBottom.hide();
47871     },
47872
47873     onNodeDrop : function(n, dd, e, data){
47874         var h = data.header;
47875         if(h != n){
47876             var cm = this.grid.colModel;
47877             var x = Roo.lib.Event.getPageX(e);
47878             var r = Roo.lib.Dom.getRegion(n.firstChild);
47879             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47880             var oldIndex = this.view.getCellIndex(h);
47881             var newIndex = this.view.getCellIndex(n);
47882             var locked = cm.isLocked(newIndex);
47883             if(pt == "after"){
47884                 newIndex++;
47885             }
47886             if(oldIndex < newIndex){
47887                 newIndex--;
47888             }
47889             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47890                 return false;
47891             }
47892             cm.setLocked(oldIndex, locked, true);
47893             cm.moveColumn(oldIndex, newIndex);
47894             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47895             return true;
47896         }
47897         return false;
47898     }
47899 });
47900 /*
47901  * Based on:
47902  * Ext JS Library 1.1.1
47903  * Copyright(c) 2006-2007, Ext JS, LLC.
47904  *
47905  * Originally Released Under LGPL - original licence link has changed is not relivant.
47906  *
47907  * Fork - LGPL
47908  * <script type="text/javascript">
47909  */
47910   
47911 /**
47912  * @class Roo.grid.GridView
47913  * @extends Roo.util.Observable
47914  *
47915  * @constructor
47916  * @param {Object} config
47917  */
47918 Roo.grid.GridView = function(config){
47919     Roo.grid.GridView.superclass.constructor.call(this);
47920     this.el = null;
47921
47922     Roo.apply(this, config);
47923 };
47924
47925 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47926
47927     /**
47928      * Override this function to apply custom css classes to rows during rendering
47929      * @param {Record} record The record
47930      * @param {Number} index
47931      * @method getRowClass
47932      */
47933     rowClass : "x-grid-row",
47934
47935     cellClass : "x-grid-col",
47936
47937     tdClass : "x-grid-td",
47938
47939     hdClass : "x-grid-hd",
47940
47941     splitClass : "x-grid-split",
47942
47943     sortClasses : ["sort-asc", "sort-desc"],
47944
47945     enableMoveAnim : false,
47946
47947     hlColor: "C3DAF9",
47948
47949     dh : Roo.DomHelper,
47950
47951     fly : Roo.Element.fly,
47952
47953     css : Roo.util.CSS,
47954
47955     borderWidth: 1,
47956
47957     splitOffset: 3,
47958
47959     scrollIncrement : 22,
47960
47961     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47962
47963     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47964
47965     bind : function(ds, cm){
47966         if(this.ds){
47967             this.ds.un("load", this.onLoad, this);
47968             this.ds.un("datachanged", this.onDataChange, this);
47969             this.ds.un("add", this.onAdd, this);
47970             this.ds.un("remove", this.onRemove, this);
47971             this.ds.un("update", this.onUpdate, this);
47972             this.ds.un("clear", this.onClear, this);
47973         }
47974         if(ds){
47975             ds.on("load", this.onLoad, this);
47976             ds.on("datachanged", this.onDataChange, this);
47977             ds.on("add", this.onAdd, this);
47978             ds.on("remove", this.onRemove, this);
47979             ds.on("update", this.onUpdate, this);
47980             ds.on("clear", this.onClear, this);
47981         }
47982         this.ds = ds;
47983
47984         if(this.cm){
47985             this.cm.un("widthchange", this.onColWidthChange, this);
47986             this.cm.un("headerchange", this.onHeaderChange, this);
47987             this.cm.un("hiddenchange", this.onHiddenChange, this);
47988             this.cm.un("columnmoved", this.onColumnMove, this);
47989             this.cm.un("columnlockchange", this.onColumnLock, this);
47990         }
47991         if(cm){
47992             this.generateRules(cm);
47993             cm.on("widthchange", this.onColWidthChange, this);
47994             cm.on("headerchange", this.onHeaderChange, this);
47995             cm.on("hiddenchange", this.onHiddenChange, this);
47996             cm.on("columnmoved", this.onColumnMove, this);
47997             cm.on("columnlockchange", this.onColumnLock, this);
47998         }
47999         this.cm = cm;
48000     },
48001
48002     init: function(grid){
48003         Roo.grid.GridView.superclass.init.call(this, grid);
48004
48005         this.bind(grid.dataSource, grid.colModel);
48006
48007         grid.on("headerclick", this.handleHeaderClick, this);
48008
48009         if(grid.trackMouseOver){
48010             grid.on("mouseover", this.onRowOver, this);
48011             grid.on("mouseout", this.onRowOut, this);
48012         }
48013         grid.cancelTextSelection = function(){};
48014         this.gridId = grid.id;
48015
48016         var tpls = this.templates || {};
48017
48018         if(!tpls.master){
48019             tpls.master = new Roo.Template(
48020                '<div class="x-grid" hidefocus="true">',
48021                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48022                   '<div class="x-grid-topbar"></div>',
48023                   '<div class="x-grid-scroller"><div></div></div>',
48024                   '<div class="x-grid-locked">',
48025                       '<div class="x-grid-header">{lockedHeader}</div>',
48026                       '<div class="x-grid-body">{lockedBody}</div>',
48027                   "</div>",
48028                   '<div class="x-grid-viewport">',
48029                       '<div class="x-grid-header">{header}</div>',
48030                       '<div class="x-grid-body">{body}</div>',
48031                   "</div>",
48032                   '<div class="x-grid-bottombar"></div>',
48033                  
48034                   '<div class="x-grid-resize-proxy">&#160;</div>',
48035                "</div>"
48036             );
48037             tpls.master.disableformats = true;
48038         }
48039
48040         if(!tpls.header){
48041             tpls.header = new Roo.Template(
48042                '<table border="0" cellspacing="0" cellpadding="0">',
48043                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48044                "</table>{splits}"
48045             );
48046             tpls.header.disableformats = true;
48047         }
48048         tpls.header.compile();
48049
48050         if(!tpls.hcell){
48051             tpls.hcell = new Roo.Template(
48052                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48053                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48054                 "</div></td>"
48055              );
48056              tpls.hcell.disableFormats = true;
48057         }
48058         tpls.hcell.compile();
48059
48060         if(!tpls.hsplit){
48061             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48062             tpls.hsplit.disableFormats = true;
48063         }
48064         tpls.hsplit.compile();
48065
48066         if(!tpls.body){
48067             tpls.body = new Roo.Template(
48068                '<table border="0" cellspacing="0" cellpadding="0">',
48069                "<tbody>{rows}</tbody>",
48070                "</table>"
48071             );
48072             tpls.body.disableFormats = true;
48073         }
48074         tpls.body.compile();
48075
48076         if(!tpls.row){
48077             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48078             tpls.row.disableFormats = true;
48079         }
48080         tpls.row.compile();
48081
48082         if(!tpls.cell){
48083             tpls.cell = new Roo.Template(
48084                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48085                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48086                 "</td>"
48087             );
48088             tpls.cell.disableFormats = true;
48089         }
48090         tpls.cell.compile();
48091
48092         this.templates = tpls;
48093     },
48094
48095     // remap these for backwards compat
48096     onColWidthChange : function(){
48097         this.updateColumns.apply(this, arguments);
48098     },
48099     onHeaderChange : function(){
48100         this.updateHeaders.apply(this, arguments);
48101     }, 
48102     onHiddenChange : function(){
48103         this.handleHiddenChange.apply(this, arguments);
48104     },
48105     onColumnMove : function(){
48106         this.handleColumnMove.apply(this, arguments);
48107     },
48108     onColumnLock : function(){
48109         this.handleLockChange.apply(this, arguments);
48110     },
48111
48112     onDataChange : function(){
48113         this.refresh();
48114         this.updateHeaderSortState();
48115     },
48116
48117     onClear : function(){
48118         this.refresh();
48119     },
48120
48121     onUpdate : function(ds, record){
48122         this.refreshRow(record);
48123     },
48124
48125     refreshRow : function(record){
48126         var ds = this.ds, index;
48127         if(typeof record == 'number'){
48128             index = record;
48129             record = ds.getAt(index);
48130         }else{
48131             index = ds.indexOf(record);
48132         }
48133         this.insertRows(ds, index, index, true);
48134         this.onRemove(ds, record, index+1, true);
48135         this.syncRowHeights(index, index);
48136         this.layout();
48137         this.fireEvent("rowupdated", this, index, record);
48138     },
48139
48140     onAdd : function(ds, records, index){
48141         this.insertRows(ds, index, index + (records.length-1));
48142     },
48143
48144     onRemove : function(ds, record, index, isUpdate){
48145         if(isUpdate !== true){
48146             this.fireEvent("beforerowremoved", this, index, record);
48147         }
48148         var bt = this.getBodyTable(), lt = this.getLockedTable();
48149         if(bt.rows[index]){
48150             bt.firstChild.removeChild(bt.rows[index]);
48151         }
48152         if(lt.rows[index]){
48153             lt.firstChild.removeChild(lt.rows[index]);
48154         }
48155         if(isUpdate !== true){
48156             this.stripeRows(index);
48157             this.syncRowHeights(index, index);
48158             this.layout();
48159             this.fireEvent("rowremoved", this, index, record);
48160         }
48161     },
48162
48163     onLoad : function(){
48164         this.scrollToTop();
48165     },
48166
48167     /**
48168      * Scrolls the grid to the top
48169      */
48170     scrollToTop : function(){
48171         if(this.scroller){
48172             this.scroller.dom.scrollTop = 0;
48173             this.syncScroll();
48174         }
48175     },
48176
48177     /**
48178      * Gets a panel in the header of the grid that can be used for toolbars etc.
48179      * After modifying the contents of this panel a call to grid.autoSize() may be
48180      * required to register any changes in size.
48181      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48182      * @return Roo.Element
48183      */
48184     getHeaderPanel : function(doShow){
48185         if(doShow){
48186             this.headerPanel.show();
48187         }
48188         return this.headerPanel;
48189     },
48190
48191     /**
48192      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48193      * After modifying the contents of this panel a call to grid.autoSize() may be
48194      * required to register any changes in size.
48195      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48196      * @return Roo.Element
48197      */
48198     getFooterPanel : function(doShow){
48199         if(doShow){
48200             this.footerPanel.show();
48201         }
48202         return this.footerPanel;
48203     },
48204
48205     initElements : function(){
48206         var E = Roo.Element;
48207         var el = this.grid.getGridEl().dom.firstChild;
48208         var cs = el.childNodes;
48209
48210         this.el = new E(el);
48211         
48212          this.focusEl = new E(el.firstChild);
48213         this.focusEl.swallowEvent("click", true);
48214         
48215         this.headerPanel = new E(cs[1]);
48216         this.headerPanel.enableDisplayMode("block");
48217
48218         this.scroller = new E(cs[2]);
48219         this.scrollSizer = new E(this.scroller.dom.firstChild);
48220
48221         this.lockedWrap = new E(cs[3]);
48222         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48223         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48224
48225         this.mainWrap = new E(cs[4]);
48226         this.mainHd = new E(this.mainWrap.dom.firstChild);
48227         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48228
48229         this.footerPanel = new E(cs[5]);
48230         this.footerPanel.enableDisplayMode("block");
48231
48232         this.resizeProxy = new E(cs[6]);
48233
48234         this.headerSelector = String.format(
48235            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48236            this.lockedHd.id, this.mainHd.id
48237         );
48238
48239         this.splitterSelector = String.format(
48240            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48241            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48242         );
48243     },
48244     idToCssName : function(s)
48245     {
48246         return s.replace(/[^a-z0-9]+/ig, '-');
48247     },
48248
48249     getHeaderCell : function(index){
48250         return Roo.DomQuery.select(this.headerSelector)[index];
48251     },
48252
48253     getHeaderCellMeasure : function(index){
48254         return this.getHeaderCell(index).firstChild;
48255     },
48256
48257     getHeaderCellText : function(index){
48258         return this.getHeaderCell(index).firstChild.firstChild;
48259     },
48260
48261     getLockedTable : function(){
48262         return this.lockedBody.dom.firstChild;
48263     },
48264
48265     getBodyTable : function(){
48266         return this.mainBody.dom.firstChild;
48267     },
48268
48269     getLockedRow : function(index){
48270         return this.getLockedTable().rows[index];
48271     },
48272
48273     getRow : function(index){
48274         return this.getBodyTable().rows[index];
48275     },
48276
48277     getRowComposite : function(index){
48278         if(!this.rowEl){
48279             this.rowEl = new Roo.CompositeElementLite();
48280         }
48281         var els = [], lrow, mrow;
48282         if(lrow = this.getLockedRow(index)){
48283             els.push(lrow);
48284         }
48285         if(mrow = this.getRow(index)){
48286             els.push(mrow);
48287         }
48288         this.rowEl.elements = els;
48289         return this.rowEl;
48290     },
48291
48292     getCell : function(rowIndex, colIndex){
48293         var locked = this.cm.getLockedCount();
48294         var source;
48295         if(colIndex < locked){
48296             source = this.lockedBody.dom.firstChild;
48297         }else{
48298             source = this.mainBody.dom.firstChild;
48299             colIndex -= locked;
48300         }
48301         return source.rows[rowIndex].childNodes[colIndex];
48302     },
48303
48304     getCellText : function(rowIndex, colIndex){
48305         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48306     },
48307
48308     getCellBox : function(cell){
48309         var b = this.fly(cell).getBox();
48310         if(Roo.isOpera){ // opera fails to report the Y
48311             b.y = cell.offsetTop + this.mainBody.getY();
48312         }
48313         return b;
48314     },
48315
48316     getCellIndex : function(cell){
48317         var id = String(cell.className).match(this.cellRE);
48318         if(id){
48319             return parseInt(id[1], 10);
48320         }
48321         return 0;
48322     },
48323
48324     findHeaderIndex : function(n){
48325         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48326         return r ? this.getCellIndex(r) : false;
48327     },
48328
48329     findHeaderCell : function(n){
48330         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48331         return r ? r : false;
48332     },
48333
48334     findRowIndex : function(n){
48335         if(!n){
48336             return false;
48337         }
48338         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48339         return r ? r.rowIndex : false;
48340     },
48341
48342     findCellIndex : function(node){
48343         var stop = this.el.dom;
48344         while(node && node != stop){
48345             if(this.findRE.test(node.className)){
48346                 return this.getCellIndex(node);
48347             }
48348             node = node.parentNode;
48349         }
48350         return false;
48351     },
48352
48353     getColumnId : function(index){
48354         return this.cm.getColumnId(index);
48355     },
48356
48357     getSplitters : function()
48358     {
48359         if(this.splitterSelector){
48360            return Roo.DomQuery.select(this.splitterSelector);
48361         }else{
48362             return null;
48363       }
48364     },
48365
48366     getSplitter : function(index){
48367         return this.getSplitters()[index];
48368     },
48369
48370     onRowOver : function(e, t){
48371         var row;
48372         if((row = this.findRowIndex(t)) !== false){
48373             this.getRowComposite(row).addClass("x-grid-row-over");
48374         }
48375     },
48376
48377     onRowOut : function(e, t){
48378         var row;
48379         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48380             this.getRowComposite(row).removeClass("x-grid-row-over");
48381         }
48382     },
48383
48384     renderHeaders : function(){
48385         var cm = this.cm;
48386         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48387         var cb = [], lb = [], sb = [], lsb = [], p = {};
48388         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48389             p.cellId = "x-grid-hd-0-" + i;
48390             p.splitId = "x-grid-csplit-0-" + i;
48391             p.id = cm.getColumnId(i);
48392             p.title = cm.getColumnTooltip(i) || "";
48393             p.value = cm.getColumnHeader(i) || "";
48394             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48395             if(!cm.isLocked(i)){
48396                 cb[cb.length] = ct.apply(p);
48397                 sb[sb.length] = st.apply(p);
48398             }else{
48399                 lb[lb.length] = ct.apply(p);
48400                 lsb[lsb.length] = st.apply(p);
48401             }
48402         }
48403         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48404                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48405     },
48406
48407     updateHeaders : function(){
48408         var html = this.renderHeaders();
48409         this.lockedHd.update(html[0]);
48410         this.mainHd.update(html[1]);
48411     },
48412
48413     /**
48414      * Focuses the specified row.
48415      * @param {Number} row The row index
48416      */
48417     focusRow : function(row)
48418     {
48419         //Roo.log('GridView.focusRow');
48420         var x = this.scroller.dom.scrollLeft;
48421         this.focusCell(row, 0, false);
48422         this.scroller.dom.scrollLeft = x;
48423     },
48424
48425     /**
48426      * Focuses the specified cell.
48427      * @param {Number} row The row index
48428      * @param {Number} col The column index
48429      * @param {Boolean} hscroll false to disable horizontal scrolling
48430      */
48431     focusCell : function(row, col, hscroll)
48432     {
48433         //Roo.log('GridView.focusCell');
48434         var el = this.ensureVisible(row, col, hscroll);
48435         this.focusEl.alignTo(el, "tl-tl");
48436         if(Roo.isGecko){
48437             this.focusEl.focus();
48438         }else{
48439             this.focusEl.focus.defer(1, this.focusEl);
48440         }
48441     },
48442
48443     /**
48444      * Scrolls the specified cell into view
48445      * @param {Number} row The row index
48446      * @param {Number} col The column index
48447      * @param {Boolean} hscroll false to disable horizontal scrolling
48448      */
48449     ensureVisible : function(row, col, hscroll)
48450     {
48451         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48452         //return null; //disable for testing.
48453         if(typeof row != "number"){
48454             row = row.rowIndex;
48455         }
48456         if(row < 0 && row >= this.ds.getCount()){
48457             return  null;
48458         }
48459         col = (col !== undefined ? col : 0);
48460         var cm = this.grid.colModel;
48461         while(cm.isHidden(col)){
48462             col++;
48463         }
48464
48465         var el = this.getCell(row, col);
48466         if(!el){
48467             return null;
48468         }
48469         var c = this.scroller.dom;
48470
48471         var ctop = parseInt(el.offsetTop, 10);
48472         var cleft = parseInt(el.offsetLeft, 10);
48473         var cbot = ctop + el.offsetHeight;
48474         var cright = cleft + el.offsetWidth;
48475         
48476         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48477         var stop = parseInt(c.scrollTop, 10);
48478         var sleft = parseInt(c.scrollLeft, 10);
48479         var sbot = stop + ch;
48480         var sright = sleft + c.clientWidth;
48481         /*
48482         Roo.log('GridView.ensureVisible:' +
48483                 ' ctop:' + ctop +
48484                 ' c.clientHeight:' + c.clientHeight +
48485                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48486                 ' stop:' + stop +
48487                 ' cbot:' + cbot +
48488                 ' sbot:' + sbot +
48489                 ' ch:' + ch  
48490                 );
48491         */
48492         if(ctop < stop){
48493              c.scrollTop = ctop;
48494             //Roo.log("set scrolltop to ctop DISABLE?");
48495         }else if(cbot > sbot){
48496             //Roo.log("set scrolltop to cbot-ch");
48497             c.scrollTop = cbot-ch;
48498         }
48499         
48500         if(hscroll !== false){
48501             if(cleft < sleft){
48502                 c.scrollLeft = cleft;
48503             }else if(cright > sright){
48504                 c.scrollLeft = cright-c.clientWidth;
48505             }
48506         }
48507          
48508         return el;
48509     },
48510
48511     updateColumns : function(){
48512         this.grid.stopEditing();
48513         var cm = this.grid.colModel, colIds = this.getColumnIds();
48514         //var totalWidth = cm.getTotalWidth();
48515         var pos = 0;
48516         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48517             //if(cm.isHidden(i)) continue;
48518             var w = cm.getColumnWidth(i);
48519             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48520             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48521         }
48522         this.updateSplitters();
48523     },
48524
48525     generateRules : function(cm){
48526         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48527         Roo.util.CSS.removeStyleSheet(rulesId);
48528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48529             var cid = cm.getColumnId(i);
48530             var align = '';
48531             if(cm.config[i].align){
48532                 align = 'text-align:'+cm.config[i].align+';';
48533             }
48534             var hidden = '';
48535             if(cm.isHidden(i)){
48536                 hidden = 'display:none;';
48537             }
48538             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48539             ruleBuf.push(
48540                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48541                     this.hdSelector, cid, " {\n", align, width, "}\n",
48542                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48543                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48544         }
48545         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48546     },
48547
48548     updateSplitters : function(){
48549         var cm = this.cm, s = this.getSplitters();
48550         if(s){ // splitters not created yet
48551             var pos = 0, locked = true;
48552             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48553                 if(cm.isHidden(i)) continue;
48554                 var w = cm.getColumnWidth(i); // make sure it's a number
48555                 if(!cm.isLocked(i) && locked){
48556                     pos = 0;
48557                     locked = false;
48558                 }
48559                 pos += w;
48560                 s[i].style.left = (pos-this.splitOffset) + "px";
48561             }
48562         }
48563     },
48564
48565     handleHiddenChange : function(colModel, colIndex, hidden){
48566         if(hidden){
48567             this.hideColumn(colIndex);
48568         }else{
48569             this.unhideColumn(colIndex);
48570         }
48571     },
48572
48573     hideColumn : function(colIndex){
48574         var cid = this.getColumnId(colIndex);
48575         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48576         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48577         if(Roo.isSafari){
48578             this.updateHeaders();
48579         }
48580         this.updateSplitters();
48581         this.layout();
48582     },
48583
48584     unhideColumn : function(colIndex){
48585         var cid = this.getColumnId(colIndex);
48586         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48587         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48588
48589         if(Roo.isSafari){
48590             this.updateHeaders();
48591         }
48592         this.updateSplitters();
48593         this.layout();
48594     },
48595
48596     insertRows : function(dm, firstRow, lastRow, isUpdate){
48597         if(firstRow == 0 && lastRow == dm.getCount()-1){
48598             this.refresh();
48599         }else{
48600             if(!isUpdate){
48601                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48602             }
48603             var s = this.getScrollState();
48604             var markup = this.renderRows(firstRow, lastRow);
48605             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48606             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48607             this.restoreScroll(s);
48608             if(!isUpdate){
48609                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48610                 this.syncRowHeights(firstRow, lastRow);
48611                 this.stripeRows(firstRow);
48612                 this.layout();
48613             }
48614         }
48615     },
48616
48617     bufferRows : function(markup, target, index){
48618         var before = null, trows = target.rows, tbody = target.tBodies[0];
48619         if(index < trows.length){
48620             before = trows[index];
48621         }
48622         var b = document.createElement("div");
48623         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48624         var rows = b.firstChild.rows;
48625         for(var i = 0, len = rows.length; i < len; i++){
48626             if(before){
48627                 tbody.insertBefore(rows[0], before);
48628             }else{
48629                 tbody.appendChild(rows[0]);
48630             }
48631         }
48632         b.innerHTML = "";
48633         b = null;
48634     },
48635
48636     deleteRows : function(dm, firstRow, lastRow){
48637         if(dm.getRowCount()<1){
48638             this.fireEvent("beforerefresh", this);
48639             this.mainBody.update("");
48640             this.lockedBody.update("");
48641             this.fireEvent("refresh", this);
48642         }else{
48643             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48644             var bt = this.getBodyTable();
48645             var tbody = bt.firstChild;
48646             var rows = bt.rows;
48647             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48648                 tbody.removeChild(rows[firstRow]);
48649             }
48650             this.stripeRows(firstRow);
48651             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48652         }
48653     },
48654
48655     updateRows : function(dataSource, firstRow, lastRow){
48656         var s = this.getScrollState();
48657         this.refresh();
48658         this.restoreScroll(s);
48659     },
48660
48661     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48662         if(!noRefresh){
48663            this.refresh();
48664         }
48665         this.updateHeaderSortState();
48666     },
48667
48668     getScrollState : function(){
48669         
48670         var sb = this.scroller.dom;
48671         return {left: sb.scrollLeft, top: sb.scrollTop};
48672     },
48673
48674     stripeRows : function(startRow){
48675         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48676             return;
48677         }
48678         startRow = startRow || 0;
48679         var rows = this.getBodyTable().rows;
48680         var lrows = this.getLockedTable().rows;
48681         var cls = ' x-grid-row-alt ';
48682         for(var i = startRow, len = rows.length; i < len; i++){
48683             var row = rows[i], lrow = lrows[i];
48684             var isAlt = ((i+1) % 2 == 0);
48685             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48686             if(isAlt == hasAlt){
48687                 continue;
48688             }
48689             if(isAlt){
48690                 row.className += " x-grid-row-alt";
48691             }else{
48692                 row.className = row.className.replace("x-grid-row-alt", "");
48693             }
48694             if(lrow){
48695                 lrow.className = row.className;
48696             }
48697         }
48698     },
48699
48700     restoreScroll : function(state){
48701         //Roo.log('GridView.restoreScroll');
48702         var sb = this.scroller.dom;
48703         sb.scrollLeft = state.left;
48704         sb.scrollTop = state.top;
48705         this.syncScroll();
48706     },
48707
48708     syncScroll : function(){
48709         //Roo.log('GridView.syncScroll');
48710         var sb = this.scroller.dom;
48711         var sh = this.mainHd.dom;
48712         var bs = this.mainBody.dom;
48713         var lv = this.lockedBody.dom;
48714         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48715         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48716     },
48717
48718     handleScroll : function(e){
48719         this.syncScroll();
48720         var sb = this.scroller.dom;
48721         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48722         e.stopEvent();
48723     },
48724
48725     handleWheel : function(e){
48726         var d = e.getWheelDelta();
48727         this.scroller.dom.scrollTop -= d*22;
48728         // set this here to prevent jumpy scrolling on large tables
48729         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48730         e.stopEvent();
48731     },
48732
48733     renderRows : function(startRow, endRow){
48734         // pull in all the crap needed to render rows
48735         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48736         var colCount = cm.getColumnCount();
48737
48738         if(ds.getCount() < 1){
48739             return ["", ""];
48740         }
48741
48742         // build a map for all the columns
48743         var cs = [];
48744         for(var i = 0; i < colCount; i++){
48745             var name = cm.getDataIndex(i);
48746             cs[i] = {
48747                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48748                 renderer : cm.getRenderer(i),
48749                 id : cm.getColumnId(i),
48750                 locked : cm.isLocked(i)
48751             };
48752         }
48753
48754         startRow = startRow || 0;
48755         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48756
48757         // records to render
48758         var rs = ds.getRange(startRow, endRow);
48759
48760         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48761     },
48762
48763     // As much as I hate to duplicate code, this was branched because FireFox really hates
48764     // [].join("") on strings. The performance difference was substantial enough to
48765     // branch this function
48766     doRender : Roo.isGecko ?
48767             function(cs, rs, ds, startRow, colCount, stripe){
48768                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48769                 // buffers
48770                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48771                 
48772                 var hasListener = this.grid.hasListener('rowclass');
48773                 var rowcfg = {};
48774                 for(var j = 0, len = rs.length; j < len; j++){
48775                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48776                     for(var i = 0; i < colCount; i++){
48777                         c = cs[i];
48778                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48779                         p.id = c.id;
48780                         p.css = p.attr = "";
48781                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48782                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48783                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48784                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48785                         }
48786                         var markup = ct.apply(p);
48787                         if(!c.locked){
48788                             cb+= markup;
48789                         }else{
48790                             lcb+= markup;
48791                         }
48792                     }
48793                     var alt = [];
48794                     if(stripe && ((rowIndex+1) % 2 == 0)){
48795                         alt.push("x-grid-row-alt")
48796                     }
48797                     if(r.dirty){
48798                         alt.push(  " x-grid-dirty-row");
48799                     }
48800                     rp.cells = lcb;
48801                     if(this.getRowClass){
48802                         alt.push(this.getRowClass(r, rowIndex));
48803                     }
48804                     if (hasListener) {
48805                         rowcfg = {
48806                              
48807                             record: r,
48808                             rowIndex : rowIndex,
48809                             rowClass : ''
48810                         }
48811                         this.grid.fireEvent('rowclass', this, rowcfg);
48812                         alt.push(rowcfg.rowClass);
48813                     }
48814                     rp.alt = alt.join(" ");
48815                     lbuf+= rt.apply(rp);
48816                     rp.cells = cb;
48817                     buf+=  rt.apply(rp);
48818                 }
48819                 return [lbuf, buf];
48820             } :
48821             function(cs, rs, ds, startRow, colCount, stripe){
48822                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48823                 // buffers
48824                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48825                 var hasListener = this.grid.hasListener('rowclass');
48826                 var rowcfg = {};
48827                 for(var j = 0, len = rs.length; j < len; j++){
48828                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48829                     for(var i = 0; i < colCount; i++){
48830                         c = cs[i];
48831                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48832                         p.id = c.id;
48833                         p.css = p.attr = "";
48834                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48835                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48836                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48837                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48838                         }
48839                         var markup = ct.apply(p);
48840                         if(!c.locked){
48841                             cb[cb.length] = markup;
48842                         }else{
48843                             lcb[lcb.length] = markup;
48844                         }
48845                     }
48846                     var alt = [];
48847                     if(stripe && ((rowIndex+1) % 2 == 0)){
48848                         alt.push( "x-grid-row-alt");
48849                     }
48850                     if(r.dirty){
48851                         alt.push(" x-grid-dirty-row");
48852                     }
48853                     rp.cells = lcb;
48854                     if(this.getRowClass){
48855                         alt.push( this.getRowClass(r, rowIndex));
48856                     }
48857                     if (hasListener) {
48858                         rowcfg = {
48859                              
48860                             record: r,
48861                             rowIndex : rowIndex,
48862                             rowClass : ''
48863                         }
48864                         this.grid.fireEvent('rowclass', this, rowcfg);
48865                         alt.push(rowcfg.rowClass);
48866                     }
48867                     rp.alt = alt.join(" ");
48868                     rp.cells = lcb.join("");
48869                     lbuf[lbuf.length] = rt.apply(rp);
48870                     rp.cells = cb.join("");
48871                     buf[buf.length] =  rt.apply(rp);
48872                 }
48873                 return [lbuf.join(""), buf.join("")];
48874             },
48875
48876     renderBody : function(){
48877         var markup = this.renderRows();
48878         var bt = this.templates.body;
48879         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48880     },
48881
48882     /**
48883      * Refreshes the grid
48884      * @param {Boolean} headersToo
48885      */
48886     refresh : function(headersToo){
48887         this.fireEvent("beforerefresh", this);
48888         this.grid.stopEditing();
48889         var result = this.renderBody();
48890         this.lockedBody.update(result[0]);
48891         this.mainBody.update(result[1]);
48892         if(headersToo === true){
48893             this.updateHeaders();
48894             this.updateColumns();
48895             this.updateSplitters();
48896             this.updateHeaderSortState();
48897         }
48898         this.syncRowHeights();
48899         this.layout();
48900         this.fireEvent("refresh", this);
48901     },
48902
48903     handleColumnMove : function(cm, oldIndex, newIndex){
48904         this.indexMap = null;
48905         var s = this.getScrollState();
48906         this.refresh(true);
48907         this.restoreScroll(s);
48908         this.afterMove(newIndex);
48909     },
48910
48911     afterMove : function(colIndex){
48912         if(this.enableMoveAnim && Roo.enableFx){
48913             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48914         }
48915         // if multisort - fix sortOrder, and reload..
48916         if (this.grid.dataSource.multiSort) {
48917             // the we can call sort again..
48918             var dm = this.grid.dataSource;
48919             var cm = this.grid.colModel;
48920             var so = [];
48921             for(var i = 0; i < cm.config.length; i++ ) {
48922                 
48923                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48924                     continue; // dont' bother, it's not in sort list or being set.
48925                 }
48926                 
48927                 so.push(cm.config[i].dataIndex);
48928             };
48929             dm.sortOrder = so;
48930             dm.load(dm.lastOptions);
48931             
48932             
48933         }
48934         
48935     },
48936
48937     updateCell : function(dm, rowIndex, dataIndex){
48938         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48939         if(typeof colIndex == "undefined"){ // not present in grid
48940             return;
48941         }
48942         var cm = this.grid.colModel;
48943         var cell = this.getCell(rowIndex, colIndex);
48944         var cellText = this.getCellText(rowIndex, colIndex);
48945
48946         var p = {
48947             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48948             id : cm.getColumnId(colIndex),
48949             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48950         };
48951         var renderer = cm.getRenderer(colIndex);
48952         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48953         if(typeof val == "undefined" || val === "") val = "&#160;";
48954         cellText.innerHTML = val;
48955         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48956         this.syncRowHeights(rowIndex, rowIndex);
48957     },
48958
48959     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48960         var maxWidth = 0;
48961         if(this.grid.autoSizeHeaders){
48962             var h = this.getHeaderCellMeasure(colIndex);
48963             maxWidth = Math.max(maxWidth, h.scrollWidth);
48964         }
48965         var tb, index;
48966         if(this.cm.isLocked(colIndex)){
48967             tb = this.getLockedTable();
48968             index = colIndex;
48969         }else{
48970             tb = this.getBodyTable();
48971             index = colIndex - this.cm.getLockedCount();
48972         }
48973         if(tb && tb.rows){
48974             var rows = tb.rows;
48975             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48976             for(var i = 0; i < stopIndex; i++){
48977                 var cell = rows[i].childNodes[index].firstChild;
48978                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48979             }
48980         }
48981         return maxWidth + /*margin for error in IE*/ 5;
48982     },
48983     /**
48984      * Autofit a column to its content.
48985      * @param {Number} colIndex
48986      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48987      */
48988      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48989          if(this.cm.isHidden(colIndex)){
48990              return; // can't calc a hidden column
48991          }
48992         if(forceMinSize){
48993             var cid = this.cm.getColumnId(colIndex);
48994             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48995            if(this.grid.autoSizeHeaders){
48996                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48997            }
48998         }
48999         var newWidth = this.calcColumnWidth(colIndex);
49000         this.cm.setColumnWidth(colIndex,
49001             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49002         if(!suppressEvent){
49003             this.grid.fireEvent("columnresize", colIndex, newWidth);
49004         }
49005     },
49006
49007     /**
49008      * Autofits all columns to their content and then expands to fit any extra space in the grid
49009      */
49010      autoSizeColumns : function(){
49011         var cm = this.grid.colModel;
49012         var colCount = cm.getColumnCount();
49013         for(var i = 0; i < colCount; i++){
49014             this.autoSizeColumn(i, true, true);
49015         }
49016         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49017             this.fitColumns();
49018         }else{
49019             this.updateColumns();
49020             this.layout();
49021         }
49022     },
49023
49024     /**
49025      * Autofits all columns to the grid's width proportionate with their current size
49026      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49027      */
49028     fitColumns : function(reserveScrollSpace){
49029         var cm = this.grid.colModel;
49030         var colCount = cm.getColumnCount();
49031         var cols = [];
49032         var width = 0;
49033         var i, w;
49034         for (i = 0; i < colCount; i++){
49035             if(!cm.isHidden(i) && !cm.isFixed(i)){
49036                 w = cm.getColumnWidth(i);
49037                 cols.push(i);
49038                 cols.push(w);
49039                 width += w;
49040             }
49041         }
49042         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49043         if(reserveScrollSpace){
49044             avail -= 17;
49045         }
49046         var frac = (avail - cm.getTotalWidth())/width;
49047         while (cols.length){
49048             w = cols.pop();
49049             i = cols.pop();
49050             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49051         }
49052         this.updateColumns();
49053         this.layout();
49054     },
49055
49056     onRowSelect : function(rowIndex){
49057         var row = this.getRowComposite(rowIndex);
49058         row.addClass("x-grid-row-selected");
49059     },
49060
49061     onRowDeselect : function(rowIndex){
49062         var row = this.getRowComposite(rowIndex);
49063         row.removeClass("x-grid-row-selected");
49064     },
49065
49066     onCellSelect : function(row, col){
49067         var cell = this.getCell(row, col);
49068         if(cell){
49069             Roo.fly(cell).addClass("x-grid-cell-selected");
49070         }
49071     },
49072
49073     onCellDeselect : function(row, col){
49074         var cell = this.getCell(row, col);
49075         if(cell){
49076             Roo.fly(cell).removeClass("x-grid-cell-selected");
49077         }
49078     },
49079
49080     updateHeaderSortState : function(){
49081         
49082         // sort state can be single { field: xxx, direction : yyy}
49083         // or   { xxx=>ASC , yyy : DESC ..... }
49084         
49085         var mstate = {};
49086         if (!this.ds.multiSort) { 
49087             var state = this.ds.getSortState();
49088             if(!state){
49089                 return;
49090             }
49091             mstate[state.field] = state.direction;
49092             // FIXME... - this is not used here.. but might be elsewhere..
49093             this.sortState = state;
49094             
49095         } else {
49096             mstate = this.ds.sortToggle;
49097         }
49098         //remove existing sort classes..
49099         
49100         var sc = this.sortClasses;
49101         var hds = this.el.select(this.headerSelector).removeClass(sc);
49102         
49103         for(var f in mstate) {
49104         
49105             var sortColumn = this.cm.findColumnIndex(f);
49106             
49107             if(sortColumn != -1){
49108                 var sortDir = mstate[f];        
49109                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49110             }
49111         }
49112         
49113          
49114         
49115     },
49116
49117
49118     handleHeaderClick : function(g, index){
49119         if(this.headersDisabled){
49120             return;
49121         }
49122         var dm = g.dataSource, cm = g.colModel;
49123         if(!cm.isSortable(index)){
49124             return;
49125         }
49126         g.stopEditing();
49127         
49128         if (dm.multiSort) {
49129             // update the sortOrder
49130             var so = [];
49131             for(var i = 0; i < cm.config.length; i++ ) {
49132                 
49133                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49134                     continue; // dont' bother, it's not in sort list or being set.
49135                 }
49136                 
49137                 so.push(cm.config[i].dataIndex);
49138             };
49139             dm.sortOrder = so;
49140         }
49141         
49142         
49143         dm.sort(cm.getDataIndex(index));
49144     },
49145
49146
49147     destroy : function(){
49148         if(this.colMenu){
49149             this.colMenu.removeAll();
49150             Roo.menu.MenuMgr.unregister(this.colMenu);
49151             this.colMenu.getEl().remove();
49152             delete this.colMenu;
49153         }
49154         if(this.hmenu){
49155             this.hmenu.removeAll();
49156             Roo.menu.MenuMgr.unregister(this.hmenu);
49157             this.hmenu.getEl().remove();
49158             delete this.hmenu;
49159         }
49160         if(this.grid.enableColumnMove){
49161             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49162             if(dds){
49163                 for(var dd in dds){
49164                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49165                         var elid = dds[dd].dragElId;
49166                         dds[dd].unreg();
49167                         Roo.get(elid).remove();
49168                     } else if(dds[dd].config.isTarget){
49169                         dds[dd].proxyTop.remove();
49170                         dds[dd].proxyBottom.remove();
49171                         dds[dd].unreg();
49172                     }
49173                     if(Roo.dd.DDM.locationCache[dd]){
49174                         delete Roo.dd.DDM.locationCache[dd];
49175                     }
49176                 }
49177                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49178             }
49179         }
49180         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49181         this.bind(null, null);
49182         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49183     },
49184
49185     handleLockChange : function(){
49186         this.refresh(true);
49187     },
49188
49189     onDenyColumnLock : function(){
49190
49191     },
49192
49193     onDenyColumnHide : function(){
49194
49195     },
49196
49197     handleHdMenuClick : function(item){
49198         var index = this.hdCtxIndex;
49199         var cm = this.cm, ds = this.ds;
49200         switch(item.id){
49201             case "asc":
49202                 ds.sort(cm.getDataIndex(index), "ASC");
49203                 break;
49204             case "desc":
49205                 ds.sort(cm.getDataIndex(index), "DESC");
49206                 break;
49207             case "lock":
49208                 var lc = cm.getLockedCount();
49209                 if(cm.getColumnCount(true) <= lc+1){
49210                     this.onDenyColumnLock();
49211                     return;
49212                 }
49213                 if(lc != index){
49214                     cm.setLocked(index, true, true);
49215                     cm.moveColumn(index, lc);
49216                     this.grid.fireEvent("columnmove", index, lc);
49217                 }else{
49218                     cm.setLocked(index, true);
49219                 }
49220             break;
49221             case "unlock":
49222                 var lc = cm.getLockedCount();
49223                 if((lc-1) != index){
49224                     cm.setLocked(index, false, true);
49225                     cm.moveColumn(index, lc-1);
49226                     this.grid.fireEvent("columnmove", index, lc-1);
49227                 }else{
49228                     cm.setLocked(index, false);
49229                 }
49230             break;
49231             default:
49232                 index = cm.getIndexById(item.id.substr(4));
49233                 if(index != -1){
49234                     if(item.checked && cm.getColumnCount(true) <= 1){
49235                         this.onDenyColumnHide();
49236                         return false;
49237                     }
49238                     cm.setHidden(index, item.checked);
49239                 }
49240         }
49241         return true;
49242     },
49243
49244     beforeColMenuShow : function(){
49245         var cm = this.cm,  colCount = cm.getColumnCount();
49246         this.colMenu.removeAll();
49247         for(var i = 0; i < colCount; i++){
49248             this.colMenu.add(new Roo.menu.CheckItem({
49249                 id: "col-"+cm.getColumnId(i),
49250                 text: cm.getColumnHeader(i),
49251                 checked: !cm.isHidden(i),
49252                 hideOnClick:false
49253             }));
49254         }
49255     },
49256
49257     handleHdCtx : function(g, index, e){
49258         e.stopEvent();
49259         var hd = this.getHeaderCell(index);
49260         this.hdCtxIndex = index;
49261         var ms = this.hmenu.items, cm = this.cm;
49262         ms.get("asc").setDisabled(!cm.isSortable(index));
49263         ms.get("desc").setDisabled(!cm.isSortable(index));
49264         if(this.grid.enableColLock !== false){
49265             ms.get("lock").setDisabled(cm.isLocked(index));
49266             ms.get("unlock").setDisabled(!cm.isLocked(index));
49267         }
49268         this.hmenu.show(hd, "tl-bl");
49269     },
49270
49271     handleHdOver : function(e){
49272         var hd = this.findHeaderCell(e.getTarget());
49273         if(hd && !this.headersDisabled){
49274             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49275                this.fly(hd).addClass("x-grid-hd-over");
49276             }
49277         }
49278     },
49279
49280     handleHdOut : function(e){
49281         var hd = this.findHeaderCell(e.getTarget());
49282         if(hd){
49283             this.fly(hd).removeClass("x-grid-hd-over");
49284         }
49285     },
49286
49287     handleSplitDblClick : function(e, t){
49288         var i = this.getCellIndex(t);
49289         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49290             this.autoSizeColumn(i, true);
49291             this.layout();
49292         }
49293     },
49294
49295     render : function(){
49296
49297         var cm = this.cm;
49298         var colCount = cm.getColumnCount();
49299
49300         if(this.grid.monitorWindowResize === true){
49301             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49302         }
49303         var header = this.renderHeaders();
49304         var body = this.templates.body.apply({rows:""});
49305         var html = this.templates.master.apply({
49306             lockedBody: body,
49307             body: body,
49308             lockedHeader: header[0],
49309             header: header[1]
49310         });
49311
49312         //this.updateColumns();
49313
49314         this.grid.getGridEl().dom.innerHTML = html;
49315
49316         this.initElements();
49317         
49318         // a kludge to fix the random scolling effect in webkit
49319         this.el.on("scroll", function() {
49320             this.el.dom.scrollTop=0; // hopefully not recursive..
49321         },this);
49322
49323         this.scroller.on("scroll", this.handleScroll, this);
49324         this.lockedBody.on("mousewheel", this.handleWheel, this);
49325         this.mainBody.on("mousewheel", this.handleWheel, this);
49326
49327         this.mainHd.on("mouseover", this.handleHdOver, this);
49328         this.mainHd.on("mouseout", this.handleHdOut, this);
49329         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49330                 {delegate: "."+this.splitClass});
49331
49332         this.lockedHd.on("mouseover", this.handleHdOver, this);
49333         this.lockedHd.on("mouseout", this.handleHdOut, this);
49334         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49335                 {delegate: "."+this.splitClass});
49336
49337         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49338             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49339         }
49340
49341         this.updateSplitters();
49342
49343         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49344             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49345             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49346         }
49347
49348         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49349             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49350             this.hmenu.add(
49351                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49352                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49353             );
49354             if(this.grid.enableColLock !== false){
49355                 this.hmenu.add('-',
49356                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49357                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49358                 );
49359             }
49360             if(this.grid.enableColumnHide !== false){
49361
49362                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49363                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49364                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49365
49366                 this.hmenu.add('-',
49367                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49368                 );
49369             }
49370             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49371
49372             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49373         }
49374
49375         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49376             this.dd = new Roo.grid.GridDragZone(this.grid, {
49377                 ddGroup : this.grid.ddGroup || 'GridDD'
49378             });
49379         }
49380
49381         /*
49382         for(var i = 0; i < colCount; i++){
49383             if(cm.isHidden(i)){
49384                 this.hideColumn(i);
49385             }
49386             if(cm.config[i].align){
49387                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49388                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49389             }
49390         }*/
49391         
49392         this.updateHeaderSortState();
49393
49394         this.beforeInitialResize();
49395         this.layout(true);
49396
49397         // two part rendering gives faster view to the user
49398         this.renderPhase2.defer(1, this);
49399     },
49400
49401     renderPhase2 : function(){
49402         // render the rows now
49403         this.refresh();
49404         if(this.grid.autoSizeColumns){
49405             this.autoSizeColumns();
49406         }
49407     },
49408
49409     beforeInitialResize : function(){
49410
49411     },
49412
49413     onColumnSplitterMoved : function(i, w){
49414         this.userResized = true;
49415         var cm = this.grid.colModel;
49416         cm.setColumnWidth(i, w, true);
49417         var cid = cm.getColumnId(i);
49418         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49419         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49420         this.updateSplitters();
49421         this.layout();
49422         this.grid.fireEvent("columnresize", i, w);
49423     },
49424
49425     syncRowHeights : function(startIndex, endIndex){
49426         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49427             startIndex = startIndex || 0;
49428             var mrows = this.getBodyTable().rows;
49429             var lrows = this.getLockedTable().rows;
49430             var len = mrows.length-1;
49431             endIndex = Math.min(endIndex || len, len);
49432             for(var i = startIndex; i <= endIndex; i++){
49433                 var m = mrows[i], l = lrows[i];
49434                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49435                 m.style.height = l.style.height = h + "px";
49436             }
49437         }
49438     },
49439
49440     layout : function(initialRender, is2ndPass){
49441         var g = this.grid;
49442         var auto = g.autoHeight;
49443         var scrollOffset = 16;
49444         var c = g.getGridEl(), cm = this.cm,
49445                 expandCol = g.autoExpandColumn,
49446                 gv = this;
49447         //c.beginMeasure();
49448
49449         if(!c.dom.offsetWidth){ // display:none?
49450             if(initialRender){
49451                 this.lockedWrap.show();
49452                 this.mainWrap.show();
49453             }
49454             return;
49455         }
49456
49457         var hasLock = this.cm.isLocked(0);
49458
49459         var tbh = this.headerPanel.getHeight();
49460         var bbh = this.footerPanel.getHeight();
49461
49462         if(auto){
49463             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49464             var newHeight = ch + c.getBorderWidth("tb");
49465             if(g.maxHeight){
49466                 newHeight = Math.min(g.maxHeight, newHeight);
49467             }
49468             c.setHeight(newHeight);
49469         }
49470
49471         if(g.autoWidth){
49472             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49473         }
49474
49475         var s = this.scroller;
49476
49477         var csize = c.getSize(true);
49478
49479         this.el.setSize(csize.width, csize.height);
49480
49481         this.headerPanel.setWidth(csize.width);
49482         this.footerPanel.setWidth(csize.width);
49483
49484         var hdHeight = this.mainHd.getHeight();
49485         var vw = csize.width;
49486         var vh = csize.height - (tbh + bbh);
49487
49488         s.setSize(vw, vh);
49489
49490         var bt = this.getBodyTable();
49491         var ltWidth = hasLock ?
49492                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49493
49494         var scrollHeight = bt.offsetHeight;
49495         var scrollWidth = ltWidth + bt.offsetWidth;
49496         var vscroll = false, hscroll = false;
49497
49498         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49499
49500         var lw = this.lockedWrap, mw = this.mainWrap;
49501         var lb = this.lockedBody, mb = this.mainBody;
49502
49503         setTimeout(function(){
49504             var t = s.dom.offsetTop;
49505             var w = s.dom.clientWidth,
49506                 h = s.dom.clientHeight;
49507
49508             lw.setTop(t);
49509             lw.setSize(ltWidth, h);
49510
49511             mw.setLeftTop(ltWidth, t);
49512             mw.setSize(w-ltWidth, h);
49513
49514             lb.setHeight(h-hdHeight);
49515             mb.setHeight(h-hdHeight);
49516
49517             if(is2ndPass !== true && !gv.userResized && expandCol){
49518                 // high speed resize without full column calculation
49519                 
49520                 var ci = cm.getIndexById(expandCol);
49521                 if (ci < 0) {
49522                     ci = cm.findColumnIndex(expandCol);
49523                 }
49524                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49525                 var expandId = cm.getColumnId(ci);
49526                 var  tw = cm.getTotalWidth(false);
49527                 var currentWidth = cm.getColumnWidth(ci);
49528                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49529                 if(currentWidth != cw){
49530                     cm.setColumnWidth(ci, cw, true);
49531                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49532                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49533                     gv.updateSplitters();
49534                     gv.layout(false, true);
49535                 }
49536             }
49537
49538             if(initialRender){
49539                 lw.show();
49540                 mw.show();
49541             }
49542             //c.endMeasure();
49543         }, 10);
49544     },
49545
49546     onWindowResize : function(){
49547         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49548             return;
49549         }
49550         this.layout();
49551     },
49552
49553     appendFooter : function(parentEl){
49554         return null;
49555     },
49556
49557     sortAscText : "Sort Ascending",
49558     sortDescText : "Sort Descending",
49559     lockText : "Lock Column",
49560     unlockText : "Unlock Column",
49561     columnsText : "Columns"
49562 });
49563
49564
49565 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49566     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49567     this.proxy.el.addClass('x-grid3-col-dd');
49568 };
49569
49570 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49571     handleMouseDown : function(e){
49572
49573     },
49574
49575     callHandleMouseDown : function(e){
49576         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49577     }
49578 });
49579 /*
49580  * Based on:
49581  * Ext JS Library 1.1.1
49582  * Copyright(c) 2006-2007, Ext JS, LLC.
49583  *
49584  * Originally Released Under LGPL - original licence link has changed is not relivant.
49585  *
49586  * Fork - LGPL
49587  * <script type="text/javascript">
49588  */
49589  
49590 // private
49591 // This is a support class used internally by the Grid components
49592 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49593     this.grid = grid;
49594     this.view = grid.getView();
49595     this.proxy = this.view.resizeProxy;
49596     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49597         "gridSplitters" + this.grid.getGridEl().id, {
49598         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49599     });
49600     this.setHandleElId(Roo.id(hd));
49601     this.setOuterHandleElId(Roo.id(hd2));
49602     this.scroll = false;
49603 };
49604 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49605     fly: Roo.Element.fly,
49606
49607     b4StartDrag : function(x, y){
49608         this.view.headersDisabled = true;
49609         this.proxy.setHeight(this.view.mainWrap.getHeight());
49610         var w = this.cm.getColumnWidth(this.cellIndex);
49611         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49612         this.resetConstraints();
49613         this.setXConstraint(minw, 1000);
49614         this.setYConstraint(0, 0);
49615         this.minX = x - minw;
49616         this.maxX = x + 1000;
49617         this.startPos = x;
49618         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49619     },
49620
49621
49622     handleMouseDown : function(e){
49623         ev = Roo.EventObject.setEvent(e);
49624         var t = this.fly(ev.getTarget());
49625         if(t.hasClass("x-grid-split")){
49626             this.cellIndex = this.view.getCellIndex(t.dom);
49627             this.split = t.dom;
49628             this.cm = this.grid.colModel;
49629             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49630                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49631             }
49632         }
49633     },
49634
49635     endDrag : function(e){
49636         this.view.headersDisabled = false;
49637         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49638         var diff = endX - this.startPos;
49639         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49640     },
49641
49642     autoOffset : function(){
49643         this.setDelta(0,0);
49644     }
49645 });/*
49646  * Based on:
49647  * Ext JS Library 1.1.1
49648  * Copyright(c) 2006-2007, Ext JS, LLC.
49649  *
49650  * Originally Released Under LGPL - original licence link has changed is not relivant.
49651  *
49652  * Fork - LGPL
49653  * <script type="text/javascript">
49654  */
49655  
49656 // private
49657 // This is a support class used internally by the Grid components
49658 Roo.grid.GridDragZone = function(grid, config){
49659     this.view = grid.getView();
49660     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49661     if(this.view.lockedBody){
49662         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49663         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49664     }
49665     this.scroll = false;
49666     this.grid = grid;
49667     this.ddel = document.createElement('div');
49668     this.ddel.className = 'x-grid-dd-wrap';
49669 };
49670
49671 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49672     ddGroup : "GridDD",
49673
49674     getDragData : function(e){
49675         var t = Roo.lib.Event.getTarget(e);
49676         var rowIndex = this.view.findRowIndex(t);
49677         if(rowIndex !== false){
49678             var sm = this.grid.selModel;
49679             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49680               //  sm.mouseDown(e, t);
49681             //}
49682             if (e.hasModifier()){
49683                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49684             }
49685             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49686         }
49687         return false;
49688     },
49689
49690     onInitDrag : function(e){
49691         var data = this.dragData;
49692         this.ddel.innerHTML = this.grid.getDragDropText();
49693         this.proxy.update(this.ddel);
49694         // fire start drag?
49695     },
49696
49697     afterRepair : function(){
49698         this.dragging = false;
49699     },
49700
49701     getRepairXY : function(e, data){
49702         return false;
49703     },
49704
49705     onEndDrag : function(data, e){
49706         // fire end drag?
49707     },
49708
49709     onValidDrop : function(dd, e, id){
49710         // fire drag drop?
49711         this.hideProxy();
49712     },
49713
49714     beforeInvalidDrop : function(e, id){
49715
49716     }
49717 });/*
49718  * Based on:
49719  * Ext JS Library 1.1.1
49720  * Copyright(c) 2006-2007, Ext JS, LLC.
49721  *
49722  * Originally Released Under LGPL - original licence link has changed is not relivant.
49723  *
49724  * Fork - LGPL
49725  * <script type="text/javascript">
49726  */
49727  
49728
49729 /**
49730  * @class Roo.grid.ColumnModel
49731  * @extends Roo.util.Observable
49732  * This is the default implementation of a ColumnModel used by the Grid. It defines
49733  * the columns in the grid.
49734  * <br>Usage:<br>
49735  <pre><code>
49736  var colModel = new Roo.grid.ColumnModel([
49737         {header: "Ticker", width: 60, sortable: true, locked: true},
49738         {header: "Company Name", width: 150, sortable: true},
49739         {header: "Market Cap.", width: 100, sortable: true},
49740         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49741         {header: "Employees", width: 100, sortable: true, resizable: false}
49742  ]);
49743  </code></pre>
49744  * <p>
49745  
49746  * The config options listed for this class are options which may appear in each
49747  * individual column definition.
49748  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49749  * @constructor
49750  * @param {Object} config An Array of column config objects. See this class's
49751  * config objects for details.
49752 */
49753 Roo.grid.ColumnModel = function(config){
49754         /**
49755      * The config passed into the constructor
49756      */
49757     this.config = config;
49758     this.lookup = {};
49759
49760     // if no id, create one
49761     // if the column does not have a dataIndex mapping,
49762     // map it to the order it is in the config
49763     for(var i = 0, len = config.length; i < len; i++){
49764         var c = config[i];
49765         if(typeof c.dataIndex == "undefined"){
49766             c.dataIndex = i;
49767         }
49768         if(typeof c.renderer == "string"){
49769             c.renderer = Roo.util.Format[c.renderer];
49770         }
49771         if(typeof c.id == "undefined"){
49772             c.id = Roo.id();
49773         }
49774         if(c.editor && c.editor.xtype){
49775             c.editor  = Roo.factory(c.editor, Roo.grid);
49776         }
49777         if(c.editor && c.editor.isFormField){
49778             c.editor = new Roo.grid.GridEditor(c.editor);
49779         }
49780         this.lookup[c.id] = c;
49781     }
49782
49783     /**
49784      * The width of columns which have no width specified (defaults to 100)
49785      * @type Number
49786      */
49787     this.defaultWidth = 100;
49788
49789     /**
49790      * Default sortable of columns which have no sortable specified (defaults to false)
49791      * @type Boolean
49792      */
49793     this.defaultSortable = false;
49794
49795     this.addEvents({
49796         /**
49797              * @event widthchange
49798              * Fires when the width of a column changes.
49799              * @param {ColumnModel} this
49800              * @param {Number} columnIndex The column index
49801              * @param {Number} newWidth The new width
49802              */
49803             "widthchange": true,
49804         /**
49805              * @event headerchange
49806              * Fires when the text of a header changes.
49807              * @param {ColumnModel} this
49808              * @param {Number} columnIndex The column index
49809              * @param {Number} newText The new header text
49810              */
49811             "headerchange": true,
49812         /**
49813              * @event hiddenchange
49814              * Fires when a column is hidden or "unhidden".
49815              * @param {ColumnModel} this
49816              * @param {Number} columnIndex The column index
49817              * @param {Boolean} hidden true if hidden, false otherwise
49818              */
49819             "hiddenchange": true,
49820             /**
49821          * @event columnmoved
49822          * Fires when a column is moved.
49823          * @param {ColumnModel} this
49824          * @param {Number} oldIndex
49825          * @param {Number} newIndex
49826          */
49827         "columnmoved" : true,
49828         /**
49829          * @event columlockchange
49830          * Fires when a column's locked state is changed
49831          * @param {ColumnModel} this
49832          * @param {Number} colIndex
49833          * @param {Boolean} locked true if locked
49834          */
49835         "columnlockchange" : true
49836     });
49837     Roo.grid.ColumnModel.superclass.constructor.call(this);
49838 };
49839 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49840     /**
49841      * @cfg {String} header The header text to display in the Grid view.
49842      */
49843     /**
49844      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49845      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49846      * specified, the column's index is used as an index into the Record's data Array.
49847      */
49848     /**
49849      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49850      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49851      */
49852     /**
49853      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49854      * Defaults to the value of the {@link #defaultSortable} property.
49855      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49856      */
49857     /**
49858      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49859      */
49860     /**
49861      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49862      */
49863     /**
49864      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49865      */
49866     /**
49867      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49868      */
49869     /**
49870      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49871      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49872      * default renderer uses the raw data value.
49873      */
49874        /**
49875      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49876      */
49877     /**
49878      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49879      */
49880
49881     /**
49882      * Returns the id of the column at the specified index.
49883      * @param {Number} index The column index
49884      * @return {String} the id
49885      */
49886     getColumnId : function(index){
49887         return this.config[index].id;
49888     },
49889
49890     /**
49891      * Returns the column for a specified id.
49892      * @param {String} id The column id
49893      * @return {Object} the column
49894      */
49895     getColumnById : function(id){
49896         return this.lookup[id];
49897     },
49898
49899     
49900     /**
49901      * Returns the column for a specified dataIndex.
49902      * @param {String} dataIndex The column dataIndex
49903      * @return {Object|Boolean} the column or false if not found
49904      */
49905     getColumnByDataIndex: function(dataIndex){
49906         var index = this.findColumnIndex(dataIndex);
49907         return index > -1 ? this.config[index] : false;
49908     },
49909     
49910     /**
49911      * Returns the index for a specified column id.
49912      * @param {String} id The column id
49913      * @return {Number} the index, or -1 if not found
49914      */
49915     getIndexById : function(id){
49916         for(var i = 0, len = this.config.length; i < len; i++){
49917             if(this.config[i].id == id){
49918                 return i;
49919             }
49920         }
49921         return -1;
49922     },
49923     
49924     /**
49925      * Returns the index for a specified column dataIndex.
49926      * @param {String} dataIndex The column dataIndex
49927      * @return {Number} the index, or -1 if not found
49928      */
49929     
49930     findColumnIndex : function(dataIndex){
49931         for(var i = 0, len = this.config.length; i < len; i++){
49932             if(this.config[i].dataIndex == dataIndex){
49933                 return i;
49934             }
49935         }
49936         return -1;
49937     },
49938     
49939     
49940     moveColumn : function(oldIndex, newIndex){
49941         var c = this.config[oldIndex];
49942         this.config.splice(oldIndex, 1);
49943         this.config.splice(newIndex, 0, c);
49944         this.dataMap = null;
49945         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49946     },
49947
49948     isLocked : function(colIndex){
49949         return this.config[colIndex].locked === true;
49950     },
49951
49952     setLocked : function(colIndex, value, suppressEvent){
49953         if(this.isLocked(colIndex) == value){
49954             return;
49955         }
49956         this.config[colIndex].locked = value;
49957         if(!suppressEvent){
49958             this.fireEvent("columnlockchange", this, colIndex, value);
49959         }
49960     },
49961
49962     getTotalLockedWidth : function(){
49963         var totalWidth = 0;
49964         for(var i = 0; i < this.config.length; i++){
49965             if(this.isLocked(i) && !this.isHidden(i)){
49966                 this.totalWidth += this.getColumnWidth(i);
49967             }
49968         }
49969         return totalWidth;
49970     },
49971
49972     getLockedCount : function(){
49973         for(var i = 0, len = this.config.length; i < len; i++){
49974             if(!this.isLocked(i)){
49975                 return i;
49976             }
49977         }
49978     },
49979
49980     /**
49981      * Returns the number of columns.
49982      * @return {Number}
49983      */
49984     getColumnCount : function(visibleOnly){
49985         if(visibleOnly === true){
49986             var c = 0;
49987             for(var i = 0, len = this.config.length; i < len; i++){
49988                 if(!this.isHidden(i)){
49989                     c++;
49990                 }
49991             }
49992             return c;
49993         }
49994         return this.config.length;
49995     },
49996
49997     /**
49998      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49999      * @param {Function} fn
50000      * @param {Object} scope (optional)
50001      * @return {Array} result
50002      */
50003     getColumnsBy : function(fn, scope){
50004         var r = [];
50005         for(var i = 0, len = this.config.length; i < len; i++){
50006             var c = this.config[i];
50007             if(fn.call(scope||this, c, i) === true){
50008                 r[r.length] = c;
50009             }
50010         }
50011         return r;
50012     },
50013
50014     /**
50015      * Returns true if the specified column is sortable.
50016      * @param {Number} col The column index
50017      * @return {Boolean}
50018      */
50019     isSortable : function(col){
50020         if(typeof this.config[col].sortable == "undefined"){
50021             return this.defaultSortable;
50022         }
50023         return this.config[col].sortable;
50024     },
50025
50026     /**
50027      * Returns the rendering (formatting) function defined for the column.
50028      * @param {Number} col The column index.
50029      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50030      */
50031     getRenderer : function(col){
50032         if(!this.config[col].renderer){
50033             return Roo.grid.ColumnModel.defaultRenderer;
50034         }
50035         return this.config[col].renderer;
50036     },
50037
50038     /**
50039      * Sets the rendering (formatting) function for a column.
50040      * @param {Number} col The column index
50041      * @param {Function} fn The function to use to process the cell's raw data
50042      * to return HTML markup for the grid view. The render function is called with
50043      * the following parameters:<ul>
50044      * <li>Data value.</li>
50045      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50046      * <li>css A CSS style string to apply to the table cell.</li>
50047      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50048      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50049      * <li>Row index</li>
50050      * <li>Column index</li>
50051      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50052      */
50053     setRenderer : function(col, fn){
50054         this.config[col].renderer = fn;
50055     },
50056
50057     /**
50058      * Returns the width for the specified column.
50059      * @param {Number} col The column index
50060      * @return {Number}
50061      */
50062     getColumnWidth : function(col){
50063         return this.config[col].width * 1 || this.defaultWidth;
50064     },
50065
50066     /**
50067      * Sets the width for a column.
50068      * @param {Number} col The column index
50069      * @param {Number} width The new width
50070      */
50071     setColumnWidth : function(col, width, suppressEvent){
50072         this.config[col].width = width;
50073         this.totalWidth = null;
50074         if(!suppressEvent){
50075              this.fireEvent("widthchange", this, col, width);
50076         }
50077     },
50078
50079     /**
50080      * Returns the total width of all columns.
50081      * @param {Boolean} includeHidden True to include hidden column widths
50082      * @return {Number}
50083      */
50084     getTotalWidth : function(includeHidden){
50085         if(!this.totalWidth){
50086             this.totalWidth = 0;
50087             for(var i = 0, len = this.config.length; i < len; i++){
50088                 if(includeHidden || !this.isHidden(i)){
50089                     this.totalWidth += this.getColumnWidth(i);
50090                 }
50091             }
50092         }
50093         return this.totalWidth;
50094     },
50095
50096     /**
50097      * Returns the header for the specified column.
50098      * @param {Number} col The column index
50099      * @return {String}
50100      */
50101     getColumnHeader : function(col){
50102         return this.config[col].header;
50103     },
50104
50105     /**
50106      * Sets the header for a column.
50107      * @param {Number} col The column index
50108      * @param {String} header The new header
50109      */
50110     setColumnHeader : function(col, header){
50111         this.config[col].header = header;
50112         this.fireEvent("headerchange", this, col, header);
50113     },
50114
50115     /**
50116      * Returns the tooltip for the specified column.
50117      * @param {Number} col The column index
50118      * @return {String}
50119      */
50120     getColumnTooltip : function(col){
50121             return this.config[col].tooltip;
50122     },
50123     /**
50124      * Sets the tooltip for a column.
50125      * @param {Number} col The column index
50126      * @param {String} tooltip The new tooltip
50127      */
50128     setColumnTooltip : function(col, tooltip){
50129             this.config[col].tooltip = tooltip;
50130     },
50131
50132     /**
50133      * Returns the dataIndex for the specified column.
50134      * @param {Number} col The column index
50135      * @return {Number}
50136      */
50137     getDataIndex : function(col){
50138         return this.config[col].dataIndex;
50139     },
50140
50141     /**
50142      * Sets the dataIndex for a column.
50143      * @param {Number} col The column index
50144      * @param {Number} dataIndex The new dataIndex
50145      */
50146     setDataIndex : function(col, dataIndex){
50147         this.config[col].dataIndex = dataIndex;
50148     },
50149
50150     
50151     
50152     /**
50153      * Returns true if the cell is editable.
50154      * @param {Number} colIndex The column index
50155      * @param {Number} rowIndex The row index
50156      * @return {Boolean}
50157      */
50158     isCellEditable : function(colIndex, rowIndex){
50159         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50160     },
50161
50162     /**
50163      * Returns the editor defined for the cell/column.
50164      * return false or null to disable editing.
50165      * @param {Number} colIndex The column index
50166      * @param {Number} rowIndex The row index
50167      * @return {Object}
50168      */
50169     getCellEditor : function(colIndex, rowIndex){
50170         return this.config[colIndex].editor;
50171     },
50172
50173     /**
50174      * Sets if a column is editable.
50175      * @param {Number} col The column index
50176      * @param {Boolean} editable True if the column is editable
50177      */
50178     setEditable : function(col, editable){
50179         this.config[col].editable = editable;
50180     },
50181
50182
50183     /**
50184      * Returns true if the column is hidden.
50185      * @param {Number} colIndex The column index
50186      * @return {Boolean}
50187      */
50188     isHidden : function(colIndex){
50189         return this.config[colIndex].hidden;
50190     },
50191
50192
50193     /**
50194      * Returns true if the column width cannot be changed
50195      */
50196     isFixed : function(colIndex){
50197         return this.config[colIndex].fixed;
50198     },
50199
50200     /**
50201      * Returns true if the column can be resized
50202      * @return {Boolean}
50203      */
50204     isResizable : function(colIndex){
50205         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50206     },
50207     /**
50208      * Sets if a column is hidden.
50209      * @param {Number} colIndex The column index
50210      * @param {Boolean} hidden True if the column is hidden
50211      */
50212     setHidden : function(colIndex, hidden){
50213         this.config[colIndex].hidden = hidden;
50214         this.totalWidth = null;
50215         this.fireEvent("hiddenchange", this, colIndex, hidden);
50216     },
50217
50218     /**
50219      * Sets the editor for a column.
50220      * @param {Number} col The column index
50221      * @param {Object} editor The editor object
50222      */
50223     setEditor : function(col, editor){
50224         this.config[col].editor = editor;
50225     }
50226 });
50227
50228 Roo.grid.ColumnModel.defaultRenderer = function(value){
50229         if(typeof value == "string" && value.length < 1){
50230             return "&#160;";
50231         }
50232         return value;
50233 };
50234
50235 // Alias for backwards compatibility
50236 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50237 /*
50238  * Based on:
50239  * Ext JS Library 1.1.1
50240  * Copyright(c) 2006-2007, Ext JS, LLC.
50241  *
50242  * Originally Released Under LGPL - original licence link has changed is not relivant.
50243  *
50244  * Fork - LGPL
50245  * <script type="text/javascript">
50246  */
50247
50248 /**
50249  * @class Roo.grid.AbstractSelectionModel
50250  * @extends Roo.util.Observable
50251  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50252  * implemented by descendant classes.  This class should not be directly instantiated.
50253  * @constructor
50254  */
50255 Roo.grid.AbstractSelectionModel = function(){
50256     this.locked = false;
50257     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50258 };
50259
50260 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50261     /** @ignore Called by the grid automatically. Do not call directly. */
50262     init : function(grid){
50263         this.grid = grid;
50264         this.initEvents();
50265     },
50266
50267     /**
50268      * Locks the selections.
50269      */
50270     lock : function(){
50271         this.locked = true;
50272     },
50273
50274     /**
50275      * Unlocks the selections.
50276      */
50277     unlock : function(){
50278         this.locked = false;
50279     },
50280
50281     /**
50282      * Returns true if the selections are locked.
50283      * @return {Boolean}
50284      */
50285     isLocked : function(){
50286         return this.locked;
50287     }
50288 });/*
50289  * Based on:
50290  * Ext JS Library 1.1.1
50291  * Copyright(c) 2006-2007, Ext JS, LLC.
50292  *
50293  * Originally Released Under LGPL - original licence link has changed is not relivant.
50294  *
50295  * Fork - LGPL
50296  * <script type="text/javascript">
50297  */
50298 /**
50299  * @extends Roo.grid.AbstractSelectionModel
50300  * @class Roo.grid.RowSelectionModel
50301  * The default SelectionModel used by {@link Roo.grid.Grid}.
50302  * It supports multiple selections and keyboard selection/navigation. 
50303  * @constructor
50304  * @param {Object} config
50305  */
50306 Roo.grid.RowSelectionModel = function(config){
50307     Roo.apply(this, config);
50308     this.selections = new Roo.util.MixedCollection(false, function(o){
50309         return o.id;
50310     });
50311
50312     this.last = false;
50313     this.lastActive = false;
50314
50315     this.addEvents({
50316         /**
50317              * @event selectionchange
50318              * Fires when the selection changes
50319              * @param {SelectionModel} this
50320              */
50321             "selectionchange" : true,
50322         /**
50323              * @event afterselectionchange
50324              * Fires after the selection changes (eg. by key press or clicking)
50325              * @param {SelectionModel} this
50326              */
50327             "afterselectionchange" : true,
50328         /**
50329              * @event beforerowselect
50330              * Fires when a row is selected being selected, return false to cancel.
50331              * @param {SelectionModel} this
50332              * @param {Number} rowIndex The selected index
50333              * @param {Boolean} keepExisting False if other selections will be cleared
50334              */
50335             "beforerowselect" : true,
50336         /**
50337              * @event rowselect
50338              * Fires when a row is selected.
50339              * @param {SelectionModel} this
50340              * @param {Number} rowIndex The selected index
50341              * @param {Roo.data.Record} r The record
50342              */
50343             "rowselect" : true,
50344         /**
50345              * @event rowdeselect
50346              * Fires when a row is deselected.
50347              * @param {SelectionModel} this
50348              * @param {Number} rowIndex The selected index
50349              */
50350         "rowdeselect" : true
50351     });
50352     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50353     this.locked = false;
50354 };
50355
50356 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50357     /**
50358      * @cfg {Boolean} singleSelect
50359      * True to allow selection of only one row at a time (defaults to false)
50360      */
50361     singleSelect : false,
50362
50363     // private
50364     initEvents : function(){
50365
50366         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50367             this.grid.on("mousedown", this.handleMouseDown, this);
50368         }else{ // allow click to work like normal
50369             this.grid.on("rowclick", this.handleDragableRowClick, this);
50370         }
50371
50372         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50373             "up" : function(e){
50374                 if(!e.shiftKey){
50375                     this.selectPrevious(e.shiftKey);
50376                 }else if(this.last !== false && this.lastActive !== false){
50377                     var last = this.last;
50378                     this.selectRange(this.last,  this.lastActive-1);
50379                     this.grid.getView().focusRow(this.lastActive);
50380                     if(last !== false){
50381                         this.last = last;
50382                     }
50383                 }else{
50384                     this.selectFirstRow();
50385                 }
50386                 this.fireEvent("afterselectionchange", this);
50387             },
50388             "down" : function(e){
50389                 if(!e.shiftKey){
50390                     this.selectNext(e.shiftKey);
50391                 }else if(this.last !== false && this.lastActive !== false){
50392                     var last = this.last;
50393                     this.selectRange(this.last,  this.lastActive+1);
50394                     this.grid.getView().focusRow(this.lastActive);
50395                     if(last !== false){
50396                         this.last = last;
50397                     }
50398                 }else{
50399                     this.selectFirstRow();
50400                 }
50401                 this.fireEvent("afterselectionchange", this);
50402             },
50403             scope: this
50404         });
50405
50406         var view = this.grid.view;
50407         view.on("refresh", this.onRefresh, this);
50408         view.on("rowupdated", this.onRowUpdated, this);
50409         view.on("rowremoved", this.onRemove, this);
50410     },
50411
50412     // private
50413     onRefresh : function(){
50414         var ds = this.grid.dataSource, i, v = this.grid.view;
50415         var s = this.selections;
50416         s.each(function(r){
50417             if((i = ds.indexOfId(r.id)) != -1){
50418                 v.onRowSelect(i);
50419             }else{
50420                 s.remove(r);
50421             }
50422         });
50423     },
50424
50425     // private
50426     onRemove : function(v, index, r){
50427         this.selections.remove(r);
50428     },
50429
50430     // private
50431     onRowUpdated : function(v, index, r){
50432         if(this.isSelected(r)){
50433             v.onRowSelect(index);
50434         }
50435     },
50436
50437     /**
50438      * Select records.
50439      * @param {Array} records The records to select
50440      * @param {Boolean} keepExisting (optional) True to keep existing selections
50441      */
50442     selectRecords : function(records, keepExisting){
50443         if(!keepExisting){
50444             this.clearSelections();
50445         }
50446         var ds = this.grid.dataSource;
50447         for(var i = 0, len = records.length; i < len; i++){
50448             this.selectRow(ds.indexOf(records[i]), true);
50449         }
50450     },
50451
50452     /**
50453      * Gets the number of selected rows.
50454      * @return {Number}
50455      */
50456     getCount : function(){
50457         return this.selections.length;
50458     },
50459
50460     /**
50461      * Selects the first row in the grid.
50462      */
50463     selectFirstRow : function(){
50464         this.selectRow(0);
50465     },
50466
50467     /**
50468      * Select the last row.
50469      * @param {Boolean} keepExisting (optional) True to keep existing selections
50470      */
50471     selectLastRow : function(keepExisting){
50472         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50473     },
50474
50475     /**
50476      * Selects the row immediately following the last selected row.
50477      * @param {Boolean} keepExisting (optional) True to keep existing selections
50478      */
50479     selectNext : function(keepExisting){
50480         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50481             this.selectRow(this.last+1, keepExisting);
50482             this.grid.getView().focusRow(this.last);
50483         }
50484     },
50485
50486     /**
50487      * Selects the row that precedes the last selected row.
50488      * @param {Boolean} keepExisting (optional) True to keep existing selections
50489      */
50490     selectPrevious : function(keepExisting){
50491         if(this.last){
50492             this.selectRow(this.last-1, keepExisting);
50493             this.grid.getView().focusRow(this.last);
50494         }
50495     },
50496
50497     /**
50498      * Returns the selected records
50499      * @return {Array} Array of selected records
50500      */
50501     getSelections : function(){
50502         return [].concat(this.selections.items);
50503     },
50504
50505     /**
50506      * Returns the first selected record.
50507      * @return {Record}
50508      */
50509     getSelected : function(){
50510         return this.selections.itemAt(0);
50511     },
50512
50513
50514     /**
50515      * Clears all selections.
50516      */
50517     clearSelections : function(fast){
50518         if(this.locked) return;
50519         if(fast !== true){
50520             var ds = this.grid.dataSource;
50521             var s = this.selections;
50522             s.each(function(r){
50523                 this.deselectRow(ds.indexOfId(r.id));
50524             }, this);
50525             s.clear();
50526         }else{
50527             this.selections.clear();
50528         }
50529         this.last = false;
50530     },
50531
50532
50533     /**
50534      * Selects all rows.
50535      */
50536     selectAll : function(){
50537         if(this.locked) return;
50538         this.selections.clear();
50539         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50540             this.selectRow(i, true);
50541         }
50542     },
50543
50544     /**
50545      * Returns True if there is a selection.
50546      * @return {Boolean}
50547      */
50548     hasSelection : function(){
50549         return this.selections.length > 0;
50550     },
50551
50552     /**
50553      * Returns True if the specified row is selected.
50554      * @param {Number/Record} record The record or index of the record to check
50555      * @return {Boolean}
50556      */
50557     isSelected : function(index){
50558         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50559         return (r && this.selections.key(r.id) ? true : false);
50560     },
50561
50562     /**
50563      * Returns True if the specified record id is selected.
50564      * @param {String} id The id of record to check
50565      * @return {Boolean}
50566      */
50567     isIdSelected : function(id){
50568         return (this.selections.key(id) ? true : false);
50569     },
50570
50571     // private
50572     handleMouseDown : function(e, t){
50573         var view = this.grid.getView(), rowIndex;
50574         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50575             return;
50576         };
50577         if(e.shiftKey && this.last !== false){
50578             var last = this.last;
50579             this.selectRange(last, rowIndex, e.ctrlKey);
50580             this.last = last; // reset the last
50581             view.focusRow(rowIndex);
50582         }else{
50583             var isSelected = this.isSelected(rowIndex);
50584             if(e.button !== 0 && isSelected){
50585                 view.focusRow(rowIndex);
50586             }else if(e.ctrlKey && isSelected){
50587                 this.deselectRow(rowIndex);
50588             }else if(!isSelected){
50589                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50590                 view.focusRow(rowIndex);
50591             }
50592         }
50593         this.fireEvent("afterselectionchange", this);
50594     },
50595     // private
50596     handleDragableRowClick :  function(grid, rowIndex, e) 
50597     {
50598         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50599             this.selectRow(rowIndex, false);
50600             grid.view.focusRow(rowIndex);
50601              this.fireEvent("afterselectionchange", this);
50602         }
50603     },
50604     
50605     /**
50606      * Selects multiple rows.
50607      * @param {Array} rows Array of the indexes of the row to select
50608      * @param {Boolean} keepExisting (optional) True to keep existing selections
50609      */
50610     selectRows : function(rows, keepExisting){
50611         if(!keepExisting){
50612             this.clearSelections();
50613         }
50614         for(var i = 0, len = rows.length; i < len; i++){
50615             this.selectRow(rows[i], true);
50616         }
50617     },
50618
50619     /**
50620      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50621      * @param {Number} startRow The index of the first row in the range
50622      * @param {Number} endRow The index of the last row in the range
50623      * @param {Boolean} keepExisting (optional) True to retain existing selections
50624      */
50625     selectRange : function(startRow, endRow, keepExisting){
50626         if(this.locked) return;
50627         if(!keepExisting){
50628             this.clearSelections();
50629         }
50630         if(startRow <= endRow){
50631             for(var i = startRow; i <= endRow; i++){
50632                 this.selectRow(i, true);
50633             }
50634         }else{
50635             for(var i = startRow; i >= endRow; i--){
50636                 this.selectRow(i, true);
50637             }
50638         }
50639     },
50640
50641     /**
50642      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50643      * @param {Number} startRow The index of the first row in the range
50644      * @param {Number} endRow The index of the last row in the range
50645      */
50646     deselectRange : function(startRow, endRow, preventViewNotify){
50647         if(this.locked) return;
50648         for(var i = startRow; i <= endRow; i++){
50649             this.deselectRow(i, preventViewNotify);
50650         }
50651     },
50652
50653     /**
50654      * Selects a row.
50655      * @param {Number} row The index of the row to select
50656      * @param {Boolean} keepExisting (optional) True to keep existing selections
50657      */
50658     selectRow : function(index, keepExisting, preventViewNotify){
50659         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50660         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50661             if(!keepExisting || this.singleSelect){
50662                 this.clearSelections();
50663             }
50664             var r = this.grid.dataSource.getAt(index);
50665             this.selections.add(r);
50666             this.last = this.lastActive = index;
50667             if(!preventViewNotify){
50668                 this.grid.getView().onRowSelect(index);
50669             }
50670             this.fireEvent("rowselect", this, index, r);
50671             this.fireEvent("selectionchange", this);
50672         }
50673     },
50674
50675     /**
50676      * Deselects a row.
50677      * @param {Number} row The index of the row to deselect
50678      */
50679     deselectRow : function(index, preventViewNotify){
50680         if(this.locked) return;
50681         if(this.last == index){
50682             this.last = false;
50683         }
50684         if(this.lastActive == index){
50685             this.lastActive = false;
50686         }
50687         var r = this.grid.dataSource.getAt(index);
50688         this.selections.remove(r);
50689         if(!preventViewNotify){
50690             this.grid.getView().onRowDeselect(index);
50691         }
50692         this.fireEvent("rowdeselect", this, index);
50693         this.fireEvent("selectionchange", this);
50694     },
50695
50696     // private
50697     restoreLast : function(){
50698         if(this._last){
50699             this.last = this._last;
50700         }
50701     },
50702
50703     // private
50704     acceptsNav : function(row, col, cm){
50705         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50706     },
50707
50708     // private
50709     onEditorKey : function(field, e){
50710         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50711         if(k == e.TAB){
50712             e.stopEvent();
50713             ed.completeEdit();
50714             if(e.shiftKey){
50715                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50716             }else{
50717                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50718             }
50719         }else if(k == e.ENTER && !e.ctrlKey){
50720             e.stopEvent();
50721             ed.completeEdit();
50722             if(e.shiftKey){
50723                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50724             }else{
50725                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50726             }
50727         }else if(k == e.ESC){
50728             ed.cancelEdit();
50729         }
50730         if(newCell){
50731             g.startEditing(newCell[0], newCell[1]);
50732         }
50733     }
50734 });/*
50735  * Based on:
50736  * Ext JS Library 1.1.1
50737  * Copyright(c) 2006-2007, Ext JS, LLC.
50738  *
50739  * Originally Released Under LGPL - original licence link has changed is not relivant.
50740  *
50741  * Fork - LGPL
50742  * <script type="text/javascript">
50743  */
50744 /**
50745  * @class Roo.grid.CellSelectionModel
50746  * @extends Roo.grid.AbstractSelectionModel
50747  * This class provides the basic implementation for cell selection in a grid.
50748  * @constructor
50749  * @param {Object} config The object containing the configuration of this model.
50750  */
50751 Roo.grid.CellSelectionModel = function(config){
50752     Roo.apply(this, config);
50753
50754     this.selection = null;
50755
50756     this.addEvents({
50757         /**
50758              * @event beforerowselect
50759              * Fires before a cell is selected.
50760              * @param {SelectionModel} this
50761              * @param {Number} rowIndex The selected row index
50762              * @param {Number} colIndex The selected cell index
50763              */
50764             "beforecellselect" : true,
50765         /**
50766              * @event cellselect
50767              * Fires when a cell is selected.
50768              * @param {SelectionModel} this
50769              * @param {Number} rowIndex The selected row index
50770              * @param {Number} colIndex The selected cell index
50771              */
50772             "cellselect" : true,
50773         /**
50774              * @event selectionchange
50775              * Fires when the active selection changes.
50776              * @param {SelectionModel} this
50777              * @param {Object} selection null for no selection or an object (o) with two properties
50778                 <ul>
50779                 <li>o.record: the record object for the row the selection is in</li>
50780                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50781                 </ul>
50782              */
50783             "selectionchange" : true
50784     });
50785     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50786 };
50787
50788 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50789
50790     /** @ignore */
50791     initEvents : function(){
50792         this.grid.on("mousedown", this.handleMouseDown, this);
50793         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50794         var view = this.grid.view;
50795         view.on("refresh", this.onViewChange, this);
50796         view.on("rowupdated", this.onRowUpdated, this);
50797         view.on("beforerowremoved", this.clearSelections, this);
50798         view.on("beforerowsinserted", this.clearSelections, this);
50799         if(this.grid.isEditor){
50800             this.grid.on("beforeedit", this.beforeEdit,  this);
50801         }
50802     },
50803
50804         //private
50805     beforeEdit : function(e){
50806         this.select(e.row, e.column, false, true, e.record);
50807     },
50808
50809         //private
50810     onRowUpdated : function(v, index, r){
50811         if(this.selection && this.selection.record == r){
50812             v.onCellSelect(index, this.selection.cell[1]);
50813         }
50814     },
50815
50816         //private
50817     onViewChange : function(){
50818         this.clearSelections(true);
50819     },
50820
50821         /**
50822          * Returns the currently selected cell,.
50823          * @return {Array} The selected cell (row, column) or null if none selected.
50824          */
50825     getSelectedCell : function(){
50826         return this.selection ? this.selection.cell : null;
50827     },
50828
50829     /**
50830      * Clears all selections.
50831      * @param {Boolean} true to prevent the gridview from being notified about the change.
50832      */
50833     clearSelections : function(preventNotify){
50834         var s = this.selection;
50835         if(s){
50836             if(preventNotify !== true){
50837                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50838             }
50839             this.selection = null;
50840             this.fireEvent("selectionchange", this, null);
50841         }
50842     },
50843
50844     /**
50845      * Returns true if there is a selection.
50846      * @return {Boolean}
50847      */
50848     hasSelection : function(){
50849         return this.selection ? true : false;
50850     },
50851
50852     /** @ignore */
50853     handleMouseDown : function(e, t){
50854         var v = this.grid.getView();
50855         if(this.isLocked()){
50856             return;
50857         };
50858         var row = v.findRowIndex(t);
50859         var cell = v.findCellIndex(t);
50860         if(row !== false && cell !== false){
50861             this.select(row, cell);
50862         }
50863     },
50864
50865     /**
50866      * Selects a cell.
50867      * @param {Number} rowIndex
50868      * @param {Number} collIndex
50869      */
50870     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50871         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50872             this.clearSelections();
50873             r = r || this.grid.dataSource.getAt(rowIndex);
50874             this.selection = {
50875                 record : r,
50876                 cell : [rowIndex, colIndex]
50877             };
50878             if(!preventViewNotify){
50879                 var v = this.grid.getView();
50880                 v.onCellSelect(rowIndex, colIndex);
50881                 if(preventFocus !== true){
50882                     v.focusCell(rowIndex, colIndex);
50883                 }
50884             }
50885             this.fireEvent("cellselect", this, rowIndex, colIndex);
50886             this.fireEvent("selectionchange", this, this.selection);
50887         }
50888     },
50889
50890         //private
50891     isSelectable : function(rowIndex, colIndex, cm){
50892         return !cm.isHidden(colIndex);
50893     },
50894
50895     /** @ignore */
50896     handleKeyDown : function(e){
50897         Roo.log('Cell Sel Model handleKeyDown');
50898         if(!e.isNavKeyPress()){
50899             return;
50900         }
50901         var g = this.grid, s = this.selection;
50902         if(!s){
50903             e.stopEvent();
50904             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50905             if(cell){
50906                 this.select(cell[0], cell[1]);
50907             }
50908             return;
50909         }
50910         var sm = this;
50911         var walk = function(row, col, step){
50912             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50913         };
50914         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50915         var newCell;
50916
50917         switch(k){
50918             case e.TAB:
50919                 // handled by onEditorKey
50920                 if (g.isEditor && g.editing) {
50921                     return;
50922                 }
50923                 if(e.shiftKey){
50924                      newCell = walk(r, c-1, -1);
50925                 }else{
50926                      newCell = walk(r, c+1, 1);
50927                 }
50928              break;
50929              case e.DOWN:
50930                  newCell = walk(r+1, c, 1);
50931              break;
50932              case e.UP:
50933                  newCell = walk(r-1, c, -1);
50934              break;
50935              case e.RIGHT:
50936                  newCell = walk(r, c+1, 1);
50937              break;
50938              case e.LEFT:
50939                  newCell = walk(r, c-1, -1);
50940              break;
50941              case e.ENTER:
50942                  if(g.isEditor && !g.editing){
50943                     g.startEditing(r, c);
50944                     e.stopEvent();
50945                     return;
50946                 }
50947              break;
50948         };
50949         if(newCell){
50950             this.select(newCell[0], newCell[1]);
50951             e.stopEvent();
50952         }
50953     },
50954
50955     acceptsNav : function(row, col, cm){
50956         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50957     },
50958
50959     onEditorKey : function(field, e){
50960         
50961         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50962         ///Roo.log('onEditorKey' + k);
50963         
50964         if(k == e.TAB){
50965             if(e.shiftKey){
50966                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50967             }else{
50968                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50969             }
50970             e.stopEvent();
50971         }else if(k == e.ENTER && !e.ctrlKey){
50972             ed.completeEdit();
50973             e.stopEvent();
50974             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50975         }else if(k == e.ESC){
50976             ed.cancelEdit();
50977         }
50978         
50979         
50980         if(newCell){
50981             //Roo.log('next cell after edit');
50982             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50983         }
50984     }
50985 });/*
50986  * Based on:
50987  * Ext JS Library 1.1.1
50988  * Copyright(c) 2006-2007, Ext JS, LLC.
50989  *
50990  * Originally Released Under LGPL - original licence link has changed is not relivant.
50991  *
50992  * Fork - LGPL
50993  * <script type="text/javascript">
50994  */
50995  
50996 /**
50997  * @class Roo.grid.EditorGrid
50998  * @extends Roo.grid.Grid
50999  * Class for creating and editable grid.
51000  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51001  * The container MUST have some type of size defined for the grid to fill. The container will be 
51002  * automatically set to position relative if it isn't already.
51003  * @param {Object} dataSource The data model to bind to
51004  * @param {Object} colModel The column model with info about this grid's columns
51005  */
51006 Roo.grid.EditorGrid = function(container, config){
51007     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51008     this.getGridEl().addClass("xedit-grid");
51009
51010     if(!this.selModel){
51011         this.selModel = new Roo.grid.CellSelectionModel();
51012     }
51013
51014     this.activeEditor = null;
51015
51016         this.addEvents({
51017             /**
51018              * @event beforeedit
51019              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51020              * <ul style="padding:5px;padding-left:16px;">
51021              * <li>grid - This grid</li>
51022              * <li>record - The record being edited</li>
51023              * <li>field - The field name being edited</li>
51024              * <li>value - The value for the field being edited.</li>
51025              * <li>row - The grid row index</li>
51026              * <li>column - The grid column index</li>
51027              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51028              * </ul>
51029              * @param {Object} e An edit event (see above for description)
51030              */
51031             "beforeedit" : true,
51032             /**
51033              * @event afteredit
51034              * Fires after a cell is edited. <br />
51035              * <ul style="padding:5px;padding-left:16px;">
51036              * <li>grid - This grid</li>
51037              * <li>record - The record being edited</li>
51038              * <li>field - The field name being edited</li>
51039              * <li>value - The value being set</li>
51040              * <li>originalValue - The original value for the field, before the edit.</li>
51041              * <li>row - The grid row index</li>
51042              * <li>column - The grid column index</li>
51043              * </ul>
51044              * @param {Object} e An edit event (see above for description)
51045              */
51046             "afteredit" : true,
51047             /**
51048              * @event validateedit
51049              * Fires after a cell is edited, but before the value is set in the record. 
51050          * You can use this to modify the value being set in the field, Return false
51051              * to cancel the change. The edit event object has the following properties <br />
51052              * <ul style="padding:5px;padding-left:16px;">
51053          * <li>editor - This editor</li>
51054              * <li>grid - This grid</li>
51055              * <li>record - The record being edited</li>
51056              * <li>field - The field name being edited</li>
51057              * <li>value - The value being set</li>
51058              * <li>originalValue - The original value for the field, before the edit.</li>
51059              * <li>row - The grid row index</li>
51060              * <li>column - The grid column index</li>
51061              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51062              * </ul>
51063              * @param {Object} e An edit event (see above for description)
51064              */
51065             "validateedit" : true
51066         });
51067     this.on("bodyscroll", this.stopEditing,  this);
51068     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51069 };
51070
51071 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51072     /**
51073      * @cfg {Number} clicksToEdit
51074      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51075      */
51076     clicksToEdit: 2,
51077
51078     // private
51079     isEditor : true,
51080     // private
51081     trackMouseOver: false, // causes very odd FF errors
51082
51083     onCellDblClick : function(g, row, col){
51084         this.startEditing(row, col);
51085     },
51086
51087     onEditComplete : function(ed, value, startValue){
51088         this.editing = false;
51089         this.activeEditor = null;
51090         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51091         var r = ed.record;
51092         var field = this.colModel.getDataIndex(ed.col);
51093         var e = {
51094             grid: this,
51095             record: r,
51096             field: field,
51097             originalValue: startValue,
51098             value: value,
51099             row: ed.row,
51100             column: ed.col,
51101             cancel:false,
51102             editor: ed
51103         };
51104         if(String(value) !== String(startValue)){
51105             
51106             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51107                 r.set(field, e.value);
51108                 // if we are dealing with a combo box..
51109                 // then we also set the 'name' colum to be the displayField
51110                 if (ed.field.displayField && ed.field.name) {
51111                     r.set(ed.field.name, ed.field.el.dom.value);
51112                 }
51113                 
51114                 delete e.cancel; //?? why!!!
51115                 this.fireEvent("afteredit", e);
51116             }
51117         } else {
51118             this.fireEvent("afteredit", e); // always fire it!
51119         }
51120         this.view.focusCell(ed.row, ed.col);
51121     },
51122
51123     /**
51124      * Starts editing the specified for the specified row/column
51125      * @param {Number} rowIndex
51126      * @param {Number} colIndex
51127      */
51128     startEditing : function(row, col){
51129         this.stopEditing();
51130         if(this.colModel.isCellEditable(col, row)){
51131             this.view.ensureVisible(row, col, true);
51132             var r = this.dataSource.getAt(row);
51133             var field = this.colModel.getDataIndex(col);
51134             var e = {
51135                 grid: this,
51136                 record: r,
51137                 field: field,
51138                 value: r.data[field],
51139                 row: row,
51140                 column: col,
51141                 cancel:false
51142             };
51143             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51144                 this.editing = true;
51145                 var ed = this.colModel.getCellEditor(col, row);
51146                 
51147                 if (!ed) {
51148                     return;
51149                 }
51150                 if(!ed.rendered){
51151                     ed.render(ed.parentEl || document.body);
51152                 }
51153                 ed.field.reset();
51154                 (function(){ // complex but required for focus issues in safari, ie and opera
51155                     ed.row = row;
51156                     ed.col = col;
51157                     ed.record = r;
51158                     ed.on("complete", this.onEditComplete, this, {single: true});
51159                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
51160                     this.activeEditor = ed;
51161                     var v = r.data[field];
51162                     ed.startEdit(this.view.getCell(row, col), v);
51163                     // combo's with 'displayField and name set
51164                     if (ed.field.displayField && ed.field.name) {
51165                         ed.field.el.dom.value = r.data[ed.field.name];
51166                     }
51167                     
51168                     
51169                 }).defer(50, this);
51170             }
51171         }
51172     },
51173         
51174     /**
51175      * Stops any active editing
51176      */
51177     stopEditing : function(){
51178         if(this.activeEditor){
51179             this.activeEditor.completeEdit();
51180         }
51181         this.activeEditor = null;
51182     }
51183 });/*
51184  * Based on:
51185  * Ext JS Library 1.1.1
51186  * Copyright(c) 2006-2007, Ext JS, LLC.
51187  *
51188  * Originally Released Under LGPL - original licence link has changed is not relivant.
51189  *
51190  * Fork - LGPL
51191  * <script type="text/javascript">
51192  */
51193
51194 // private - not really -- you end up using it !
51195 // This is a support class used internally by the Grid components
51196
51197 /**
51198  * @class Roo.grid.GridEditor
51199  * @extends Roo.Editor
51200  * Class for creating and editable grid elements.
51201  * @param {Object} config any settings (must include field)
51202  */
51203 Roo.grid.GridEditor = function(field, config){
51204     if (!config && field.field) {
51205         config = field;
51206         field = Roo.factory(config.field, Roo.form);
51207     }
51208     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51209     field.monitorTab = false;
51210 };
51211
51212 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51213     
51214     /**
51215      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51216      */
51217     
51218     alignment: "tl-tl",
51219     autoSize: "width",
51220     hideEl : false,
51221     cls: "x-small-editor x-grid-editor",
51222     shim:false,
51223     shadow:"frame"
51224 });/*
51225  * Based on:
51226  * Ext JS Library 1.1.1
51227  * Copyright(c) 2006-2007, Ext JS, LLC.
51228  *
51229  * Originally Released Under LGPL - original licence link has changed is not relivant.
51230  *
51231  * Fork - LGPL
51232  * <script type="text/javascript">
51233  */
51234   
51235
51236   
51237 Roo.grid.PropertyRecord = Roo.data.Record.create([
51238     {name:'name',type:'string'},  'value'
51239 ]);
51240
51241
51242 Roo.grid.PropertyStore = function(grid, source){
51243     this.grid = grid;
51244     this.store = new Roo.data.Store({
51245         recordType : Roo.grid.PropertyRecord
51246     });
51247     this.store.on('update', this.onUpdate,  this);
51248     if(source){
51249         this.setSource(source);
51250     }
51251     Roo.grid.PropertyStore.superclass.constructor.call(this);
51252 };
51253
51254
51255
51256 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51257     setSource : function(o){
51258         this.source = o;
51259         this.store.removeAll();
51260         var data = [];
51261         for(var k in o){
51262             if(this.isEditableValue(o[k])){
51263                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51264             }
51265         }
51266         this.store.loadRecords({records: data}, {}, true);
51267     },
51268
51269     onUpdate : function(ds, record, type){
51270         if(type == Roo.data.Record.EDIT){
51271             var v = record.data['value'];
51272             var oldValue = record.modified['value'];
51273             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51274                 this.source[record.id] = v;
51275                 record.commit();
51276                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51277             }else{
51278                 record.reject();
51279             }
51280         }
51281     },
51282
51283     getProperty : function(row){
51284        return this.store.getAt(row);
51285     },
51286
51287     isEditableValue: function(val){
51288         if(val && val instanceof Date){
51289             return true;
51290         }else if(typeof val == 'object' || typeof val == 'function'){
51291             return false;
51292         }
51293         return true;
51294     },
51295
51296     setValue : function(prop, value){
51297         this.source[prop] = value;
51298         this.store.getById(prop).set('value', value);
51299     },
51300
51301     getSource : function(){
51302         return this.source;
51303     }
51304 });
51305
51306 Roo.grid.PropertyColumnModel = function(grid, store){
51307     this.grid = grid;
51308     var g = Roo.grid;
51309     g.PropertyColumnModel.superclass.constructor.call(this, [
51310         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51311         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51312     ]);
51313     this.store = store;
51314     this.bselect = Roo.DomHelper.append(document.body, {
51315         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51316             {tag: 'option', value: 'true', html: 'true'},
51317             {tag: 'option', value: 'false', html: 'false'}
51318         ]
51319     });
51320     Roo.id(this.bselect);
51321     var f = Roo.form;
51322     this.editors = {
51323         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51324         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51325         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51326         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51327         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51328     };
51329     this.renderCellDelegate = this.renderCell.createDelegate(this);
51330     this.renderPropDelegate = this.renderProp.createDelegate(this);
51331 };
51332
51333 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51334     
51335     
51336     nameText : 'Name',
51337     valueText : 'Value',
51338     
51339     dateFormat : 'm/j/Y',
51340     
51341     
51342     renderDate : function(dateVal){
51343         return dateVal.dateFormat(this.dateFormat);
51344     },
51345
51346     renderBool : function(bVal){
51347         return bVal ? 'true' : 'false';
51348     },
51349
51350     isCellEditable : function(colIndex, rowIndex){
51351         return colIndex == 1;
51352     },
51353
51354     getRenderer : function(col){
51355         return col == 1 ?
51356             this.renderCellDelegate : this.renderPropDelegate;
51357     },
51358
51359     renderProp : function(v){
51360         return this.getPropertyName(v);
51361     },
51362
51363     renderCell : function(val){
51364         var rv = val;
51365         if(val instanceof Date){
51366             rv = this.renderDate(val);
51367         }else if(typeof val == 'boolean'){
51368             rv = this.renderBool(val);
51369         }
51370         return Roo.util.Format.htmlEncode(rv);
51371     },
51372
51373     getPropertyName : function(name){
51374         var pn = this.grid.propertyNames;
51375         return pn && pn[name] ? pn[name] : name;
51376     },
51377
51378     getCellEditor : function(colIndex, rowIndex){
51379         var p = this.store.getProperty(rowIndex);
51380         var n = p.data['name'], val = p.data['value'];
51381         
51382         if(typeof(this.grid.customEditors[n]) == 'string'){
51383             return this.editors[this.grid.customEditors[n]];
51384         }
51385         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51386             return this.grid.customEditors[n];
51387         }
51388         if(val instanceof Date){
51389             return this.editors['date'];
51390         }else if(typeof val == 'number'){
51391             return this.editors['number'];
51392         }else if(typeof val == 'boolean'){
51393             return this.editors['boolean'];
51394         }else{
51395             return this.editors['string'];
51396         }
51397     }
51398 });
51399
51400 /**
51401  * @class Roo.grid.PropertyGrid
51402  * @extends Roo.grid.EditorGrid
51403  * This class represents the  interface of a component based property grid control.
51404  * <br><br>Usage:<pre><code>
51405  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51406       
51407  });
51408  // set any options
51409  grid.render();
51410  * </code></pre>
51411   
51412  * @constructor
51413  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51414  * The container MUST have some type of size defined for the grid to fill. The container will be
51415  * automatically set to position relative if it isn't already.
51416  * @param {Object} config A config object that sets properties on this grid.
51417  */
51418 Roo.grid.PropertyGrid = function(container, config){
51419     config = config || {};
51420     var store = new Roo.grid.PropertyStore(this);
51421     this.store = store;
51422     var cm = new Roo.grid.PropertyColumnModel(this, store);
51423     store.store.sort('name', 'ASC');
51424     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51425         ds: store.store,
51426         cm: cm,
51427         enableColLock:false,
51428         enableColumnMove:false,
51429         stripeRows:false,
51430         trackMouseOver: false,
51431         clicksToEdit:1
51432     }, config));
51433     this.getGridEl().addClass('x-props-grid');
51434     this.lastEditRow = null;
51435     this.on('columnresize', this.onColumnResize, this);
51436     this.addEvents({
51437          /**
51438              * @event beforepropertychange
51439              * Fires before a property changes (return false to stop?)
51440              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51441              * @param {String} id Record Id
51442              * @param {String} newval New Value
51443          * @param {String} oldval Old Value
51444              */
51445         "beforepropertychange": true,
51446         /**
51447              * @event propertychange
51448              * Fires after a property changes
51449              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51450              * @param {String} id Record Id
51451              * @param {String} newval New Value
51452          * @param {String} oldval Old Value
51453              */
51454         "propertychange": true
51455     });
51456     this.customEditors = this.customEditors || {};
51457 };
51458 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51459     
51460      /**
51461      * @cfg {Object} customEditors map of colnames=> custom editors.
51462      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51463      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51464      * false disables editing of the field.
51465          */
51466     
51467       /**
51468      * @cfg {Object} propertyNames map of property Names to their displayed value
51469          */
51470     
51471     render : function(){
51472         Roo.grid.PropertyGrid.superclass.render.call(this);
51473         this.autoSize.defer(100, this);
51474     },
51475
51476     autoSize : function(){
51477         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51478         if(this.view){
51479             this.view.fitColumns();
51480         }
51481     },
51482
51483     onColumnResize : function(){
51484         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51485         this.autoSize();
51486     },
51487     /**
51488      * Sets the data for the Grid
51489      * accepts a Key => Value object of all the elements avaiable.
51490      * @param {Object} data  to appear in grid.
51491      */
51492     setSource : function(source){
51493         this.store.setSource(source);
51494         //this.autoSize();
51495     },
51496     /**
51497      * Gets all the data from the grid.
51498      * @return {Object} data  data stored in grid
51499      */
51500     getSource : function(){
51501         return this.store.getSource();
51502     }
51503 });/*
51504  * Based on:
51505  * Ext JS Library 1.1.1
51506  * Copyright(c) 2006-2007, Ext JS, LLC.
51507  *
51508  * Originally Released Under LGPL - original licence link has changed is not relivant.
51509  *
51510  * Fork - LGPL
51511  * <script type="text/javascript">
51512  */
51513  
51514 /**
51515  * @class Roo.LoadMask
51516  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51517  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51518  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51519  * element's UpdateManager load indicator and will be destroyed after the initial load.
51520  * @constructor
51521  * Create a new LoadMask
51522  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51523  * @param {Object} config The config object
51524  */
51525 Roo.LoadMask = function(el, config){
51526     this.el = Roo.get(el);
51527     Roo.apply(this, config);
51528     if(this.store){
51529         this.store.on('beforeload', this.onBeforeLoad, this);
51530         this.store.on('load', this.onLoad, this);
51531         this.store.on('loadexception', this.onLoad, this);
51532         this.removeMask = false;
51533     }else{
51534         var um = this.el.getUpdateManager();
51535         um.showLoadIndicator = false; // disable the default indicator
51536         um.on('beforeupdate', this.onBeforeLoad, this);
51537         um.on('update', this.onLoad, this);
51538         um.on('failure', this.onLoad, this);
51539         this.removeMask = true;
51540     }
51541 };
51542
51543 Roo.LoadMask.prototype = {
51544     /**
51545      * @cfg {Boolean} removeMask
51546      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51547      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51548      */
51549     /**
51550      * @cfg {String} msg
51551      * The text to display in a centered loading message box (defaults to 'Loading...')
51552      */
51553     msg : 'Loading...',
51554     /**
51555      * @cfg {String} msgCls
51556      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51557      */
51558     msgCls : 'x-mask-loading',
51559
51560     /**
51561      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51562      * @type Boolean
51563      */
51564     disabled: false,
51565
51566     /**
51567      * Disables the mask to prevent it from being displayed
51568      */
51569     disable : function(){
51570        this.disabled = true;
51571     },
51572
51573     /**
51574      * Enables the mask so that it can be displayed
51575      */
51576     enable : function(){
51577         this.disabled = false;
51578     },
51579
51580     // private
51581     onLoad : function(){
51582         this.el.unmask(this.removeMask);
51583     },
51584
51585     // private
51586     onBeforeLoad : function(){
51587         if(!this.disabled){
51588             this.el.mask(this.msg, this.msgCls);
51589         }
51590     },
51591
51592     // private
51593     destroy : function(){
51594         if(this.store){
51595             this.store.un('beforeload', this.onBeforeLoad, this);
51596             this.store.un('load', this.onLoad, this);
51597             this.store.un('loadexception', this.onLoad, this);
51598         }else{
51599             var um = this.el.getUpdateManager();
51600             um.un('beforeupdate', this.onBeforeLoad, this);
51601             um.un('update', this.onLoad, this);
51602             um.un('failure', this.onLoad, this);
51603         }
51604     }
51605 };/*
51606  * Based on:
51607  * Ext JS Library 1.1.1
51608  * Copyright(c) 2006-2007, Ext JS, LLC.
51609  *
51610  * Originally Released Under LGPL - original licence link has changed is not relivant.
51611  *
51612  * Fork - LGPL
51613  * <script type="text/javascript">
51614  */
51615 Roo.XTemplate = function(){
51616     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51617     var s = this.html;
51618
51619     s = ['<tpl>', s, '</tpl>'].join('');
51620
51621     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51622
51623     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51624     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51625     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51626     var m, id = 0;
51627     var tpls = [];
51628
51629     while(m = s.match(re)){
51630        var m2 = m[0].match(nameRe);
51631        var m3 = m[0].match(ifRe);
51632        var m4 = m[0].match(execRe);
51633        var exp = null, fn = null, exec = null;
51634        var name = m2 && m2[1] ? m2[1] : '';
51635        if(m3){
51636            exp = m3 && m3[1] ? m3[1] : null;
51637            if(exp){
51638                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51639            }
51640        }
51641        if(m4){
51642            exp = m4 && m4[1] ? m4[1] : null;
51643            if(exp){
51644                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51645            }
51646        }
51647        if(name){
51648            switch(name){
51649                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51650                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51651                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51652            }
51653        }
51654        tpls.push({
51655             id: id,
51656             target: name,
51657             exec: exec,
51658             test: fn,
51659             body: m[1]||''
51660         });
51661        s = s.replace(m[0], '{xtpl'+ id + '}');
51662        ++id;
51663     }
51664     for(var i = tpls.length-1; i >= 0; --i){
51665         this.compileTpl(tpls[i]);
51666     }
51667     this.master = tpls[tpls.length-1];
51668     this.tpls = tpls;
51669 };
51670 Roo.extend(Roo.XTemplate, Roo.Template, {
51671
51672     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51673
51674     applySubTemplate : function(id, values, parent){
51675         var t = this.tpls[id];
51676         if(t.test && !t.test.call(this, values, parent)){
51677             return '';
51678         }
51679         if(t.exec && t.exec.call(this, values, parent)){
51680             return '';
51681         }
51682         var vs = t.target ? t.target.call(this, values, parent) : values;
51683         parent = t.target ? values : parent;
51684         if(t.target && vs instanceof Array){
51685             var buf = [];
51686             for(var i = 0, len = vs.length; i < len; i++){
51687                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51688             }
51689             return buf.join('');
51690         }
51691         return t.compiled.call(this, vs, parent);
51692     },
51693
51694     compileTpl : function(tpl){
51695         var fm = Roo.util.Format;
51696         var useF = this.disableFormats !== true;
51697         var sep = Roo.isGecko ? "+" : ",";
51698         var fn = function(m, name, format, args){
51699             if(name.substr(0, 4) == 'xtpl'){
51700                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51701             }
51702             var v;
51703             if(name.indexOf('.') != -1){
51704                 v = name;
51705             }else{
51706                 v = "values['" + name + "']";
51707             }
51708             if(format && useF){
51709                 args = args ? ',' + args : "";
51710                 if(format.substr(0, 5) != "this."){
51711                     format = "fm." + format + '(';
51712                 }else{
51713                     format = 'this.call("'+ format.substr(5) + '", ';
51714                     args = ", values";
51715                 }
51716             }else{
51717                 args= ''; format = "("+v+" === undefined ? '' : ";
51718             }
51719             return "'"+ sep + format + v + args + ")"+sep+"'";
51720         };
51721         var body;
51722         // branched to use + in gecko and [].join() in others
51723         if(Roo.isGecko){
51724             body = "tpl.compiled = function(values, parent){ return '" +
51725                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51726                     "';};";
51727         }else{
51728             body = ["tpl.compiled = function(values, parent){ return ['"];
51729             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51730             body.push("'].join('');};");
51731             body = body.join('');
51732         }
51733         /** eval:var:zzzzzzz */
51734         eval(body);
51735         return this;
51736     },
51737
51738     applyTemplate : function(values){
51739         return this.master.compiled.call(this, values, {});
51740         var s = this.subs;
51741     },
51742
51743     apply : function(){
51744         return this.applyTemplate.apply(this, arguments);
51745     },
51746
51747     compile : function(){return this;}
51748 });
51749
51750 Roo.XTemplate.from = function(el){
51751     el = Roo.getDom(el);
51752     return new Roo.XTemplate(el.value || el.innerHTML);
51753 };/*
51754  * Original code for Roojs - LGPL
51755  * <script type="text/javascript">
51756  */
51757  
51758 /**
51759  * @class Roo.XComponent
51760  * A delayed Element creator...
51761  * Or a way to group chunks of interface together.
51762  * 
51763  * Mypart.xyx = new Roo.XComponent({
51764
51765     parent : 'Mypart.xyz', // empty == document.element.!!
51766     order : '001',
51767     name : 'xxxx'
51768     region : 'xxxx'
51769     disabled : function() {} 
51770      
51771     tree : function() { // return an tree of xtype declared components
51772         var MODULE = this;
51773         return 
51774         {
51775             xtype : 'NestedLayoutPanel',
51776             // technicall
51777         }
51778      ]
51779  *})
51780  *
51781  *
51782  * It can be used to build a big heiracy, with parent etc.
51783  * or you can just use this to render a single compoent to a dom element
51784  * MYPART.render(Roo.Element | String(id) | dom_element )
51785  * 
51786  * @extends Roo.util.Observable
51787  * @constructor
51788  * @param cfg {Object} configuration of component
51789  * 
51790  */
51791 Roo.XComponent = function(cfg) {
51792     Roo.apply(this, cfg);
51793     this.addEvents({ 
51794         /**
51795              * @event built
51796              * Fires when this the componnt is built
51797              * @param {Roo.XComponent} c the component
51798              */
51799         'built' : true,
51800         /**
51801              * @event buildcomplete
51802              * Fires on the top level element when all elements have been built
51803              * @param {Roo.XComponent} c the top level component.
51804          */
51805         'buildcomplete' : true
51806         
51807     });
51808     this.region = this.region || 'center'; // default..
51809     Roo.XComponent.register(this);
51810     this.modules = false;
51811     this.el = false; // where the layout goes..
51812     
51813     
51814 }
51815 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51816     /**
51817      * @property el
51818      * The created element (with Roo.factory())
51819      * @type {Roo.Layout}
51820      */
51821     el  : false,
51822     
51823     /**
51824      * @property el
51825      * for BC  - use el in new code
51826      * @type {Roo.Layout}
51827      */
51828     panel : false,
51829     
51830     /**
51831      * @property layout
51832      * for BC  - use el in new code
51833      * @type {Roo.Layout}
51834      */
51835     layout : false,
51836     
51837      /**
51838      * @cfg {Function|boolean} disabled
51839      * If this module is disabled by some rule, return true from the funtion
51840      */
51841     disabled : false,
51842     
51843     /**
51844      * @cfg {String} parent 
51845      * Name of parent element which it get xtype added to..
51846      */
51847     parent: false,
51848     
51849     /**
51850      * @cfg {String} order
51851      * Used to set the order in which elements are created (usefull for multiple tabs)
51852      */
51853     
51854     order : false,
51855     /**
51856      * @cfg {String} name
51857      * String to display while loading.
51858      */
51859     name : false,
51860     /**
51861      * @cfg {String} region
51862      * Region to render component to (defaults to center)
51863      */
51864     region : 'center',
51865     
51866     /**
51867      * @cfg {Array} items
51868      * A single item array - the first element is the root of the tree..
51869      * It's done this way to stay compatible with the Xtype system...
51870      */
51871     items : false,
51872     
51873     
51874      /**
51875      * render
51876      * render element to dom or tree
51877      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51878      */
51879     
51880     render : function(el)
51881     {
51882         
51883         el = el || false;
51884         var hp = this.parent ? 1 : 0;
51885         
51886         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
51887             // if parent is a '#.....' string, then let's use that..
51888             var ename = this.parent.substr(1)
51889             this.parent = false;
51890             el = Roo.get(ename);
51891             if (!el) {
51892                 Roo.log("Warning - element can not be found :#" + ename );
51893                 return;
51894             }
51895         }
51896         
51897         
51898         if (!this.parent) {
51899             
51900             el = el ? Roo.get(el) : false;
51901             
51902             // it's a top level one..
51903             this.parent =  {
51904                 el : new Roo.BorderLayout(el || document.body, {
51905                 
51906                      center: {
51907                          titlebar: false,
51908                          autoScroll:false,
51909                          closeOnTab: true,
51910                          tabPosition: 'top',
51911                           //resizeTabs: true,
51912                          alwaysShowTabs: el && hp? false :  true,
51913                          hideTabs: el || !hp ? true :  false,
51914                          minTabWidth: 140
51915                      }
51916                  })
51917             }
51918         }
51919         
51920         
51921             
51922         var tree = this.tree();
51923         tree.region = tree.region || this.region;
51924         this.el = this.parent.el.addxtype(tree);
51925         this.fireEvent('built', this);
51926         
51927         this.panel = this.el;
51928         this.layout = this.panel.layout;    
51929          
51930     }
51931     
51932 });
51933
51934 Roo.apply(Roo.XComponent, {
51935     
51936     /**
51937      * @property  buildCompleted
51938      * True when the builder has completed building the interface.
51939      * @type Boolean
51940      */
51941     buildCompleted : false,
51942      
51943     /**
51944      * @property  topModule
51945      * the upper most module - uses document.element as it's constructor.
51946      * @type Object
51947      */
51948      
51949     topModule  : false,
51950       
51951     /**
51952      * @property  modules
51953      * array of modules to be created by registration system.
51954      * @type {Array} of Roo.XComponent
51955      */
51956     
51957     modules : [],
51958     /**
51959      * @property  elmodules
51960      * array of modules to be created by which use #ID 
51961      * @type {Array} of Roo.XComponent
51962      */
51963      
51964     elmodules : [],
51965
51966     
51967     /**
51968      * Register components to be built later.
51969      *
51970      * This solves the following issues
51971      * - Building is not done on page load, but after an authentication process has occured.
51972      * - Interface elements are registered on page load
51973      * - Parent Interface elements may not be loaded before child, so this handles that..
51974      * 
51975      *
51976      * example:
51977      * 
51978      * MyApp.register({
51979           order : '000001',
51980           module : 'Pman.Tab.projectMgr',
51981           region : 'center',
51982           parent : 'Pman.layout',
51983           disabled : false,  // or use a function..
51984         })
51985      
51986      * * @param {Object} details about module
51987      */
51988     register : function(obj) {
51989         this.modules.push(obj);
51990          
51991     },
51992     /**
51993      * convert a string to an object..
51994      * eg. 'AAA.BBB' -> finds AAA.BBB
51995
51996      */
51997     
51998     toObject : function(str)
51999     {
52000         if (!str || typeof(str) == 'object') {
52001             return str;
52002         }
52003         if (str.substring(0,1) == '#') {
52004             return str;
52005         }
52006
52007         var ar = str.split('.');
52008         var rt, o;
52009         rt = ar.shift();
52010             /** eval:var:o */
52011         try {
52012             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52013         } catch (e) {
52014             throw "Module not found : " + str;
52015         }
52016         
52017         if (o === false) {
52018             throw "Module not found : " + str;
52019         }
52020         Roo.each(ar, function(e) {
52021             if (typeof(o[e]) == 'undefined') {
52022                 throw "Module not found : " + str;
52023             }
52024             o = o[e];
52025         });
52026         
52027         return o;
52028         
52029     },
52030     
52031     
52032     /**
52033      * move modules into their correct place in the tree..
52034      * 
52035      */
52036     preBuild : function ()
52037     {
52038         var _t = this;
52039         Roo.each(this.modules , function (obj)
52040         {
52041             var opar = obj.parent;
52042             try { 
52043                 obj.parent = this.toObject(opar);
52044             } catch(e) {
52045                 Roo.log(e.toString());
52046                 return;
52047             }
52048             
52049             if (!obj.parent) {
52050                 this.topModule = obj;
52051                 return;
52052             }
52053             if (typeof(obj.parent) == 'string') {
52054                 this.elmodules.push(obj);
52055                 return;
52056             }
52057             if (obj.parent.constructor != Roo.XComponent) {
52058                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52059             }
52060             if (!obj.parent.modules) {
52061                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52062                     function(o) { return o.order + '' }
52063                 );
52064             }
52065             
52066             obj.parent.modules.add(obj);
52067         }, this);
52068     },
52069     
52070      /**
52071      * make a list of modules to build.
52072      * @return {Array} list of modules. 
52073      */ 
52074     
52075     buildOrder : function()
52076     {
52077         var _this = this;
52078         var cmp = function(a,b) {   
52079             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52080         };
52081         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52082             throw "No top level modules to build";
52083         }
52084         
52085         // make a flat list in order of modules to build.
52086         var mods = this.topModule ? [ this.topModule ] : [];
52087         Roo.each(this.elmodules,function(e) { mods.push(e) });
52088
52089         
52090         // add modules to their parents..
52091         var addMod = function(m) {
52092            // Roo.debug && Roo.log(m.modKey);
52093             
52094             mods.push(m);
52095             if (m.modules) {
52096                 m.modules.keySort('ASC',  cmp );
52097                 m.modules.each(addMod);
52098             }
52099             // not sure if this is used any more..
52100             if (m.finalize) {
52101                 m.finalize.name = m.name + " (clean up) ";
52102                 mods.push(m.finalize);
52103             }
52104             
52105         }
52106         if (this.topModule) { 
52107             this.topModule.modules.keySort('ASC',  cmp );
52108             this.topModule.modules.each(addMod);
52109         }
52110         return mods;
52111     },
52112     
52113      /**
52114      * Build the registered modules.
52115      * @param {Object} parent element.
52116      * @param {Function} optional method to call after module has been added.
52117      * 
52118      */ 
52119    
52120     build : function() 
52121     {
52122         
52123         this.preBuild();
52124         var mods = this.buildOrder();
52125       
52126         //this.allmods = mods;
52127         //Roo.debug && Roo.log(mods);
52128         //return;
52129         if (!mods.length) { // should not happen
52130             throw "NO modules!!!";
52131         }
52132         
52133         
52134         
52135         // flash it up as modal - so we store the mask!?
52136         Roo.MessageBox.show({ title: 'loading' });
52137         Roo.MessageBox.show({
52138            title: "Please wait...",
52139            msg: "Building Interface...",
52140            width:450,
52141            progress:true,
52142            closable:false,
52143            modal: false
52144           
52145         });
52146         var total = mods.length;
52147         
52148         var _this = this;
52149         var progressRun = function() {
52150             if (!mods.length) {
52151                 Roo.debug && Roo.log('hide?');
52152                 Roo.MessageBox.hide();
52153                 if (_this.topModule) { 
52154                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52155                 }
52156                 // THE END...
52157                 return false;   
52158             }
52159             
52160             var m = mods.shift();
52161             
52162             
52163             Roo.debug && Roo.log(m);
52164             // not sure if this is supported any more.. - modules that are are just function
52165             if (typeof(m) == 'function') { 
52166                 m.call(this);
52167                 return progressRun.defer(10, _this);
52168             } 
52169             
52170             
52171             
52172             Roo.MessageBox.updateProgress(
52173                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52174                     " of " + total + 
52175                     (m.name ? (' - ' + m.name) : '')
52176                     );
52177             
52178          
52179             // is the module disabled?
52180             var disabled = (typeof(m.disabled) == 'function') ?
52181                 m.disabled.call(m.module.disabled) : m.disabled;    
52182             
52183             
52184             if (disabled) {
52185                 return progressRun(); // we do not update the display!
52186             }
52187             
52188             // now build 
52189             
52190             m.render();
52191             // it's 10 on top level, and 1 on others??? why...
52192             return progressRun.defer(10, _this);
52193              
52194         }
52195         progressRun.defer(1, _this);
52196      
52197         
52198         
52199     }
52200     
52201      
52202    
52203     
52204     
52205 });
52206  //<script type="text/javascript">
52207
52208
52209 /**
52210  * @class Roo.Login
52211  * @extends Roo.LayoutDialog
52212  * A generic Login Dialog..... - only one needed in theory!?!?
52213  *
52214  * Fires XComponent builder on success...
52215  * 
52216  * Sends 
52217  *    username,password, lang = for login actions.
52218  *    check = 1 for periodic checking that sesion is valid.
52219  *    passwordRequest = email request password
52220  *    logout = 1 = to logout
52221  * 
52222  * Affects: (this id="????" elements)
52223  *   loading  (removed) (used to indicate application is loading)
52224  *   loading-mask (hides) (used to hide application when it's building loading)
52225  *   
52226  * 
52227  * Usage: 
52228  *    
52229  * 
52230  * Myapp.login = Roo.Login({
52231      url: xxxx,
52232    
52233      realm : 'Myapp', 
52234      
52235      
52236      method : 'POST',
52237      
52238      
52239      * 
52240  })
52241  * 
52242  * 
52243  * 
52244  **/
52245  
52246 Roo.Login = function(cfg)
52247 {
52248     this.addEvents({
52249         'refreshed' : true
52250     });
52251     
52252     Roo.apply(this,cfg);
52253     
52254     Roo.onReady(function() {
52255         this.onLoad();
52256     }, this);
52257     // call parent..
52258     
52259    
52260     Roo.Login.superclass.constructor.call(this, this);
52261     //this.addxtype(this.items[0]);
52262     
52263     
52264 }
52265
52266
52267 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52268     
52269     /**
52270      * @cfg {String} method
52271      * Method used to query for login details.
52272      */
52273     
52274     method : 'POST',
52275     /**
52276      * @cfg {String} url
52277      * URL to query login data. - eg. baseURL + '/Login.php'
52278      */
52279     url : '',
52280     
52281     /**
52282      * @property user
52283      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52284      * @type {Object} 
52285      */
52286     user : false,
52287     /**
52288      * @property checkFails
52289      * Number of times we have attempted to get authentication check, and failed.
52290      * @type {Number} 
52291      */
52292     checkFails : 0,
52293       /**
52294      * @property intervalID
52295      * The window interval that does the constant login checking.
52296      * @type {Number} 
52297      */
52298     intervalID : 0,
52299     
52300     
52301     onLoad : function() // called on page load...
52302     {
52303         // load 
52304          
52305         if (Roo.get('loading')) { // clear any loading indicator..
52306             Roo.get('loading').remove();
52307         }
52308         
52309         //this.switchLang('en'); // set the language to english..
52310        
52311         this.check({
52312             success:  function(response, opts)  {  // check successfull...
52313             
52314                 var res = this.processResponse(response);
52315                 this.checkFails =0;
52316                 if (!res.success) { // error!
52317                     this.checkFails = 5;
52318                     //console.log('call failure');
52319                     return this.failure(response,opts);
52320                 }
52321                 
52322                 if (!res.data.id) { // id=0 == login failure.
52323                     return this.show();
52324                 }
52325                 
52326                               
52327                         //console.log(success);
52328                 this.fillAuth(res.data);   
52329                 this.checkFails =0;
52330                 Roo.XComponent.build();
52331             },
52332             failure : this.show
52333         });
52334         
52335     }, 
52336     
52337     
52338     check: function(cfg) // called every so often to refresh cookie etc..
52339     {
52340         if (cfg.again) { // could be undefined..
52341             this.checkFails++;
52342         } else {
52343             this.checkFails = 0;
52344         }
52345         var _this = this;
52346         if (this.sending) {
52347             if ( this.checkFails > 4) {
52348                 Roo.MessageBox.alert("Error",  
52349                     "Error getting authentication status. - try reloading, or wait a while", function() {
52350                         _this.sending = false;
52351                     }); 
52352                 return;
52353             }
52354             cfg.again = true;
52355             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52356             return;
52357         }
52358         this.sending = true;
52359         
52360         Roo.Ajax.request({  
52361             url: this.url,
52362             params: {
52363                 getAuthUser: true
52364             },  
52365             method: this.method,
52366             success:  cfg.success || this.success,
52367             failure : cfg.failure || this.failure,
52368             scope : this,
52369             callCfg : cfg
52370               
52371         });  
52372     }, 
52373     
52374     
52375     logout: function()
52376     {
52377         window.onbeforeunload = function() { }; // false does not work for IE..
52378         this.user = false;
52379         var _this = this;
52380         
52381         Roo.Ajax.request({  
52382             url: this.url,
52383             params: {
52384                 logout: 1
52385             },  
52386             method: 'GET',
52387             failure : function() {
52388                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52389                     document.location = document.location.toString() + '?ts=' + Math.random();
52390                 });
52391                 
52392             },
52393             success : function() {
52394                 _this.user = false;
52395                 this.checkFails =0;
52396                 // fixme..
52397                 document.location = document.location.toString() + '?ts=' + Math.random();
52398             }
52399               
52400               
52401         }); 
52402     },
52403     
52404     processResponse : function (response)
52405     {
52406         var res = '';
52407         try {
52408             res = Roo.decode(response.responseText);
52409             // oops...
52410             if (typeof(res) != 'object') {
52411                 res = { success : false, errorMsg : res, errors : true };
52412             }
52413             if (typeof(res.success) == 'undefined') {
52414                 res.success = false;
52415             }
52416             
52417         } catch(e) {
52418             res = { success : false,  errorMsg : response.responseText, errors : true };
52419         }
52420         return res;
52421     },
52422     
52423     success : function(response, opts)  // check successfull...
52424     {  
52425         this.sending = false;
52426         var res = this.processResponse(response);
52427         if (!res.success) {
52428             return this.failure(response, opts);
52429         }
52430         if (!res.data || !res.data.id) {
52431             return this.failure(response,opts);
52432         }
52433         //console.log(res);
52434         this.fillAuth(res.data);
52435         
52436         this.checkFails =0;
52437         
52438     },
52439     
52440     
52441     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52442     {
52443         this.authUser = -1;
52444         this.sending = false;
52445         var res = this.processResponse(response);
52446         //console.log(res);
52447         if ( this.checkFails > 2) {
52448         
52449             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52450                 "Error getting authentication status. - try reloading"); 
52451             return;
52452         }
52453         opts.callCfg.again = true;
52454         this.check.defer(1000, this, [ opts.callCfg ]);
52455         return;  
52456     },
52457     
52458     
52459     
52460     fillAuth: function(au) {
52461         this.startAuthCheck();
52462         this.authUserId = au.id;
52463         this.authUser = au;
52464         this.lastChecked = new Date();
52465         this.fireEvent('refreshed', au);
52466         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52467         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52468         au.lang = au.lang || 'en';
52469         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52470         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52471         this.switchLang(au.lang );
52472         
52473      
52474         // open system... - -on setyp..
52475         if (this.authUserId  < 0) {
52476             Roo.MessageBox.alert("Warning", 
52477                 "This is an open system - please set up a admin user with a password.");  
52478         }
52479          
52480         //Pman.onload(); // which should do nothing if it's a re-auth result...
52481         
52482              
52483     },
52484     
52485     startAuthCheck : function() // starter for timeout checking..
52486     {
52487         if (this.intervalID) { // timer already in place...
52488             return false;
52489         }
52490         var _this = this;
52491         this.intervalID =  window.setInterval(function() {
52492               _this.check(false);
52493             }, 120000); // every 120 secs = 2mins..
52494         
52495         
52496     },
52497          
52498     
52499     switchLang : function (lang) 
52500     {
52501         _T = typeof(_T) == 'undefined' ? false : _T;
52502           if (!_T || !lang.length) {
52503             return;
52504         }
52505         
52506         if (!_T && lang != 'en') {
52507             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52508             return;
52509         }
52510         
52511         if (typeof(_T.en) == 'undefined') {
52512             _T.en = {};
52513             Roo.apply(_T.en, _T);
52514         }
52515         
52516         if (typeof(_T[lang]) == 'undefined') {
52517             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52518             return;
52519         }
52520         
52521         
52522         Roo.apply(_T, _T[lang]);
52523         // just need to set the text values for everything...
52524         var _this = this;
52525         /* this will not work ...
52526         if (this.form) { 
52527             
52528                
52529             function formLabel(name, val) {
52530                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52531             }
52532             
52533             formLabel('password', "Password"+':');
52534             formLabel('username', "Email Address"+':');
52535             formLabel('lang', "Language"+':');
52536             this.dialog.setTitle("Login");
52537             this.dialog.buttons[0].setText("Forgot Password");
52538             this.dialog.buttons[1].setText("Login");
52539         }
52540         */
52541         
52542         
52543     },
52544     
52545     
52546     title: "Login",
52547     modal: true,
52548     width:  350,
52549     //height: 230,
52550     height: 180,
52551     shadow: true,
52552     minWidth:200,
52553     minHeight:180,
52554     //proxyDrag: true,
52555     closable: false,
52556     draggable: false,
52557     collapsible: false,
52558     resizable: false,
52559     center: {  // needed??
52560         autoScroll:false,
52561         titlebar: false,
52562        // tabPosition: 'top',
52563         hideTabs: true,
52564         closeOnTab: true,
52565         alwaysShowTabs: false
52566     } ,
52567     listeners : {
52568         
52569         show  : function(dlg)
52570         {
52571             //console.log(this);
52572             this.form = this.layout.getRegion('center').activePanel.form;
52573             this.form.dialog = dlg;
52574             this.buttons[0].form = this.form;
52575             this.buttons[0].dialog = dlg;
52576             this.buttons[1].form = this.form;
52577             this.buttons[1].dialog = dlg;
52578            
52579            //this.resizeToLogo.defer(1000,this);
52580             // this is all related to resizing for logos..
52581             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52582            //// if (!sz) {
52583              //   this.resizeToLogo.defer(1000,this);
52584              //   return;
52585            // }
52586             //var w = Ext.lib.Dom.getViewWidth() - 100;
52587             //var h = Ext.lib.Dom.getViewHeight() - 100;
52588             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52589             //this.center();
52590             if (this.disabled) {
52591                 this.hide();
52592                 return;
52593             }
52594             
52595             if (this.user.id < 0) { // used for inital setup situations.
52596                 return;
52597             }
52598             
52599             if (this.intervalID) {
52600                 // remove the timer
52601                 window.clearInterval(this.intervalID);
52602                 this.intervalID = false;
52603             }
52604             
52605             
52606             if (Roo.get('loading')) {
52607                 Roo.get('loading').remove();
52608             }
52609             if (Roo.get('loading-mask')) {
52610                 Roo.get('loading-mask').hide();
52611             }
52612             
52613             //incomming._node = tnode;
52614             this.form.reset();
52615             //this.dialog.modal = !modal;
52616             //this.dialog.show();
52617             this.el.unmask(); 
52618             
52619             
52620             this.form.setValues({
52621                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52622                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52623             });
52624             
52625             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52626             if (this.form.findField('username').getValue().length > 0 ){
52627                 this.form.findField('password').focus();
52628             } else {
52629                this.form.findField('username').focus();
52630             }
52631     
52632         }
52633     },
52634     items : [
52635          {
52636        
52637             xtype : 'ContentPanel',
52638             xns : Roo,
52639             region: 'center',
52640             fitToFrame : true,
52641             
52642             items : [
52643     
52644                 {
52645                
52646                     xtype : 'Form',
52647                     xns : Roo.form,
52648                     labelWidth: 100,
52649                     style : 'margin: 10px;',
52650                     
52651                     listeners : {
52652                         actionfailed : function(f, act) {
52653                             // form can return { errors: .... }
52654                                 
52655                             //act.result.errors // invalid form element list...
52656                             //act.result.errorMsg// invalid form element list...
52657                             
52658                             this.dialog.el.unmask();
52659                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52660                                         "Login failed - communication error - try again.");
52661                                       
52662                         },
52663                         actioncomplete: function(re, act) {
52664                              
52665                             Roo.state.Manager.set(
52666                                 this.dialog.realm + '.username',  
52667                                     this.findField('username').getValue()
52668                             );
52669                             Roo.state.Manager.set(
52670                                 this.dialog.realm + '.lang',  
52671                                 this.findField('lang').getValue() 
52672                             );
52673                             
52674                             this.dialog.fillAuth(act.result.data);
52675                               
52676                             this.dialog.hide();
52677                             
52678                             if (Roo.get('loading-mask')) {
52679                                 Roo.get('loading-mask').show();
52680                             }
52681                             Roo.XComponent.build();
52682                             
52683                              
52684                             
52685                         }
52686                     },
52687                     items : [
52688                         {
52689                             xtype : 'TextField',
52690                             xns : Roo.form,
52691                             fieldLabel: "Email Address",
52692                             name: 'username',
52693                             width:200,
52694                             autoCreate : {tag: "input", type: "text", size: "20"}
52695                         },
52696                         {
52697                             xtype : 'TextField',
52698                             xns : Roo.form,
52699                             fieldLabel: "Password",
52700                             inputType: 'password',
52701                             name: 'password',
52702                             width:200,
52703                             autoCreate : {tag: "input", type: "text", size: "20"},
52704                             listeners : {
52705                                 specialkey : function(e,ev) {
52706                                     if (ev.keyCode == 13) {
52707                                         this.form.dialog.el.mask("Logging in");
52708                                         this.form.doAction('submit', {
52709                                             url: this.form.dialog.url,
52710                                             method: this.form.dialog.method
52711                                         });
52712                                     }
52713                                 }
52714                             }  
52715                         },
52716                         {
52717                             xtype : 'ComboBox',
52718                             xns : Roo.form,
52719                             fieldLabel: "Language",
52720                             name : 'langdisp',
52721                             store: {
52722                                 xtype : 'SimpleStore',
52723                                 fields: ['lang', 'ldisp'],
52724                                 data : [
52725                                     [ 'en', 'English' ],
52726                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52727                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52728                                 ]
52729                             },
52730                             
52731                             valueField : 'lang',
52732                             hiddenName:  'lang',
52733                             width: 200,
52734                             displayField:'ldisp',
52735                             typeAhead: false,
52736                             editable: false,
52737                             mode: 'local',
52738                             triggerAction: 'all',
52739                             emptyText:'Select a Language...',
52740                             selectOnFocus:true,
52741                             listeners : {
52742                                 select :  function(cb, rec, ix) {
52743                                     this.form.switchLang(rec.data.lang);
52744                                 }
52745                             }
52746                         
52747                         }
52748                     ]
52749                 }
52750                   
52751                 
52752             ]
52753         }
52754     ],
52755     buttons : [
52756         {
52757             xtype : 'Button',
52758             xns : 'Roo',
52759             text : "Forgot Password",
52760             listeners : {
52761                 click : function() {
52762                     //console.log(this);
52763                     var n = this.form.findField('username').getValue();
52764                     if (!n.length) {
52765                         Roo.MessageBox.alert("Error", "Fill in your email address");
52766                         return;
52767                     }
52768                     Roo.Ajax.request({
52769                         url: this.dialog.url,
52770                         params: {
52771                             passwordRequest: n
52772                         },
52773                         method: this.dialog.method,
52774                         success:  function(response, opts)  {  // check successfull...
52775                         
52776                             var res = this.dialog.processResponse(response);
52777                             if (!res.success) { // error!
52778                                Roo.MessageBox.alert("Error" ,
52779                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52780                                return;
52781                             }
52782                             Roo.MessageBox.alert("Notice" ,
52783                                 "Please check you email for the Password Reset message");
52784                         },
52785                         failure : function() {
52786                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52787                         }
52788                         
52789                     });
52790                 }
52791             }
52792         },
52793         {
52794             xtype : 'Button',
52795             xns : 'Roo',
52796             text : "Login",
52797             listeners : {
52798                 
52799                 click : function () {
52800                         
52801                     this.dialog.el.mask("Logging in");
52802                     this.form.doAction('submit', {
52803                             url: this.dialog.url,
52804                             method: this.dialog.method
52805                     });
52806                 }
52807             }
52808         }
52809     ]
52810   
52811   
52812 })
52813  
52814
52815
52816